diff --git a/JavaLibrary.mk b/JavaLibrary.mk index 600367562..758b6cee3 100644 --- a/JavaLibrary.mk +++ b/JavaLibrary.mk @@ -52,9 +52,10 @@ endef core_resource_dirs := \ luni/src/main/java \ ojluni/src/main/resources/ -test_resource_dirs := $(call all-core-resource-dirs,test) -test_src_files := $(call all-test-java-files-under,dalvik dom harmony-tests json luni xml) +test_resource_dirs := $(filter-out ojluni/%,$(call all-core-resource-dirs,test)) +test_src_files := $(call all-test-java-files-under,dalvik dalvik/test-rules dom harmony-tests json luni xml) ojtest_src_files := $(call all-test-java-files-under,ojluni) +ojtest_resource_dirs := $(filter ojluni/%,$(call all-core-resource-dirs,test)) ifeq ($(EMMA_INSTRUMENT),true) ifneq ($(EMMA_INSTRUMENT_STATIC),true) @@ -90,7 +91,6 @@ LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-all -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk LOCAL_REQUIRED_MODULES := tzdata LOCAL_CORE_LIBRARY := true LOCAL_UNINSTALLABLE_MODULE := true @@ -101,11 +101,13 @@ LOCAL_SRC_FILES := $(openjdk_java_files) LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true LOCAL_JAVACFLAGS := $(local_javac_flags) -LOCAL_DX_FLAGS := --core-library +# TODO(oth): Remove --min-sdk-version=26 when the O SDK version is determined. +# For now it represents the minimum sdk version required for invoke-polymorphic. +# This is only needed when ANDROID_COMPILE_WITH_JACK=false (b/36118520). +LOCAL_DX_FLAGS := --core-library --min-sdk-version=26 LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-oj -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk LOCAL_JAVA_LIBRARIES := core-all LOCAL_NOTICE_FILE := $(LOCAL_PATH)/ojluni/NOTICE LOCAL_REQUIRED_MODULES := tzdata @@ -122,7 +124,6 @@ LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-libart -LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/JavaLibrary.mk LOCAL_JAVA_LIBRARIES := core-all ifeq ($(EMMA_INSTRUMENT),true) ifneq ($(EMMA_INSTRUMENT_STATIC),true) @@ -137,14 +138,13 @@ include $(BUILD_JAVA_LIBRARY) # A library that exists to satisfy javac when # compiling source code that contains lambdas. include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(openjdk_lambda_stub_files) +LOCAL_SRC_FILES := $(openjdk_lambda_stub_files) $(openjdk_lambda_duplicate_stub_files) LOCAL_NO_STANDARD_LIBRARIES := true LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-lambda-stubs -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk LOCAL_JAVA_LIBRARIES := core-all LOCAL_NOTICE_FILE := $(LOCAL_PATH)/ojluni/NOTICE LOCAL_CORE_LIBRARY := true @@ -152,8 +152,9 @@ LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_JAVA_LIBRARY) ifeq ($(LIBCORE_SKIP_TESTS),) -# A guaranteed unstripped version of core-oj and core-libart. This is required for ART testing in -# preopted configurations. See b/24535627. +# A guaranteed unstripped version of core-oj and core-libart. +# The build system may or may not strip the core-oj and core-libart jars, +# but these will not be stripped. See b/24535627. include $(CLEAR_VARS) LOCAL_SRC_FILES := $(openjdk_java_files) LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) @@ -164,13 +165,30 @@ LOCAL_MODULE_TAGS := optional LOCAL_DEX_PREOPT := false LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-oj-testdex -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk LOCAL_JAVA_LIBRARIES := core-all LOCAL_NOTICE_FILE := $(LOCAL_PATH)/ojluni/NOTICE LOCAL_REQUIRED_MODULES := tzdata LOCAL_CORE_LIBRARY := true include $(BUILD_JAVA_LIBRARY) +# Build libcore test rules for target +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-java-files-under, dalvik/test-rules/src/main test-rules/src/main) +LOCAL_NO_STANDARD_LIBRARIES := true +LOCAL_MODULE := core-test-rules +LOCAL_JAVA_LIBRARIES := core-all +LOCAL_STATIC_JAVA_LIBRARIES := junit +include $(BUILD_STATIC_JAVA_LIBRARY) + +# Build libcore test rules for host +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-java-files-under, dalvik/test-rules/src/main test-rules/src/main) +LOCAL_NO_STANDARD_LIBRARIES := true +LOCAL_MODULE := core-test-rules-hostdex +LOCAL_JAVA_LIBRARIES := core-oj-hostdex core-libart-hostdex +LOCAL_STATIC_JAVA_LIBRARIES := junit-hostdex +include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY) + include $(CLEAR_VARS) LOCAL_SRC_FILES := $(non_openjdk_java_files) $(android_icu4j_src_files) LOCAL_JAVA_RESOURCE_DIRS := $(android_icu4j_resource_dirs) @@ -181,25 +199,52 @@ LOCAL_MODULE_TAGS := optional LOCAL_DEX_PREOPT := false LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-libart-testdex -LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/JavaLibrary.mk LOCAL_JAVA_LIBRARIES := core-all LOCAL_CORE_LIBRARY := true LOCAL_REQUIRED_MODULES := tzdata include $(BUILD_JAVA_LIBRARY) endif +ifeq ($(LIBCORE_SKIP_TESTS),) +# Build a library just containing files from luni/src/test/filesystems for use in tests. +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-java-files-under, luni/src/test/filesystems/src) +LOCAL_JAVA_RESOURCE_DIRS := luni/src/test/filesystems/resources +LOCAL_NO_STANDARD_LIBRARIES := true +LOCAL_MODULE := filesystemstest +LOCAL_JAVA_LIBRARIES := core-oj core-libart +LOCAL_DEX_PREOPT := false +include $(BUILD_JAVA_LIBRARY) +my_filesystemstest_jar := $(intermediates)/filesystemstest.jar +$(my_filesystemstest_jar): $(LOCAL_BUILT_MODULE) + $(call copy-file-to-target) +endif + ifeq ($(LIBCORE_SKIP_TESTS),) # Make the core-tests library. include $(CLEAR_VARS) LOCAL_SRC_FILES := $(test_src_files) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) +# Include individual dex.jar files (jars containing resources and a classes.dex) so that they +# be loaded by tests using ClassLoaders but are not in the main classes.dex. +LOCAL_JAVA_RESOURCE_FILES := $(my_filesystemstest_jar) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-oj core-libart core-lambda-stubs okhttp core-junit bouncycastle mockito-target -LOCAL_STATIC_JAVA_LIBRARIES := core-tests-support sqlite-jdbc mockwebserver nist-pkix-tests +LOCAL_JAVA_LIBRARIES := core-oj core-libart okhttp bouncycastle +LOCAL_STATIC_JAVA_LIBRARIES := \ + core-test-rules \ + core-tests-support \ + mockftpserver \ + mockito-target \ + mockwebserver \ + nist-pkix-tests \ + slf4j-jdk14 \ + sqlite-jdbc \ + tzdata-testing \ + junit-params LOCAL_JAVACFLAGS := $(local_javac_flags) +LOCAL_ERROR_PRONE_FLAGS := -Xep:TryFailThrowable:ERROR LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-tests -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk include $(BUILD_STATIC_JAVA_LIBRARY) endif @@ -209,11 +254,10 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,support) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-oj core-libart core-junit bouncycastle +LOCAL_JAVA_LIBRARIES := core-oj core-libart junit bouncycastle LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-bcpkix bouncycastle-ocsp LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE := core-tests-support -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk include $(BUILD_STATIC_JAVA_LIBRARY) endif @@ -223,24 +267,25 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under, jsr166-tests) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-oj core-libart core-lambda-stubs core-junit +LOCAL_JAVA_LIBRARIES := core-oj core-libart junit LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE := jsr166-tests -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk +LOCAL_JAVA_LANGUAGE_VERSION := 1.8 include $(BUILD_STATIC_JAVA_LIBRARY) endif # Make the core-ojtests library. ifeq ($(LIBCORE_SKIP_TESTS),) include $(CLEAR_VARS) + LOCAL_JAVA_RESOURCE_DIRS := $(ojtest_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true - LOCAL_JAVA_LIBRARIES := core-oj core-libart core-lambda-stubs okhttp bouncycastle + LOCAL_JAVA_LIBRARIES := core-oj core-libart okhttp bouncycastle LOCAL_STATIC_JAVA_LIBRARIES := testng LOCAL_JAVACFLAGS := $(local_javac_flags) + LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-ojtests - LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk # jack bug workaround: int[] java.util.stream.StatefulTestOp.-getjava-util-stream-StreamShapeSwitchesValues() is a private synthetic method in an interface which causes a hard verifier error LOCAL_DEX_PREOPT := false # disable AOT preverification which breaks the build. it will still throw VerifyError at runtime. include $(BUILD_JAVA_LIBRARY) @@ -252,14 +297,19 @@ ifeq ($(LIBCORE_SKIP_TESTS),) # Filter out SerializedLambdaTest because it depends on stub classes and won't actually run. LOCAL_SRC_FILES := $(filter-out %/DeserializeMethodTest.java %/SerializedLambdaTest.java ojluni/src/test/java/util/stream/boot%,$(ojtest_src_files)) # Do not include anything from the boot* directories. Those directories need a custom bootclasspath to run. # Include source code as part of JAR - LOCAL_JAVA_RESOURCE_DIRS := ojluni/src/test/dist + LOCAL_JAVA_RESOURCE_DIRS := ojluni/src/test/dist $(ojtest_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true - LOCAL_JAVA_LIBRARIES := core-oj core-libart core-lambda-stubs okhttp bouncycastle testng + LOCAL_JAVA_LIBRARIES := \ + bouncycastle \ + core-libart \ + core-oj \ + okhttp \ + testng LOCAL_JAVACFLAGS := $(local_javac_flags) + LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-ojtests-public - LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk # jack bug workaround: int[] java.util.stream.StatefulTestOp.-getjava-util-stream-StreamShapeSwitchesValues() is a private synthetic method in an interface which causes a hard verifier error LOCAL_DEX_PREOPT := false # disable AOT preverification which breaks the build. it will still throw VerifyError at runtime. include $(BUILD_JAVA_LIBRARY) @@ -271,12 +321,6 @@ endif ifeq ($(HOST_OS),linux) -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under, dex/src/main) -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := dex-host -include $(BUILD_HOST_JAVA_LIBRARY) - include $(CLEAR_VARS) LOCAL_SRC_FILES := $(non_openjdk_java_files) $(openjdk_java_files) $(android_icu4j_src_files) $(openjdk_lambda_stub_files) LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) @@ -286,7 +330,6 @@ LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-all-hostdex -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk LOCAL_REQUIRED_MODULES := tzdata-host LOCAL_CORE_LIBRARY := true LOCAL_UNINSTALLABLE_MODULE := true @@ -302,7 +345,6 @@ LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-oj-hostdex LOCAL_NOTICE_FILE := $(LOCAL_PATH)/ojluni/NOTICE -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk LOCAL_JAVA_LIBRARIES := core-all-hostdex LOCAL_REQUIRED_MODULES := tzdata-host LOCAL_CORE_LIBRARY := true @@ -318,7 +360,6 @@ LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-libart-hostdex -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk LOCAL_JAVA_LIBRARIES := core-oj-hostdex LOCAL_REQUIRED_MODULES := tzdata-host include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) @@ -326,31 +367,44 @@ include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) # A library that exists to satisfy javac when # compiling source code that contains lambdas. include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(openjdk_lambda_stub_files) +LOCAL_SRC_FILES := $(openjdk_lambda_stub_files) $(openjdk_lambda_duplicate_stub_files) LOCAL_NO_STANDARD_LIBRARIES := true LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-lambda-stubs-hostdex -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk LOCAL_JAVA_LIBRARIES := core-all-hostdex LOCAL_CORE_LIBRARY := true include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) -# Make the core-tests library. +# Make the core-tests-hostdex library. ifeq ($(LIBCORE_SKIP_TESTS),) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(test_src_files) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true - LOCAL_JAVA_LIBRARIES := core-oj-hostdex core-libart-hostdex core-lambda-stubs-hostdex okhttp-hostdex bouncycastle-hostdex core-junit-hostdex core-tests-support-hostdex mockito-api-hostdex - LOCAL_STATIC_JAVA_LIBRARIES := sqlite-jdbc-host mockwebserver-host nist-pkix-tests-host + LOCAL_JAVA_LIBRARIES := \ + bouncycastle-hostdex \ + core-libart-hostdex \ + core-oj-hostdex \ + core-tests-support-hostdex \ + junit-hostdex \ + mockito-api-hostdex \ + okhttp-hostdex + LOCAL_STATIC_JAVA_LIBRARIES := \ + core-test-rules-hostdex \ + mockftpserver-hostdex \ + mockwebserver-host \ + nist-pkix-tests-host \ + slf4j-jdk14-hostdex \ + sqlite-jdbc-host \ + tzdata-testing-hostdex \ + junit-params-hostdex LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-tests-hostdex - LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) endif @@ -360,12 +414,17 @@ ifeq ($(LIBCORE_SKIP_TESTS),) LOCAL_SRC_FILES := $(call all-test-java-files-under,support) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true - LOCAL_JAVA_LIBRARIES := core-oj-hostdex core-libart-hostdex core-junit-hostdex bouncycastle-hostdex - LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-bcpkix-hostdex bouncycastle-ocsp-hostdex + LOCAL_JAVA_LIBRARIES := \ + bouncycastle-hostdex \ + core-libart-hostdex \ + core-oj-hostdex \ + junit-hostdex + LOCAL_STATIC_JAVA_LIBRARIES := \ + bouncycastle-bcpkix-hostdex \ + bouncycastle-ocsp-hostdex LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE_TAGS := optional LOCAL_MODULE := core-tests-support-hostdex - LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) endif @@ -374,13 +433,17 @@ ifeq ($(LIBCORE_SKIP_TESTS),) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(ojtest_src_files) LOCAL_NO_STANDARD_LIBRARIES := true - LOCAL_JAVA_LIBRARIES := core-oj-hostdex core-libart-hostdex core-lambda-stubs-hostdex okhttp-hostdex bouncycastle-hostdex + LOCAL_JAVA_LIBRARIES := \ + bouncycastle-hostdex \ + core-libart-hostdex \ + core-oj-hostdex \ + okhttp-hostdex LOCAL_STATIC_JAVA_LIBRARIES := testng-hostdex LOCAL_JAVACFLAGS := $(local_javac_flags) + LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LANGUAGE_VERSION := 1.8 LOCAL_MODULE := core-ojtests-hostdex - LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) endif @@ -418,13 +481,13 @@ LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE_CLASS:=JAVA_LIBRARIES LOCAL_MODULE := libcore -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk LOCAL_DROIDDOC_OPTIONS := \ -offlinemode \ -title "libcore" \ -proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \ -todo ../$(LOCAL_MODULE)-docs-todo.html \ + -knowntags ./libcore/known_oj_tags.txt \ -hdf android.whichdoc offline LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk diff --git a/NativeCode.mk b/NativeCode.mk index be02ff170..d8e9d644a 100644 --- a/NativeCode.mk +++ b/NativeCode.mk @@ -80,7 +80,7 @@ core_c_includes := libcore/include $(LOCAL_C_INCLUDES) core_shared_libraries := $(LOCAL_SHARED_LIBRARIES) core_static_libraries := $(LOCAL_STATIC_LIBRARIES) libart_cflags := $(LOCAL_CFLAGS) -Wall -Wextra -Werror -core_cppflags += -std=gnu++11 -DU_USING_ICU_NAMESPACE=0 +core_cppflags += -DU_USING_ICU_NAMESPACE=0 # TODO(narayan): Prune down this list of exclusions once the underlying # issues have been fixed. Most of these are small changes except for # -Wunused-parameter. @@ -169,6 +169,7 @@ LOCAL_SRC_FILES += $(core_test_files) LOCAL_C_INCLUDES += libcore/include LOCAL_SHARED_LIBRARIES += libnativehelper_compat_libc++ LOCAL_MODULE_TAGS := optional +LOCAL_STRIP_MODULE := keep_symbols LOCAL_MODULE := libjavacoretests LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk LOCAL_CXX_STL := libc++ @@ -178,7 +179,9 @@ endif # LIBCORE_SKIP_TESTS # Set of gtest unit tests. include $(CLEAR_VARS) -LOCAL_CFLAGS += $(libart_cflags) +# Add -fno-builtin so that the compiler doesn't attempt to inline +# memcpy calls that are not really aligned. +LOCAL_CFLAGS += $(libart_cflags) -fno-builtin LOCAL_CPPFLAGS += $(core_cppflags) LOCAL_SRC_FILES += \ luni/src/test/native/libcore_io_Memory_test.cpp \ @@ -229,7 +232,7 @@ endif LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libjavacore LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk -LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libexpat-host libicuuc-host libicui18n-host libcrypto-host libz-host libziparchive-host +LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libexpat libicuuc libicui18n libcrypto libz-host libziparchive LOCAL_STATIC_LIBRARIES += $(core_static_libraries) LOCAL_MULTILIB := both LOCAL_CXX_STL := libc++ @@ -241,11 +244,11 @@ LOCAL_SRC_FILES := $(openjdk_core_src_files) LOCAL_C_INCLUDES := $(core_c_includes) LOCAL_CFLAGS := -D_LARGEFILE64_SOURCE -D_GNU_SOURCE -DLINUX -D__GLIBC__ # Sigh. LOCAL_CFLAGS += $(openjdk_cflags) -LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libicuuc-host libcrypto-host libz-host +LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libicuuc libcrypto libz-host LOCAL_SHARED_LIBRARIES += libopenjdkjvmd libnativehelper LOCAL_STATIC_LIBRARIES := $(core_static_libraries) libfdlibm LOCAL_MODULE_TAGS := optional -LOCAL_LDLIBS += -ldl -lpthread +LOCAL_LDLIBS += -ldl -lpthread -lrt LOCAL_MODULE := libopenjdkd LOCAL_NOTICE_FILE := $(LOCAL_PATH)/ojluni/NOTICE LOCAL_MULTILIB := both @@ -256,11 +259,11 @@ LOCAL_SRC_FILES := $(openjdk_core_src_files) LOCAL_C_INCLUDES := $(core_c_includes) LOCAL_CFLAGS := -D_LARGEFILE64_SOURCE -D_GNU_SOURCE -DLINUX -D__GLIBC__ # Sigh. LOCAL_CFLAGS += $(openjdk_cflags) -LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libicuuc-host libcrypto-host libz-host +LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libicuuc libcrypto libz-host LOCAL_SHARED_LIBRARIES += libopenjdkjvm libnativehelper LOCAL_STATIC_LIBRARIES := $(core_static_libraries) libfdlibm LOCAL_MODULE_TAGS := optional -LOCAL_LDLIBS += -ldl -lpthread +LOCAL_LDLIBS += -ldl -lpthread -lrt LOCAL_MODULE := libopenjdk LOCAL_NOTICE_FILE := $(LOCAL_PATH)/ojluni/NOTICE LOCAL_MULTILIB := both diff --git a/benchmarks/Android.mk b/benchmarks/Android.mk index 22d0d2696..c48c22444 100644 --- a/benchmarks/Android.mk +++ b/benchmarks/Android.mk @@ -28,7 +28,7 @@ LOCAL_JAVA_LIBRARIES := \ core-oj \ core-libart \ conscrypt \ - core-junit \ + legacy-test \ bouncycastle \ framework LOCAL_MODULE_TAGS := tests diff --git a/benchmarks/src/benchmarks/CloneBenchmark.java b/benchmarks/src/benchmarks/CloneBenchmark.java new file mode 100644 index 000000000..d05fb3d90 --- /dev/null +++ b/benchmarks/src/benchmarks/CloneBenchmark.java @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 2016 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. + */ + +package benchmarks; + +public class CloneBenchmark { + static class CloneableObject implements Cloneable { + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } + + static class CloneableManyFieldObject implements Cloneable { + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + Object o1 = new Object(); + Object o2 = new Object(); + Object o3 = new Object(); + Object o4 = new Object(); + Object o5 = new Object(); + Object o6 = new Object(); + Object o7 = new Object(); + Object o8 = new Object(); + Object o9 = new Object(); + Object o10 = new Object(); + Object o11 = new Object(); + Object o12 = new Object(); + Object o13 = new Object(); + Object o14 = new Object(); + Object o15 = new Object(); + Object o16 = new Object(); + Object o17 = new Object(); + Object o18 = new Object(); + Object o19 = new Object(); + Object o20 = new Object(); + Object o21 = new Object(); + Object o22 = new Object(); + Object o23 = new Object(); + Object o24 = new Object(); + Object o25 = new Object(); + Object o26 = new Object(); + Object o27 = new Object(); + Object o28 = new Object(); + Object o29 = new Object(); + Object o30 = new Object(); + Object o31 = new Object(); + Object o32 = new Object(); + Object o33 = new Object(); + Object o34 = new Object(); + Object o35 = new Object(); + Object o36 = new Object(); + Object o37 = new Object(); + Object o38 = new Object(); + Object o39 = new Object(); + Object o40 = new Object(); + Object o41 = new Object(); + Object o42 = new Object(); + Object o43 = new Object(); + Object o44 = new Object(); + Object o45 = new Object(); + Object o46 = new Object(); + Object o47 = new Object(); + Object o48 = new Object(); + Object o49 = new Object(); + Object o50 = new Object(); + Object o51 = new Object(); + Object o52 = new Object(); + Object o53 = new Object(); + Object o54 = new Object(); + Object o55 = new Object(); + Object o56 = new Object(); + Object o57 = new Object(); + Object o58 = new Object(); + Object o59 = new Object(); + Object o60 = new Object(); + Object o61 = new Object(); + Object o62 = new Object(); + Object o63 = new Object(); + Object o64 = new Object(); + Object o65 = new Object(); + Object o66 = new Object(); + Object o67 = new Object(); + Object o68 = new Object(); + Object o69 = new Object(); + Object o70 = new Object(); + Object o71 = new Object(); + Object o72 = new Object(); + Object o73 = new Object(); + Object o74 = new Object(); + Object o75 = new Object(); + Object o76 = new Object(); + Object o77 = new Object(); + Object o78 = new Object(); + Object o79 = new Object(); + Object o80 = new Object(); + Object o81 = new Object(); + Object o82 = new Object(); + Object o83 = new Object(); + Object o84 = new Object(); + Object o85 = new Object(); + Object o86 = new Object(); + Object o87 = new Object(); + Object o88 = new Object(); + Object o89 = new Object(); + Object o90 = new Object(); + Object o91 = new Object(); + Object o92 = new Object(); + Object o93 = new Object(); + Object o94 = new Object(); + Object o95 = new Object(); + Object o96 = new Object(); + Object o97 = new Object(); + Object o98 = new Object(); + Object o99 = new Object(); + Object o100 = new Object(); + Object o101 = new Object(); + Object o102 = new Object(); + Object o103 = new Object(); + Object o104 = new Object(); + Object o105 = new Object(); + Object o106 = new Object(); + Object o107 = new Object(); + Object o108 = new Object(); + Object o109 = new Object(); + Object o110 = new Object(); + Object o111 = new Object(); + Object o112 = new Object(); + Object o113 = new Object(); + Object o114 = new Object(); + Object o115 = new Object(); + Object o116 = new Object(); + Object o117 = new Object(); + Object o118 = new Object(); + Object o119 = new Object(); + Object o120 = new Object(); + Object o121 = new Object(); + Object o122 = new Object(); + Object o123 = new Object(); + Object o124 = new Object(); + Object o125 = new Object(); + Object o126 = new Object(); + Object o127 = new Object(); + Object o128 = new Object(); + Object o129 = new Object(); + Object o130 = new Object(); + Object o131 = new Object(); + Object o132 = new Object(); + Object o133 = new Object(); + Object o134 = new Object(); + Object o135 = new Object(); + Object o136 = new Object(); + Object o137 = new Object(); + Object o138 = new Object(); + Object o139 = new Object(); + Object o140 = new Object(); + Object o141 = new Object(); + Object o142 = new Object(); + Object o143 = new Object(); + Object o144 = new Object(); + Object o145 = new Object(); + Object o146 = new Object(); + Object o147 = new Object(); + Object o148 = new Object(); + Object o149 = new Object(); + Object o150 = new Object(); + Object o151 = new Object(); + Object o152 = new Object(); + Object o153 = new Object(); + Object o154 = new Object(); + Object o155 = new Object(); + Object o156 = new Object(); + Object o157 = new Object(); + Object o158 = new Object(); + Object o159 = new Object(); + Object o160 = new Object(); + Object o161 = new Object(); + Object o162 = new Object(); + Object o163 = new Object(); + Object o164 = new Object(); + Object o165 = new Object(); + Object o166 = new Object(); + Object o167 = new Object(); + Object o168 = new Object(); + Object o169 = new Object(); + Object o170 = new Object(); + Object o171 = new Object(); + Object o172 = new Object(); + Object o173 = new Object(); + Object o174 = new Object(); + Object o175 = new Object(); + Object o176 = new Object(); + Object o177 = new Object(); + Object o178 = new Object(); + Object o179 = new Object(); + Object o180 = new Object(); + Object o181 = new Object(); + Object o182 = new Object(); + Object o183 = new Object(); + Object o184 = new Object(); + Object o185 = new Object(); + Object o186 = new Object(); + Object o187 = new Object(); + Object o188 = new Object(); + Object o189 = new Object(); + Object o190 = new Object(); + Object o191 = new Object(); + Object o192 = new Object(); + Object o193 = new Object(); + Object o194 = new Object(); + Object o195 = new Object(); + Object o196 = new Object(); + Object o197 = new Object(); + Object o198 = new Object(); + Object o199 = new Object(); + Object o200 = new Object(); + Object o201 = new Object(); + Object o202 = new Object(); + Object o203 = new Object(); + Object o204 = new Object(); + Object o205 = new Object(); + Object o206 = new Object(); + Object o207 = new Object(); + Object o208 = new Object(); + Object o209 = new Object(); + Object o210 = new Object(); + Object o211 = new Object(); + Object o212 = new Object(); + Object o213 = new Object(); + Object o214 = new Object(); + Object o215 = new Object(); + Object o216 = new Object(); + Object o217 = new Object(); + Object o218 = new Object(); + Object o219 = new Object(); + Object o220 = new Object(); + Object o221 = new Object(); + Object o222 = new Object(); + Object o223 = new Object(); + Object o224 = new Object(); + Object o225 = new Object(); + Object o226 = new Object(); + Object o227 = new Object(); + Object o228 = new Object(); + Object o229 = new Object(); + Object o230 = new Object(); + Object o231 = new Object(); + Object o232 = new Object(); + Object o233 = new Object(); + Object o234 = new Object(); + Object o235 = new Object(); + Object o236 = new Object(); + Object o237 = new Object(); + Object o238 = new Object(); + Object o239 = new Object(); + Object o240 = new Object(); + Object o241 = new Object(); + Object o242 = new Object(); + Object o243 = new Object(); + Object o244 = new Object(); + Object o245 = new Object(); + Object o246 = new Object(); + Object o247 = new Object(); + Object o248 = new Object(); + Object o249 = new Object(); + Object o250 = new Object(); + Object o251 = new Object(); + Object o252 = new Object(); + Object o253 = new Object(); + Object o254 = new Object(); + Object o255 = new Object(); + Object o256 = new Object(); + Object o257 = new Object(); + Object o258 = new Object(); + Object o259 = new Object(); + Object o260 = new Object(); + Object o261 = new Object(); + Object o262 = new Object(); + Object o263 = new Object(); + Object o264 = new Object(); + Object o265 = new Object(); + Object o266 = new Object(); + Object o267 = new Object(); + Object o268 = new Object(); + Object o269 = new Object(); + Object o270 = new Object(); + Object o271 = new Object(); + Object o272 = new Object(); + Object o273 = new Object(); + Object o274 = new Object(); + Object o275 = new Object(); + Object o276 = new Object(); + Object o277 = new Object(); + Object o278 = new Object(); + Object o279 = new Object(); + Object o280 = new Object(); + Object o281 = new Object(); + Object o282 = new Object(); + Object o283 = new Object(); + Object o284 = new Object(); + Object o285 = new Object(); + Object o286 = new Object(); + Object o287 = new Object(); + Object o288 = new Object(); + Object o289 = new Object(); + Object o290 = new Object(); + Object o291 = new Object(); + Object o292 = new Object(); + Object o293 = new Object(); + Object o294 = new Object(); + Object o295 = new Object(); + Object o296 = new Object(); + Object o297 = new Object(); + Object o298 = new Object(); + Object o299 = new Object(); + Object o300 = new Object(); + Object o301 = new Object(); + Object o302 = new Object(); + Object o303 = new Object(); + Object o304 = new Object(); + Object o305 = new Object(); + Object o306 = new Object(); + Object o307 = new Object(); + Object o308 = new Object(); + Object o309 = new Object(); + Object o310 = new Object(); + Object o311 = new Object(); + Object o312 = new Object(); + Object o313 = new Object(); + Object o314 = new Object(); + Object o315 = new Object(); + Object o316 = new Object(); + Object o317 = new Object(); + Object o318 = new Object(); + Object o319 = new Object(); + Object o320 = new Object(); + Object o321 = new Object(); + Object o322 = new Object(); + Object o323 = new Object(); + Object o324 = new Object(); + Object o325 = new Object(); + Object o326 = new Object(); + Object o327 = new Object(); + Object o328 = new Object(); + Object o329 = new Object(); + Object o330 = new Object(); + Object o331 = new Object(); + Object o332 = new Object(); + Object o333 = new Object(); + Object o334 = new Object(); + Object o335 = new Object(); + Object o336 = new Object(); + Object o337 = new Object(); + Object o338 = new Object(); + Object o339 = new Object(); + Object o340 = new Object(); + Object o341 = new Object(); + Object o342 = new Object(); + Object o343 = new Object(); + Object o344 = new Object(); + Object o345 = new Object(); + Object o346 = new Object(); + Object o347 = new Object(); + Object o348 = new Object(); + Object o349 = new Object(); + Object o350 = new Object(); + Object o351 = new Object(); + Object o352 = new Object(); + Object o353 = new Object(); + Object o354 = new Object(); + Object o355 = new Object(); + Object o356 = new Object(); + Object o357 = new Object(); + Object o358 = new Object(); + Object o359 = new Object(); + Object o360 = new Object(); + Object o361 = new Object(); + Object o362 = new Object(); + Object o363 = new Object(); + Object o364 = new Object(); + Object o365 = new Object(); + Object o366 = new Object(); + Object o367 = new Object(); + Object o368 = new Object(); + Object o369 = new Object(); + Object o370 = new Object(); + Object o371 = new Object(); + Object o372 = new Object(); + Object o373 = new Object(); + Object o374 = new Object(); + Object o375 = new Object(); + Object o376 = new Object(); + Object o377 = new Object(); + Object o378 = new Object(); + Object o379 = new Object(); + Object o380 = new Object(); + Object o381 = new Object(); + Object o382 = new Object(); + Object o383 = new Object(); + Object o384 = new Object(); + Object o385 = new Object(); + Object o386 = new Object(); + Object o387 = new Object(); + Object o388 = new Object(); + Object o389 = new Object(); + Object o390 = new Object(); + Object o391 = new Object(); + Object o392 = new Object(); + Object o393 = new Object(); + Object o394 = new Object(); + Object o395 = new Object(); + Object o396 = new Object(); + Object o397 = new Object(); + Object o398 = new Object(); + Object o399 = new Object(); + Object o400 = new Object(); + Object o401 = new Object(); + Object o402 = new Object(); + Object o403 = new Object(); + Object o404 = new Object(); + Object o405 = new Object(); + Object o406 = new Object(); + Object o407 = new Object(); + Object o408 = new Object(); + Object o409 = new Object(); + Object o410 = new Object(); + Object o411 = new Object(); + Object o412 = new Object(); + Object o413 = new Object(); + Object o414 = new Object(); + Object o415 = new Object(); + Object o416 = new Object(); + Object o417 = new Object(); + Object o418 = new Object(); + Object o419 = new Object(); + Object o420 = new Object(); + Object o421 = new Object(); + Object o422 = new Object(); + Object o423 = new Object(); + Object o424 = new Object(); + Object o425 = new Object(); + Object o426 = new Object(); + Object o427 = new Object(); + Object o428 = new Object(); + Object o429 = new Object(); + Object o430 = new Object(); + Object o431 = new Object(); + Object o432 = new Object(); + Object o433 = new Object(); + Object o434 = new Object(); + Object o435 = new Object(); + Object o436 = new Object(); + Object o437 = new Object(); + Object o438 = new Object(); + Object o439 = new Object(); + Object o440 = new Object(); + Object o441 = new Object(); + Object o442 = new Object(); + Object o460 = new Object(); + Object o461 = new Object(); + Object o462 = new Object(); + Object o463 = new Object(); + Object o464 = new Object(); + Object o465 = new Object(); + Object o466 = new Object(); + Object o467 = new Object(); + Object o468 = new Object(); + Object o469 = new Object(); + Object o470 = new Object(); + Object o471 = new Object(); + Object o472 = new Object(); + Object o473 = new Object(); + Object o474 = new Object(); + Object o475 = new Object(); + Object o476 = new Object(); + Object o477 = new Object(); + Object o478 = new Object(); + Object o479 = new Object(); + Object o480 = new Object(); + Object o481 = new Object(); + Object o482 = new Object(); + Object o483 = new Object(); + Object o484 = new Object(); + Object o485 = new Object(); + Object o486 = new Object(); + Object o487 = new Object(); + Object o488 = new Object(); + Object o489 = new Object(); + Object o490 = new Object(); + Object o491 = new Object(); + Object o492 = new Object(); + Object o493 = new Object(); + Object o494 = new Object(); + Object o495 = new Object(); + Object o496 = new Object(); + Object o497 = new Object(); + Object o498 = new Object(); + Object o499 = new Object(); + Object o500 = new Object(); + Object o501 = new Object(); + Object o502 = new Object(); + Object o503 = new Object(); + Object o504 = new Object(); + Object o505 = new Object(); + Object o506 = new Object(); + Object o507 = new Object(); + Object o508 = new Object(); + Object o509 = new Object(); + Object o510 = new Object(); + Object o511 = new Object(); + Object o512 = new Object(); + Object o513 = new Object(); + Object o514 = new Object(); + Object o515 = new Object(); + Object o516 = new Object(); + Object o517 = new Object(); + Object o518 = new Object(); + Object o519 = new Object(); + Object o520 = new Object(); + Object o521 = new Object(); + Object o522 = new Object(); + Object o523 = new Object(); + Object o556 = new Object(); + Object o557 = new Object(); + Object o558 = new Object(); + Object o559 = new Object(); + Object o560 = new Object(); + Object o561 = new Object(); + Object o562 = new Object(); + Object o563 = new Object(); + Object o564 = new Object(); + Object o565 = new Object(); + Object o566 = new Object(); + Object o567 = new Object(); + Object o568 = new Object(); + Object o569 = new Object(); + Object o570 = new Object(); + Object o571 = new Object(); + Object o572 = new Object(); + Object o573 = new Object(); + Object o574 = new Object(); + Object o575 = new Object(); + Object o576 = new Object(); + Object o577 = new Object(); + Object o578 = new Object(); + Object o579 = new Object(); + Object o580 = new Object(); + Object o581 = new Object(); + Object o582 = new Object(); + Object o583 = new Object(); + Object o584 = new Object(); + Object o585 = new Object(); + Object o586 = new Object(); + Object o587 = new Object(); + Object o588 = new Object(); + Object o589 = new Object(); + Object o590 = new Object(); + Object o591 = new Object(); + Object o592 = new Object(); + Object o593 = new Object(); + Object o594 = new Object(); + Object o595 = new Object(); + Object o596 = new Object(); + Object o597 = new Object(); + Object o598 = new Object(); + Object o599 = new Object(); + Object o600 = new Object(); + Object o601 = new Object(); + Object o602 = new Object(); + Object o603 = new Object(); + Object o604 = new Object(); + Object o605 = new Object(); + Object o606 = new Object(); + Object o607 = new Object(); + Object o608 = new Object(); + Object o609 = new Object(); + Object o610 = new Object(); + Object o611 = new Object(); + Object o612 = new Object(); + Object o613 = new Object(); + Object o614 = new Object(); + Object o615 = new Object(); + Object o616 = new Object(); + Object o617 = new Object(); + Object o618 = new Object(); + Object o619 = new Object(); + Object o620 = new Object(); + Object o621 = new Object(); + Object o622 = new Object(); + Object o623 = new Object(); + Object o624 = new Object(); + Object o625 = new Object(); + Object o626 = new Object(); + Object o627 = new Object(); + Object o628 = new Object(); + Object o629 = new Object(); + Object o630 = new Object(); + Object o631 = new Object(); + Object o632 = new Object(); + Object o633 = new Object(); + Object o634 = new Object(); + Object o635 = new Object(); + Object o636 = new Object(); + Object o637 = new Object(); + Object o638 = new Object(); + Object o639 = new Object(); + Object o640 = new Object(); + Object o641 = new Object(); + Object o642 = new Object(); + Object o643 = new Object(); + Object o644 = new Object(); + Object o645 = new Object(); + Object o646 = new Object(); + Object o647 = new Object(); + Object o648 = new Object(); + Object o649 = new Object(); + Object o650 = new Object(); + Object o651 = new Object(); + Object o652 = new Object(); + Object o653 = new Object(); + Object o654 = new Object(); + Object o655 = new Object(); + Object o656 = new Object(); + Object o657 = new Object(); + Object o658 = new Object(); + Object o659 = new Object(); + Object o660 = new Object(); + Object o661 = new Object(); + Object o662 = new Object(); + Object o663 = new Object(); + Object o664 = new Object(); + Object o665 = new Object(); + Object o666 = new Object(); + Object o667 = new Object(); + Object o668 = new Object(); + Object o669 = new Object(); + Object o670 = new Object(); + Object o671 = new Object(); + Object o672 = new Object(); + Object o673 = new Object(); + Object o674 = new Object(); + Object o675 = new Object(); + Object o676 = new Object(); + Object o677 = new Object(); + Object o678 = new Object(); + Object o679 = new Object(); + Object o680 = new Object(); + Object o681 = new Object(); + Object o682 = new Object(); + Object o683 = new Object(); + Object o684 = new Object(); + Object o685 = new Object(); + Object o686 = new Object(); + Object o687 = new Object(); + Object o688 = new Object(); + Object o734 = new Object(); + Object o735 = new Object(); + Object o736 = new Object(); + Object o737 = new Object(); + Object o738 = new Object(); + Object o739 = new Object(); + Object o740 = new Object(); + Object o741 = new Object(); + Object o742 = new Object(); + Object o743 = new Object(); + Object o744 = new Object(); + Object o745 = new Object(); + Object o746 = new Object(); + Object o747 = new Object(); + Object o748 = new Object(); + Object o749 = new Object(); + Object o750 = new Object(); + Object o751 = new Object(); + Object o752 = new Object(); + Object o753 = new Object(); + Object o754 = new Object(); + Object o755 = new Object(); + Object o756 = new Object(); + Object o757 = new Object(); + Object o758 = new Object(); + Object o759 = new Object(); + Object o760 = new Object(); + Object o761 = new Object(); + Object o762 = new Object(); + Object o763 = new Object(); + Object o764 = new Object(); + Object o765 = new Object(); + Object o766 = new Object(); + Object o767 = new Object(); + Object o768 = new Object(); + Object o769 = new Object(); + Object o770 = new Object(); + Object o771 = new Object(); + Object o772 = new Object(); + Object o773 = new Object(); + Object o774 = new Object(); + Object o775 = new Object(); + Object o776 = new Object(); + Object o777 = new Object(); + Object o778 = new Object(); + Object o779 = new Object(); + Object o780 = new Object(); + Object o781 = new Object(); + Object o782 = new Object(); + Object o783 = new Object(); + Object o784 = new Object(); + Object o785 = new Object(); + Object o786 = new Object(); + Object o787 = new Object(); + Object o788 = new Object(); + Object o789 = new Object(); + Object o790 = new Object(); + Object o791 = new Object(); + Object o792 = new Object(); + Object o793 = new Object(); + Object o794 = new Object(); + Object o795 = new Object(); + Object o796 = new Object(); + Object o797 = new Object(); + Object o798 = new Object(); + Object o799 = new Object(); + Object o800 = new Object(); + Object o801 = new Object(); + Object o802 = new Object(); + Object o803 = new Object(); + Object o804 = new Object(); + Object o805 = new Object(); + Object o806 = new Object(); + Object o807 = new Object(); + Object o808 = new Object(); + Object o809 = new Object(); + Object o810 = new Object(); + Object o811 = new Object(); + Object o812 = new Object(); + Object o813 = new Object(); + Object o848 = new Object(); + Object o849 = new Object(); + Object o850 = new Object(); + Object o851 = new Object(); + Object o852 = new Object(); + Object o853 = new Object(); + Object o854 = new Object(); + Object o855 = new Object(); + Object o856 = new Object(); + Object o857 = new Object(); + Object o858 = new Object(); + Object o859 = new Object(); + Object o860 = new Object(); + Object o861 = new Object(); + Object o862 = new Object(); + Object o863 = new Object(); + Object o864 = new Object(); + Object o865 = new Object(); + Object o866 = new Object(); + Object o867 = new Object(); + Object o868 = new Object(); + Object o869 = new Object(); + Object o870 = new Object(); + Object o871 = new Object(); + Object o872 = new Object(); + Object o873 = new Object(); + Object o874 = new Object(); + Object o875 = new Object(); + Object o876 = new Object(); + Object o877 = new Object(); + Object o878 = new Object(); + Object o879 = new Object(); + Object o880 = new Object(); + Object o881 = new Object(); + Object o882 = new Object(); + Object o883 = new Object(); + Object o884 = new Object(); + Object o885 = new Object(); + Object o886 = new Object(); + Object o887 = new Object(); + Object o888 = new Object(); + Object o889 = new Object(); + Object o890 = new Object(); + Object o891 = new Object(); + Object o892 = new Object(); + Object o893 = new Object(); + Object o894 = new Object(); + Object o895 = new Object(); + Object o896 = new Object(); + Object o897 = new Object(); + Object o898 = new Object(); + Object o899 = new Object(); + Object o900 = new Object(); + Object o901 = new Object(); + Object o902 = new Object(); + Object o903 = new Object(); + Object o904 = new Object(); + Object o905 = new Object(); + Object o906 = new Object(); + Object o907 = new Object(); + Object o908 = new Object(); + Object o909 = new Object(); + Object o910 = new Object(); + Object o911 = new Object(); + Object o912 = new Object(); + Object o913 = new Object(); + Object o914 = new Object(); + Object o915 = new Object(); + Object o916 = new Object(); + Object o917 = new Object(); + Object o918 = new Object(); + Object o919 = new Object(); + Object o920 = new Object(); + Object o921 = new Object(); + Object o922 = new Object(); + Object o923 = new Object(); + Object o924 = new Object(); + Object o925 = new Object(); + Object o926 = new Object(); + Object o927 = new Object(); + Object o928 = new Object(); + Object o929 = new Object(); + Object o930 = new Object(); + Object o931 = new Object(); + Object o932 = new Object(); + Object o933 = new Object(); + Object o934 = new Object(); + Object o935 = new Object(); + Object o936 = new Object(); + Object o937 = new Object(); + Object o938 = new Object(); + Object o939 = new Object(); + Object o940 = new Object(); + Object o941 = new Object(); + Object o942 = new Object(); + Object o943 = new Object(); + Object o944 = new Object(); + Object o945 = new Object(); + Object o946 = new Object(); + Object o947 = new Object(); + Object o948 = new Object(); + Object o949 = new Object(); + Object o950 = new Object(); + Object o951 = new Object(); + Object o952 = new Object(); + Object o953 = new Object(); + Object o954 = new Object(); + Object o955 = new Object(); + Object o956 = new Object(); + Object o957 = new Object(); + Object o958 = new Object(); + Object o959 = new Object(); + Object o960 = new Object(); + Object o961 = new Object(); + Object o962 = new Object(); + Object o963 = new Object(); + Object o964 = new Object(); + Object o965 = new Object(); + Object o966 = new Object(); + Object o967 = new Object(); + Object o968 = new Object(); + Object o969 = new Object(); + Object o970 = new Object(); + Object o971 = new Object(); + Object o972 = new Object(); + Object o973 = new Object(); + Object o974 = new Object(); + Object o975 = new Object(); + Object o976 = new Object(); + Object o977 = new Object(); + Object o978 = new Object(); + Object o979 = new Object(); + Object o980 = new Object(); + Object o981 = new Object(); + Object o982 = new Object(); + Object o983 = new Object(); + Object o984 = new Object(); + Object o985 = new Object(); + Object o986 = new Object(); + Object o987 = new Object(); + Object o988 = new Object(); + Object o989 = new Object(); + Object o990 = new Object(); + Object o991 = new Object(); + Object o992 = new Object(); + Object o993 = new Object(); + Object o994 = new Object(); + Object o995 = new Object(); + Object o996 = new Object(); + Object o997 = new Object(); + Object o998 = new Object(); + Object o999 = new Object(); + } + + static class Deep0 {} + static class Deep1 extends Deep0 {} + static class Deep2 extends Deep1 {} + static class Deep3 extends Deep2 {} + static class Deep4 extends Deep3 {} + static class Deep5 extends Deep4 {} + static class Deep6 extends Deep5 {} + static class Deep7 extends Deep6 {} + static class Deep8 extends Deep7 {} + static class Deep9 extends Deep8 {} + static class Deep10 extends Deep9 {} + static class Deep11 extends Deep10 {} + static class Deep12 extends Deep11 {} + static class Deep13 extends Deep12 {} + static class Deep14 extends Deep13 {} + static class Deep15 extends Deep14 {} + static class Deep16 extends Deep15 {} + static class Deep17 extends Deep16 {} + static class Deep18 extends Deep17 {} + static class Deep19 extends Deep18 {} + static class Deep20 extends Deep19 {} + static class Deep21 extends Deep20 {} + static class Deep22 extends Deep21 {} + static class Deep23 extends Deep22 {} + static class Deep24 extends Deep23 {} + static class Deep25 extends Deep24 {} + static class Deep26 extends Deep25 {} + static class Deep27 extends Deep26 {} + static class Deep28 extends Deep27 {} + static class Deep29 extends Deep28 {} + static class Deep30 extends Deep29 {} + static class Deep31 extends Deep30 {} + static class Deep32 extends Deep31 {} + static class Deep33 extends Deep32 {} + static class Deep34 extends Deep33 {} + static class Deep35 extends Deep34 {} + static class Deep36 extends Deep35 {} + static class Deep37 extends Deep36 {} + static class Deep38 extends Deep37 {} + static class Deep39 extends Deep38 {} + static class Deep40 extends Deep39 {} + static class Deep41 extends Deep40 {} + static class Deep42 extends Deep41 {} + static class Deep43 extends Deep42 {} + static class Deep44 extends Deep43 {} + static class Deep45 extends Deep44 {} + static class Deep46 extends Deep45 {} + static class Deep47 extends Deep46 {} + static class Deep48 extends Deep47 {} + static class Deep49 extends Deep48 {} + static class Deep50 extends Deep49 {} + static class Deep51 extends Deep50 {} + static class Deep52 extends Deep51 {} + static class Deep53 extends Deep52 {} + static class Deep54 extends Deep53 {} + static class Deep55 extends Deep54 {} + static class Deep56 extends Deep55 {} + static class Deep57 extends Deep56 {} + static class Deep58 extends Deep57 {} + static class Deep59 extends Deep58 {} + static class Deep60 extends Deep59 {} + static class Deep61 extends Deep60 {} + static class Deep62 extends Deep61 {} + static class Deep63 extends Deep62 {} + static class Deep64 extends Deep63 {} + static class Deep65 extends Deep64 {} + static class Deep66 extends Deep65 {} + static class Deep67 extends Deep66 {} + static class Deep68 extends Deep67 {} + static class Deep69 extends Deep68 {} + static class Deep70 extends Deep69 {} + static class Deep71 extends Deep70 {} + static class Deep72 extends Deep71 {} + static class Deep73 extends Deep72 {} + static class Deep74 extends Deep73 {} + static class Deep75 extends Deep74 {} + static class Deep76 extends Deep75 {} + static class Deep77 extends Deep76 {} + static class Deep78 extends Deep77 {} + static class Deep79 extends Deep78 {} + static class Deep80 extends Deep79 {} + static class Deep81 extends Deep80 {} + static class Deep82 extends Deep81 {} + static class Deep83 extends Deep82 {} + static class Deep84 extends Deep83 {} + static class Deep85 extends Deep84 {} + static class Deep86 extends Deep85 {} + static class Deep87 extends Deep86 {} + static class Deep88 extends Deep87 {} + static class Deep89 extends Deep88 {} + static class Deep90 extends Deep89 {} + static class Deep91 extends Deep90 {} + static class Deep92 extends Deep91 {} + static class Deep93 extends Deep92 {} + static class Deep94 extends Deep93 {} + static class Deep95 extends Deep94 {} + static class Deep96 extends Deep95 {} + static class Deep97 extends Deep96 {} + static class Deep98 extends Deep97 {} + static class Deep99 extends Deep98 {} + static class Deep100 extends Deep99 {} + + static class DeepCloneable extends Deep100 implements Cloneable { + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } + + public void time_Object_clone(int reps) { + try { + CloneableObject o = new CloneableObject(); + for (int rep = 0; rep < reps; ++rep) { + o.clone(); + } + } catch (Exception e) { + throw new AssertionError(e.getMessage()); + } + } + + public void time_Object_manyFieldClone(int reps) { + try { + CloneableManyFieldObject o = new CloneableManyFieldObject(); + for (int rep = 0; rep < reps; ++rep) { + o.clone(); + } + } catch (Exception e) { + throw new AssertionError(e.getMessage()); + } + } + + public void time_Object_deepClone(int reps) { + try { + DeepCloneable o = new DeepCloneable(); + for (int rep = 0; rep < reps; ++rep) { + o.clone(); + } + } catch (Exception e) { + throw new AssertionError(e.getMessage()); + } + } + + public void time_Array_clone(int reps) { + int[] o = new int[32]; + for (int rep = 0; rep < reps; ++rep) { + o.clone(); + } + } + + public void time_ObjectArray_smallClone(int reps) { + Object[] o = new Object[32]; + for (int i = 0; i < o.length / 2; ++i) { + o[i] = new Object(); + } + for (int rep = 0; rep < reps; ++rep) { + o.clone(); + } + } + + public void time_ObjectArray_largeClone(int reps) { + Object[] o = new Object[2048]; + for (int i = 0; i < o.length / 2; ++i) { + o[i] = new Object(); + } + for (int rep = 0; rep < reps; ++rep) { + o.clone(); + } + } +} diff --git a/benchmarks/src/benchmarks/ImtConflictBenchmark.java b/benchmarks/src/benchmarks/ImtConflictBenchmark.java new file mode 100644 index 000000000..faff03dbe --- /dev/null +++ b/benchmarks/src/benchmarks/ImtConflictBenchmark.java @@ -0,0 +1,1706 @@ +/* + * Copyright 2016 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. + */ + +package benchmarks; +import com.google.caliper.BeforeExperiment; + +/** + * This file is script-generated by ImtConflictBenchmarkGen.py. + * It measures the performance impact of conflicts in interface method tables. + * Run `python ImtConflictBenchmarkGen.py > ImtConflictBenchmark.java` to regenerate. + * + * Each interface has 64 methods, which is the current size of an IMT. C0 implements + * one interface, C1 implements two, C2 implements three, and so on. The intent + * is that C0 has no conflicts in its IMT, C1 has depth-2 conflicts in + * its IMT, C2 has depth-3 conflicts, etc. This is currently guaranteed by + * the fact that we hash interface methods by taking their method index modulo 64. + * (Note that a "conflict depth" of 1 means no conflict at all.) + */ +public class ImtConflictBenchmark { + @BeforeExperiment + public void setup() { + C0 c0 = new C0(); + callF0(c0); + C1 c1 = new C1(); + callF0(c1); + callF43(c1); + C2 c2 = new C2(); + callF0(c2); + callF43(c2); + callF86(c2); + C3 c3 = new C3(); + callF0(c3); + callF43(c3); + callF86(c3); + callF129(c3); + C4 c4 = new C4(); + callF0(c4); + callF43(c4); + callF86(c4); + callF129(c4); + callF172(c4); + C5 c5 = new C5(); + callF0(c5); + callF43(c5); + callF86(c5); + callF129(c5); + callF172(c5); + callF215(c5); + C6 c6 = new C6(); + callF0(c6); + callF43(c6); + callF86(c6); + callF129(c6); + callF172(c6); + callF215(c6); + callF258(c6); + C7 c7 = new C7(); + callF0(c7); + callF43(c7); + callF86(c7); + callF129(c7); + callF172(c7); + callF215(c7); + callF258(c7); + callF301(c7); + C8 c8 = new C8(); + callF0(c8); + callF43(c8); + callF86(c8); + callF129(c8); + callF172(c8); + callF215(c8); + callF258(c8); + callF301(c8); + callF344(c8); + C9 c9 = new C9(); + callF0(c9); + callF43(c9); + callF86(c9); + callF129(c9); + callF172(c9); + callF215(c9); + callF258(c9); + callF301(c9); + callF344(c9); + callF387(c9); + C10 c10 = new C10(); + callF0(c10); + callF43(c10); + callF86(c10); + callF129(c10); + callF172(c10); + callF215(c10); + callF258(c10); + callF301(c10); + callF344(c10); + callF387(c10); + callF430(c10); + C11 c11 = new C11(); + callF0(c11); + callF43(c11); + callF86(c11); + callF129(c11); + callF172(c11); + callF215(c11); + callF258(c11); + callF301(c11); + callF344(c11); + callF387(c11); + callF430(c11); + callF473(c11); + C12 c12 = new C12(); + callF0(c12); + callF43(c12); + callF86(c12); + callF129(c12); + callF172(c12); + callF215(c12); + callF258(c12); + callF301(c12); + callF344(c12); + callF387(c12); + callF430(c12); + callF473(c12); + callF516(c12); + C13 c13 = new C13(); + callF0(c13); + callF43(c13); + callF86(c13); + callF129(c13); + callF172(c13); + callF215(c13); + callF258(c13); + callF301(c13); + callF344(c13); + callF387(c13); + callF430(c13); + callF473(c13); + callF516(c13); + callF559(c13); + C14 c14 = new C14(); + callF0(c14); + callF43(c14); + callF86(c14); + callF129(c14); + callF172(c14); + callF215(c14); + callF258(c14); + callF301(c14); + callF344(c14); + callF387(c14); + callF430(c14); + callF473(c14); + callF516(c14); + callF559(c14); + callF602(c14); + C15 c15 = new C15(); + callF0(c15); + callF43(c15); + callF86(c15); + callF129(c15); + callF172(c15); + callF215(c15); + callF258(c15); + callF301(c15); + callF344(c15); + callF387(c15); + callF430(c15); + callF473(c15); + callF516(c15); + callF559(c15); + callF602(c15); + callF645(c15); + C16 c16 = new C16(); + callF0(c16); + callF43(c16); + callF86(c16); + callF129(c16); + callF172(c16); + callF215(c16); + callF258(c16); + callF301(c16); + callF344(c16); + callF387(c16); + callF430(c16); + callF473(c16); + callF516(c16); + callF559(c16); + callF602(c16); + callF645(c16); + callF688(c16); + C17 c17 = new C17(); + callF0(c17); + callF43(c17); + callF86(c17); + callF129(c17); + callF172(c17); + callF215(c17); + callF258(c17); + callF301(c17); + callF344(c17); + callF387(c17); + callF430(c17); + callF473(c17); + callF516(c17); + callF559(c17); + callF602(c17); + callF645(c17); + callF688(c17); + callF731(c17); + C18 c18 = new C18(); + callF0(c18); + callF43(c18); + callF86(c18); + callF129(c18); + callF172(c18); + callF215(c18); + callF258(c18); + callF301(c18); + callF344(c18); + callF387(c18); + callF430(c18); + callF473(c18); + callF516(c18); + callF559(c18); + callF602(c18); + callF645(c18); + callF688(c18); + callF731(c18); + callF774(c18); + C19 c19 = new C19(); + callF0(c19); + callF43(c19); + callF86(c19); + callF129(c19); + callF172(c19); + callF215(c19); + callF258(c19); + callF301(c19); + callF344(c19); + callF387(c19); + callF430(c19); + callF473(c19); + callF516(c19); + callF559(c19); + callF602(c19); + callF645(c19); + callF688(c19); + callF731(c19); + callF774(c19); + callF817(c19); + } + public void timeConflictDepth01(int nreps) { + C0 c0 = new C0(); + for (int i = 0; i < nreps; i++) { + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + callF0(c0); + } + } + public void timeConflictDepth02(int nreps) { + C1 c1 = new C1(); + for (int i = 0; i < nreps; i++) { + callF0(c1); + callF43(c1); + callF0(c1); + callF43(c1); + callF0(c1); + callF43(c1); + callF0(c1); + callF43(c1); + callF0(c1); + callF43(c1); + callF0(c1); + callF43(c1); + callF0(c1); + callF43(c1); + callF0(c1); + callF43(c1); + callF0(c1); + callF43(c1); + callF0(c1); + callF43(c1); + } + } + public void timeConflictDepth03(int nreps) { + C2 c2 = new C2(); + for (int i = 0; i < nreps; i++) { + callF0(c2); + callF43(c2); + callF86(c2); + callF0(c2); + callF43(c2); + callF86(c2); + callF0(c2); + callF43(c2); + callF86(c2); + callF0(c2); + callF43(c2); + callF86(c2); + callF0(c2); + callF43(c2); + callF86(c2); + callF0(c2); + callF43(c2); + callF86(c2); + callF0(c2); + callF43(c2); + } + } + public void timeConflictDepth04(int nreps) { + C3 c3 = new C3(); + for (int i = 0; i < nreps; i++) { + callF0(c3); + callF43(c3); + callF86(c3); + callF129(c3); + callF0(c3); + callF43(c3); + callF86(c3); + callF129(c3); + callF0(c3); + callF43(c3); + callF86(c3); + callF129(c3); + callF0(c3); + callF43(c3); + callF86(c3); + callF129(c3); + callF0(c3); + callF43(c3); + callF86(c3); + callF129(c3); + } + } + public void timeConflictDepth05(int nreps) { + C4 c4 = new C4(); + for (int i = 0; i < nreps; i++) { + callF0(c4); + callF43(c4); + callF86(c4); + callF129(c4); + callF172(c4); + callF0(c4); + callF43(c4); + callF86(c4); + callF129(c4); + callF172(c4); + callF0(c4); + callF43(c4); + callF86(c4); + callF129(c4); + callF172(c4); + callF0(c4); + callF43(c4); + callF86(c4); + callF129(c4); + callF172(c4); + } + } + public void timeConflictDepth06(int nreps) { + C5 c5 = new C5(); + for (int i = 0; i < nreps; i++) { + callF0(c5); + callF43(c5); + callF86(c5); + callF129(c5); + callF172(c5); + callF215(c5); + callF0(c5); + callF43(c5); + callF86(c5); + callF129(c5); + callF172(c5); + callF215(c5); + callF0(c5); + callF43(c5); + callF86(c5); + callF129(c5); + callF172(c5); + callF215(c5); + callF0(c5); + callF43(c5); + } + } + public void timeConflictDepth07(int nreps) { + C6 c6 = new C6(); + for (int i = 0; i < nreps; i++) { + callF0(c6); + callF43(c6); + callF86(c6); + callF129(c6); + callF172(c6); + callF215(c6); + callF258(c6); + callF0(c6); + callF43(c6); + callF86(c6); + callF129(c6); + callF172(c6); + callF215(c6); + callF258(c6); + callF0(c6); + callF43(c6); + callF86(c6); + callF129(c6); + callF172(c6); + callF215(c6); + } + } + public void timeConflictDepth08(int nreps) { + C7 c7 = new C7(); + for (int i = 0; i < nreps; i++) { + callF0(c7); + callF43(c7); + callF86(c7); + callF129(c7); + callF172(c7); + callF215(c7); + callF258(c7); + callF301(c7); + callF0(c7); + callF43(c7); + callF86(c7); + callF129(c7); + callF172(c7); + callF215(c7); + callF258(c7); + callF301(c7); + callF0(c7); + callF43(c7); + callF86(c7); + callF129(c7); + } + } + public void timeConflictDepth09(int nreps) { + C8 c8 = new C8(); + for (int i = 0; i < nreps; i++) { + callF0(c8); + callF43(c8); + callF86(c8); + callF129(c8); + callF172(c8); + callF215(c8); + callF258(c8); + callF301(c8); + callF344(c8); + callF0(c8); + callF43(c8); + callF86(c8); + callF129(c8); + callF172(c8); + callF215(c8); + callF258(c8); + callF301(c8); + callF344(c8); + callF0(c8); + callF43(c8); + } + } + public void timeConflictDepth10(int nreps) { + C9 c9 = new C9(); + for (int i = 0; i < nreps; i++) { + callF0(c9); + callF43(c9); + callF86(c9); + callF129(c9); + callF172(c9); + callF215(c9); + callF258(c9); + callF301(c9); + callF344(c9); + callF387(c9); + callF0(c9); + callF43(c9); + callF86(c9); + callF129(c9); + callF172(c9); + callF215(c9); + callF258(c9); + callF301(c9); + callF344(c9); + callF387(c9); + } + } + public void timeConflictDepth11(int nreps) { + C10 c10 = new C10(); + for (int i = 0; i < nreps; i++) { + callF0(c10); + callF43(c10); + callF86(c10); + callF129(c10); + callF172(c10); + callF215(c10); + callF258(c10); + callF301(c10); + callF344(c10); + callF387(c10); + callF430(c10); + callF0(c10); + callF43(c10); + callF86(c10); + callF129(c10); + callF172(c10); + callF215(c10); + callF258(c10); + callF301(c10); + callF344(c10); + } + } + public void timeConflictDepth12(int nreps) { + C11 c11 = new C11(); + for (int i = 0; i < nreps; i++) { + callF0(c11); + callF43(c11); + callF86(c11); + callF129(c11); + callF172(c11); + callF215(c11); + callF258(c11); + callF301(c11); + callF344(c11); + callF387(c11); + callF430(c11); + callF473(c11); + callF0(c11); + callF43(c11); + callF86(c11); + callF129(c11); + callF172(c11); + callF215(c11); + callF258(c11); + callF301(c11); + } + } + public void timeConflictDepth13(int nreps) { + C12 c12 = new C12(); + for (int i = 0; i < nreps; i++) { + callF0(c12); + callF43(c12); + callF86(c12); + callF129(c12); + callF172(c12); + callF215(c12); + callF258(c12); + callF301(c12); + callF344(c12); + callF387(c12); + callF430(c12); + callF473(c12); + callF516(c12); + callF0(c12); + callF43(c12); + callF86(c12); + callF129(c12); + callF172(c12); + callF215(c12); + callF258(c12); + } + } + public void timeConflictDepth14(int nreps) { + C13 c13 = new C13(); + for (int i = 0; i < nreps; i++) { + callF0(c13); + callF43(c13); + callF86(c13); + callF129(c13); + callF172(c13); + callF215(c13); + callF258(c13); + callF301(c13); + callF344(c13); + callF387(c13); + callF430(c13); + callF473(c13); + callF516(c13); + callF559(c13); + callF0(c13); + callF43(c13); + callF86(c13); + callF129(c13); + callF172(c13); + callF215(c13); + } + } + public void timeConflictDepth15(int nreps) { + C14 c14 = new C14(); + for (int i = 0; i < nreps; i++) { + callF0(c14); + callF43(c14); + callF86(c14); + callF129(c14); + callF172(c14); + callF215(c14); + callF258(c14); + callF301(c14); + callF344(c14); + callF387(c14); + callF430(c14); + callF473(c14); + callF516(c14); + callF559(c14); + callF602(c14); + callF0(c14); + callF43(c14); + callF86(c14); + callF129(c14); + callF172(c14); + } + } + public void timeConflictDepth16(int nreps) { + C15 c15 = new C15(); + for (int i = 0; i < nreps; i++) { + callF0(c15); + callF43(c15); + callF86(c15); + callF129(c15); + callF172(c15); + callF215(c15); + callF258(c15); + callF301(c15); + callF344(c15); + callF387(c15); + callF430(c15); + callF473(c15); + callF516(c15); + callF559(c15); + callF602(c15); + callF645(c15); + callF0(c15); + callF43(c15); + callF86(c15); + callF129(c15); + } + } + public void timeConflictDepth17(int nreps) { + C16 c16 = new C16(); + for (int i = 0; i < nreps; i++) { + callF0(c16); + callF43(c16); + callF86(c16); + callF129(c16); + callF172(c16); + callF215(c16); + callF258(c16); + callF301(c16); + callF344(c16); + callF387(c16); + callF430(c16); + callF473(c16); + callF516(c16); + callF559(c16); + callF602(c16); + callF645(c16); + callF688(c16); + callF0(c16); + callF43(c16); + callF86(c16); + } + } + public void timeConflictDepth18(int nreps) { + C17 c17 = new C17(); + for (int i = 0; i < nreps; i++) { + callF0(c17); + callF43(c17); + callF86(c17); + callF129(c17); + callF172(c17); + callF215(c17); + callF258(c17); + callF301(c17); + callF344(c17); + callF387(c17); + callF430(c17); + callF473(c17); + callF516(c17); + callF559(c17); + callF602(c17); + callF645(c17); + callF688(c17); + callF731(c17); + callF0(c17); + callF43(c17); + } + } + public void timeConflictDepth19(int nreps) { + C18 c18 = new C18(); + for (int i = 0; i < nreps; i++) { + callF0(c18); + callF43(c18); + callF86(c18); + callF129(c18); + callF172(c18); + callF215(c18); + callF258(c18); + callF301(c18); + callF344(c18); + callF387(c18); + callF430(c18); + callF473(c18); + callF516(c18); + callF559(c18); + callF602(c18); + callF645(c18); + callF688(c18); + callF731(c18); + callF774(c18); + callF0(c18); + } + } + public void timeConflictDepth20(int nreps) { + C19 c19 = new C19(); + for (int i = 0; i < nreps; i++) { + callF0(c19); + callF43(c19); + callF86(c19); + callF129(c19); + callF172(c19); + callF215(c19); + callF258(c19); + callF301(c19); + callF344(c19); + callF387(c19); + callF430(c19); + callF473(c19); + callF516(c19); + callF559(c19); + callF602(c19); + callF645(c19); + callF688(c19); + callF731(c19); + callF774(c19); + callF817(c19); + } + } + public void callF0(I0 i) { i.f0(); } + public void callF43(I1 i) { i.f43(); } + public void callF86(I2 i) { i.f86(); } + public void callF129(I3 i) { i.f129(); } + public void callF172(I4 i) { i.f172(); } + public void callF215(I5 i) { i.f215(); } + public void callF258(I6 i) { i.f258(); } + public void callF301(I7 i) { i.f301(); } + public void callF344(I8 i) { i.f344(); } + public void callF387(I9 i) { i.f387(); } + public void callF430(I10 i) { i.f430(); } + public void callF473(I11 i) { i.f473(); } + public void callF516(I12 i) { i.f516(); } + public void callF559(I13 i) { i.f559(); } + public void callF602(I14 i) { i.f602(); } + public void callF645(I15 i) { i.f645(); } + public void callF688(I16 i) { i.f688(); } + public void callF731(I17 i) { i.f731(); } + public void callF774(I18 i) { i.f774(); } + public void callF817(I19 i) { i.f817(); } + static class C0 implements I0 {} + static class C1 implements I0, I1 {} + static class C2 implements I0, I1, I2 {} + static class C3 implements I0, I1, I2, I3 {} + static class C4 implements I0, I1, I2, I3, I4 {} + static class C5 implements I0, I1, I2, I3, I4, I5 {} + static class C6 implements I0, I1, I2, I3, I4, I5, I6 {} + static class C7 implements I0, I1, I2, I3, I4, I5, I6, I7 {} + static class C8 implements I0, I1, I2, I3, I4, I5, I6, I7, I8 {} + static class C9 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9 {} + static class C10 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10 {} + static class C11 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11 {} + static class C12 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12 {} + static class C13 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13 {} + static class C14 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14 {} + static class C15 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15 {} + static class C16 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16 {} + static class C17 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16, I17 {} + static class C18 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16, I17, I18 {} + static class C19 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16, I17, I18, I19 {} + static interface I0 { + default void f0() {} + default void f1() {} + default void f2() {} + default void f3() {} + default void f4() {} + default void f5() {} + default void f6() {} + default void f7() {} + default void f8() {} + default void f9() {} + default void f10() {} + default void f11() {} + default void f12() {} + default void f13() {} + default void f14() {} + default void f15() {} + default void f16() {} + default void f17() {} + default void f18() {} + default void f19() {} + default void f20() {} + default void f21() {} + default void f22() {} + default void f23() {} + default void f24() {} + default void f25() {} + default void f26() {} + default void f27() {} + default void f28() {} + default void f29() {} + default void f30() {} + default void f31() {} + default void f32() {} + default void f33() {} + default void f34() {} + default void f35() {} + default void f36() {} + default void f37() {} + default void f38() {} + default void f39() {} + default void f40() {} + default void f41() {} + default void f42() {} + } + static interface I1 { + default void f43() {} + default void f44() {} + default void f45() {} + default void f46() {} + default void f47() {} + default void f48() {} + default void f49() {} + default void f50() {} + default void f51() {} + default void f52() {} + default void f53() {} + default void f54() {} + default void f55() {} + default void f56() {} + default void f57() {} + default void f58() {} + default void f59() {} + default void f60() {} + default void f61() {} + default void f62() {} + default void f63() {} + default void f64() {} + default void f65() {} + default void f66() {} + default void f67() {} + default void f68() {} + default void f69() {} + default void f70() {} + default void f71() {} + default void f72() {} + default void f73() {} + default void f74() {} + default void f75() {} + default void f76() {} + default void f77() {} + default void f78() {} + default void f79() {} + default void f80() {} + default void f81() {} + default void f82() {} + default void f83() {} + default void f84() {} + default void f85() {} + } + static interface I2 { + default void f86() {} + default void f87() {} + default void f88() {} + default void f89() {} + default void f90() {} + default void f91() {} + default void f92() {} + default void f93() {} + default void f94() {} + default void f95() {} + default void f96() {} + default void f97() {} + default void f98() {} + default void f99() {} + default void f100() {} + default void f101() {} + default void f102() {} + default void f103() {} + default void f104() {} + default void f105() {} + default void f106() {} + default void f107() {} + default void f108() {} + default void f109() {} + default void f110() {} + default void f111() {} + default void f112() {} + default void f113() {} + default void f114() {} + default void f115() {} + default void f116() {} + default void f117() {} + default void f118() {} + default void f119() {} + default void f120() {} + default void f121() {} + default void f122() {} + default void f123() {} + default void f124() {} + default void f125() {} + default void f126() {} + default void f127() {} + default void f128() {} + } + static interface I3 { + default void f129() {} + default void f130() {} + default void f131() {} + default void f132() {} + default void f133() {} + default void f134() {} + default void f135() {} + default void f136() {} + default void f137() {} + default void f138() {} + default void f139() {} + default void f140() {} + default void f141() {} + default void f142() {} + default void f143() {} + default void f144() {} + default void f145() {} + default void f146() {} + default void f147() {} + default void f148() {} + default void f149() {} + default void f150() {} + default void f151() {} + default void f152() {} + default void f153() {} + default void f154() {} + default void f155() {} + default void f156() {} + default void f157() {} + default void f158() {} + default void f159() {} + default void f160() {} + default void f161() {} + default void f162() {} + default void f163() {} + default void f164() {} + default void f165() {} + default void f166() {} + default void f167() {} + default void f168() {} + default void f169() {} + default void f170() {} + default void f171() {} + } + static interface I4 { + default void f172() {} + default void f173() {} + default void f174() {} + default void f175() {} + default void f176() {} + default void f177() {} + default void f178() {} + default void f179() {} + default void f180() {} + default void f181() {} + default void f182() {} + default void f183() {} + default void f184() {} + default void f185() {} + default void f186() {} + default void f187() {} + default void f188() {} + default void f189() {} + default void f190() {} + default void f191() {} + default void f192() {} + default void f193() {} + default void f194() {} + default void f195() {} + default void f196() {} + default void f197() {} + default void f198() {} + default void f199() {} + default void f200() {} + default void f201() {} + default void f202() {} + default void f203() {} + default void f204() {} + default void f205() {} + default void f206() {} + default void f207() {} + default void f208() {} + default void f209() {} + default void f210() {} + default void f211() {} + default void f212() {} + default void f213() {} + default void f214() {} + } + static interface I5 { + default void f215() {} + default void f216() {} + default void f217() {} + default void f218() {} + default void f219() {} + default void f220() {} + default void f221() {} + default void f222() {} + default void f223() {} + default void f224() {} + default void f225() {} + default void f226() {} + default void f227() {} + default void f228() {} + default void f229() {} + default void f230() {} + default void f231() {} + default void f232() {} + default void f233() {} + default void f234() {} + default void f235() {} + default void f236() {} + default void f237() {} + default void f238() {} + default void f239() {} + default void f240() {} + default void f241() {} + default void f242() {} + default void f243() {} + default void f244() {} + default void f245() {} + default void f246() {} + default void f247() {} + default void f248() {} + default void f249() {} + default void f250() {} + default void f251() {} + default void f252() {} + default void f253() {} + default void f254() {} + default void f255() {} + default void f256() {} + default void f257() {} + } + static interface I6 { + default void f258() {} + default void f259() {} + default void f260() {} + default void f261() {} + default void f262() {} + default void f263() {} + default void f264() {} + default void f265() {} + default void f266() {} + default void f267() {} + default void f268() {} + default void f269() {} + default void f270() {} + default void f271() {} + default void f272() {} + default void f273() {} + default void f274() {} + default void f275() {} + default void f276() {} + default void f277() {} + default void f278() {} + default void f279() {} + default void f280() {} + default void f281() {} + default void f282() {} + default void f283() {} + default void f284() {} + default void f285() {} + default void f286() {} + default void f287() {} + default void f288() {} + default void f289() {} + default void f290() {} + default void f291() {} + default void f292() {} + default void f293() {} + default void f294() {} + default void f295() {} + default void f296() {} + default void f297() {} + default void f298() {} + default void f299() {} + default void f300() {} + } + static interface I7 { + default void f301() {} + default void f302() {} + default void f303() {} + default void f304() {} + default void f305() {} + default void f306() {} + default void f307() {} + default void f308() {} + default void f309() {} + default void f310() {} + default void f311() {} + default void f312() {} + default void f313() {} + default void f314() {} + default void f315() {} + default void f316() {} + default void f317() {} + default void f318() {} + default void f319() {} + default void f320() {} + default void f321() {} + default void f322() {} + default void f323() {} + default void f324() {} + default void f325() {} + default void f326() {} + default void f327() {} + default void f328() {} + default void f329() {} + default void f330() {} + default void f331() {} + default void f332() {} + default void f333() {} + default void f334() {} + default void f335() {} + default void f336() {} + default void f337() {} + default void f338() {} + default void f339() {} + default void f340() {} + default void f341() {} + default void f342() {} + default void f343() {} + } + static interface I8 { + default void f344() {} + default void f345() {} + default void f346() {} + default void f347() {} + default void f348() {} + default void f349() {} + default void f350() {} + default void f351() {} + default void f352() {} + default void f353() {} + default void f354() {} + default void f355() {} + default void f356() {} + default void f357() {} + default void f358() {} + default void f359() {} + default void f360() {} + default void f361() {} + default void f362() {} + default void f363() {} + default void f364() {} + default void f365() {} + default void f366() {} + default void f367() {} + default void f368() {} + default void f369() {} + default void f370() {} + default void f371() {} + default void f372() {} + default void f373() {} + default void f374() {} + default void f375() {} + default void f376() {} + default void f377() {} + default void f378() {} + default void f379() {} + default void f380() {} + default void f381() {} + default void f382() {} + default void f383() {} + default void f384() {} + default void f385() {} + default void f386() {} + } + static interface I9 { + default void f387() {} + default void f388() {} + default void f389() {} + default void f390() {} + default void f391() {} + default void f392() {} + default void f393() {} + default void f394() {} + default void f395() {} + default void f396() {} + default void f397() {} + default void f398() {} + default void f399() {} + default void f400() {} + default void f401() {} + default void f402() {} + default void f403() {} + default void f404() {} + default void f405() {} + default void f406() {} + default void f407() {} + default void f408() {} + default void f409() {} + default void f410() {} + default void f411() {} + default void f412() {} + default void f413() {} + default void f414() {} + default void f415() {} + default void f416() {} + default void f417() {} + default void f418() {} + default void f419() {} + default void f420() {} + default void f421() {} + default void f422() {} + default void f423() {} + default void f424() {} + default void f425() {} + default void f426() {} + default void f427() {} + default void f428() {} + default void f429() {} + } + static interface I10 { + default void f430() {} + default void f431() {} + default void f432() {} + default void f433() {} + default void f434() {} + default void f435() {} + default void f436() {} + default void f437() {} + default void f438() {} + default void f439() {} + default void f440() {} + default void f441() {} + default void f442() {} + default void f443() {} + default void f444() {} + default void f445() {} + default void f446() {} + default void f447() {} + default void f448() {} + default void f449() {} + default void f450() {} + default void f451() {} + default void f452() {} + default void f453() {} + default void f454() {} + default void f455() {} + default void f456() {} + default void f457() {} + default void f458() {} + default void f459() {} + default void f460() {} + default void f461() {} + default void f462() {} + default void f463() {} + default void f464() {} + default void f465() {} + default void f466() {} + default void f467() {} + default void f468() {} + default void f469() {} + default void f470() {} + default void f471() {} + default void f472() {} + } + static interface I11 { + default void f473() {} + default void f474() {} + default void f475() {} + default void f476() {} + default void f477() {} + default void f478() {} + default void f479() {} + default void f480() {} + default void f481() {} + default void f482() {} + default void f483() {} + default void f484() {} + default void f485() {} + default void f486() {} + default void f487() {} + default void f488() {} + default void f489() {} + default void f490() {} + default void f491() {} + default void f492() {} + default void f493() {} + default void f494() {} + default void f495() {} + default void f496() {} + default void f497() {} + default void f498() {} + default void f499() {} + default void f500() {} + default void f501() {} + default void f502() {} + default void f503() {} + default void f504() {} + default void f505() {} + default void f506() {} + default void f507() {} + default void f508() {} + default void f509() {} + default void f510() {} + default void f511() {} + default void f512() {} + default void f513() {} + default void f514() {} + default void f515() {} + } + static interface I12 { + default void f516() {} + default void f517() {} + default void f518() {} + default void f519() {} + default void f520() {} + default void f521() {} + default void f522() {} + default void f523() {} + default void f524() {} + default void f525() {} + default void f526() {} + default void f527() {} + default void f528() {} + default void f529() {} + default void f530() {} + default void f531() {} + default void f532() {} + default void f533() {} + default void f534() {} + default void f535() {} + default void f536() {} + default void f537() {} + default void f538() {} + default void f539() {} + default void f540() {} + default void f541() {} + default void f542() {} + default void f543() {} + default void f544() {} + default void f545() {} + default void f546() {} + default void f547() {} + default void f548() {} + default void f549() {} + default void f550() {} + default void f551() {} + default void f552() {} + default void f553() {} + default void f554() {} + default void f555() {} + default void f556() {} + default void f557() {} + default void f558() {} + } + static interface I13 { + default void f559() {} + default void f560() {} + default void f561() {} + default void f562() {} + default void f563() {} + default void f564() {} + default void f565() {} + default void f566() {} + default void f567() {} + default void f568() {} + default void f569() {} + default void f570() {} + default void f571() {} + default void f572() {} + default void f573() {} + default void f574() {} + default void f575() {} + default void f576() {} + default void f577() {} + default void f578() {} + default void f579() {} + default void f580() {} + default void f581() {} + default void f582() {} + default void f583() {} + default void f584() {} + default void f585() {} + default void f586() {} + default void f587() {} + default void f588() {} + default void f589() {} + default void f590() {} + default void f591() {} + default void f592() {} + default void f593() {} + default void f594() {} + default void f595() {} + default void f596() {} + default void f597() {} + default void f598() {} + default void f599() {} + default void f600() {} + default void f601() {} + } + static interface I14 { + default void f602() {} + default void f603() {} + default void f604() {} + default void f605() {} + default void f606() {} + default void f607() {} + default void f608() {} + default void f609() {} + default void f610() {} + default void f611() {} + default void f612() {} + default void f613() {} + default void f614() {} + default void f615() {} + default void f616() {} + default void f617() {} + default void f618() {} + default void f619() {} + default void f620() {} + default void f621() {} + default void f622() {} + default void f623() {} + default void f624() {} + default void f625() {} + default void f626() {} + default void f627() {} + default void f628() {} + default void f629() {} + default void f630() {} + default void f631() {} + default void f632() {} + default void f633() {} + default void f634() {} + default void f635() {} + default void f636() {} + default void f637() {} + default void f638() {} + default void f639() {} + default void f640() {} + default void f641() {} + default void f642() {} + default void f643() {} + default void f644() {} + } + static interface I15 { + default void f645() {} + default void f646() {} + default void f647() {} + default void f648() {} + default void f649() {} + default void f650() {} + default void f651() {} + default void f652() {} + default void f653() {} + default void f654() {} + default void f655() {} + default void f656() {} + default void f657() {} + default void f658() {} + default void f659() {} + default void f660() {} + default void f661() {} + default void f662() {} + default void f663() {} + default void f664() {} + default void f665() {} + default void f666() {} + default void f667() {} + default void f668() {} + default void f669() {} + default void f670() {} + default void f671() {} + default void f672() {} + default void f673() {} + default void f674() {} + default void f675() {} + default void f676() {} + default void f677() {} + default void f678() {} + default void f679() {} + default void f680() {} + default void f681() {} + default void f682() {} + default void f683() {} + default void f684() {} + default void f685() {} + default void f686() {} + default void f687() {} + } + static interface I16 { + default void f688() {} + default void f689() {} + default void f690() {} + default void f691() {} + default void f692() {} + default void f693() {} + default void f694() {} + default void f695() {} + default void f696() {} + default void f697() {} + default void f698() {} + default void f699() {} + default void f700() {} + default void f701() {} + default void f702() {} + default void f703() {} + default void f704() {} + default void f705() {} + default void f706() {} + default void f707() {} + default void f708() {} + default void f709() {} + default void f710() {} + default void f711() {} + default void f712() {} + default void f713() {} + default void f714() {} + default void f715() {} + default void f716() {} + default void f717() {} + default void f718() {} + default void f719() {} + default void f720() {} + default void f721() {} + default void f722() {} + default void f723() {} + default void f724() {} + default void f725() {} + default void f726() {} + default void f727() {} + default void f728() {} + default void f729() {} + default void f730() {} + } + static interface I17 { + default void f731() {} + default void f732() {} + default void f733() {} + default void f734() {} + default void f735() {} + default void f736() {} + default void f737() {} + default void f738() {} + default void f739() {} + default void f740() {} + default void f741() {} + default void f742() {} + default void f743() {} + default void f744() {} + default void f745() {} + default void f746() {} + default void f747() {} + default void f748() {} + default void f749() {} + default void f750() {} + default void f751() {} + default void f752() {} + default void f753() {} + default void f754() {} + default void f755() {} + default void f756() {} + default void f757() {} + default void f758() {} + default void f759() {} + default void f760() {} + default void f761() {} + default void f762() {} + default void f763() {} + default void f764() {} + default void f765() {} + default void f766() {} + default void f767() {} + default void f768() {} + default void f769() {} + default void f770() {} + default void f771() {} + default void f772() {} + default void f773() {} + } + static interface I18 { + default void f774() {} + default void f775() {} + default void f776() {} + default void f777() {} + default void f778() {} + default void f779() {} + default void f780() {} + default void f781() {} + default void f782() {} + default void f783() {} + default void f784() {} + default void f785() {} + default void f786() {} + default void f787() {} + default void f788() {} + default void f789() {} + default void f790() {} + default void f791() {} + default void f792() {} + default void f793() {} + default void f794() {} + default void f795() {} + default void f796() {} + default void f797() {} + default void f798() {} + default void f799() {} + default void f800() {} + default void f801() {} + default void f802() {} + default void f803() {} + default void f804() {} + default void f805() {} + default void f806() {} + default void f807() {} + default void f808() {} + default void f809() {} + default void f810() {} + default void f811() {} + default void f812() {} + default void f813() {} + default void f814() {} + default void f815() {} + default void f816() {} + } + static interface I19 { + default void f817() {} + default void f818() {} + default void f819() {} + default void f820() {} + default void f821() {} + default void f822() {} + default void f823() {} + default void f824() {} + default void f825() {} + default void f826() {} + default void f827() {} + default void f828() {} + default void f829() {} + default void f830() {} + default void f831() {} + default void f832() {} + default void f833() {} + default void f834() {} + default void f835() {} + default void f836() {} + default void f837() {} + default void f838() {} + default void f839() {} + default void f840() {} + default void f841() {} + default void f842() {} + default void f843() {} + default void f844() {} + default void f845() {} + default void f846() {} + default void f847() {} + default void f848() {} + default void f849() {} + default void f850() {} + default void f851() {} + default void f852() {} + default void f853() {} + default void f854() {} + default void f855() {} + default void f856() {} + default void f857() {} + default void f858() {} + default void f859() {} + } +} diff --git a/benchmarks/src/benchmarks/ImtConflictBenchmarkGen.py b/benchmarks/src/benchmarks/ImtConflictBenchmarkGen.py new file mode 100644 index 000000000..8f1fd7e9c --- /dev/null +++ b/benchmarks/src/benchmarks/ImtConflictBenchmarkGen.py @@ -0,0 +1,102 @@ +# +# Copyright 2016 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. +# + +import sys + +max_conflict_depth = 20 # In practice does not go above 20 for reasonable IMT sizes +try: + imt_size = int(sys.argv[1]) +except (IndexError, ValueError): + print("Usage: python ImtConflictBenchmarkGen.py ") + sys.exit(1) + +license = """\ +/* + * Copyright 2016 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. + */ +""" +description = """ +/** + * This file is script-generated by ImtConflictBenchmarkGen.py. + * It measures the performance impact of conflicts in interface method tables. + * Run `python ImtConflictBenchmarkGen.py > ImtConflictBenchmark.java` to regenerate. + * + * Each interface has 64 methods, which is the current size of an IMT. C0 implements + * one interface, C1 implements two, C2 implements three, and so on. The intent + * is that C0 has no conflicts in its IMT, C1 has depth-2 conflicts in + * its IMT, C2 has depth-3 conflicts, etc. This is currently guaranteed by + * the fact that we hash interface methods by taking their method index modulo 64. + * (Note that a "conflict depth" of 1 means no conflict at all.) + */\ +""" + +print(license) +print("package benchmarks;") +print("import com.google.caliper.BeforeExperiment;") +print(description) + +print("public class ImtConflictBenchmark {") + +# Warm up interface method tables +print(" @BeforeExperiment") +print(" public void setup() {") +for i in xrange(max_conflict_depth): + print(" C{0} c{0} = new C{0}();".format(i)) + for j in xrange(i+1): + print(" callF{}(c{});".format(imt_size * j, i)) +print(" }") + +# Print test cases--one for each conflict depth +for i in xrange(max_conflict_depth): + print(" public void timeConflictDepth{:02d}(int nreps) {{".format(i+1)) + print(" C{0} c{0} = new C{0}();".format(i)) + print(" for (int i = 0; i < nreps; i++) {") + # Cycle through each interface method in an IMT entry in order + # to test all conflict resolution possibilities + for j in xrange(max_conflict_depth): + print(" callF{}(c{});".format(imt_size * (j % (i + 1)), i)) + print(" }") + print(" }") + +# Make calls through the IMTs +for i in xrange(max_conflict_depth): + print(" public void callF{0}(I{1} i) {{ i.f{0}(); }}".format(imt_size*i, i)) + +# Class definitions, implementing varying amounts of interfaces +for i in xrange(max_conflict_depth): + interfaces = ", ".join(["I{}".format(j) for j in xrange(i+1)]) + print(" static class C{} implements {} {{}}".format(i, interfaces)) + +# Interface definitions, each with enough methods to fill an entire IMT +for i in xrange(max_conflict_depth): + print(" static interface I{} {{".format(i)) + for j in xrange(imt_size): + print(" default void f{}() {{}}".format(i*imt_size + j)) + print(" }") + +print "}" diff --git a/benchmarks/src/benchmarks/StringDexCacheBenchmark.java b/benchmarks/src/benchmarks/StringDexCacheBenchmark.java new file mode 100644 index 000000000..ce72b4d90 --- /dev/null +++ b/benchmarks/src/benchmarks/StringDexCacheBenchmark.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 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. + */ + +package benchmarks; + +/** + * How long does it take to access a string in the dex cache? + */ +public class StringDexCacheBenchmark { + public int timeStringDexCacheAccess(int reps) { + int v = 0; + for (int rep = 0; rep < reps; ++rep) { + // Deliberately obscured to make optimizations less likely. + String s = (rep >= 0) ? "hello, world!" : null; + v += s.length(); + } + return v; + } +} diff --git a/benchmarks/src/benchmarks/XmlSerializeBenchmark.java b/benchmarks/src/benchmarks/XmlSerializeBenchmark.java index 0ef262012..c542e87ce 100644 --- a/benchmarks/src/benchmarks/XmlSerializeBenchmark.java +++ b/benchmarks/src/benchmarks/XmlSerializeBenchmark.java @@ -88,7 +88,7 @@ protected void setUp() throws Exception { String[] splitted = datasetAsString.split(" "); dataset = new double[splitted.length]; for (int i = 0; i < splitted.length; i++) { - dataset[i] = Double.valueOf(splitted[i]); + dataset[i] = Double.parseDouble(splitted[i]); } } diff --git a/benchmarks/src/benchmarks/regression/DateFormatBenchmark.java b/benchmarks/src/benchmarks/regression/DateFormatBenchmark.java new file mode 100644 index 000000000..bd5bf1a76 --- /dev/null +++ b/benchmarks/src/benchmarks/regression/DateFormatBenchmark.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 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. + */ + +package benchmarks.regression; + +import com.google.caliper.BeforeExperiment; + +import java.text.DateFormat; +import java.util.Locale; + +public final class DateFormatBenchmark { + + private Locale locale1; + private Locale locale2; + private Locale locale3; + private Locale locale4; + + @BeforeExperiment + protected void setUp() throws Exception { + locale1 = Locale.TAIWAN; + locale2 = Locale.GERMANY; + locale3 = Locale.FRANCE; + locale4 = Locale.ITALY; + } + + public void timeGetDateTimeInstance(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + DateFormat.getDateTimeInstance(); + } + } + + public void timeGetDateTimeInstance_multiple(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale1); + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale2); + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale3); + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale4); + } + } +} diff --git a/benchmarks/src/benchmarks/regression/MessageDigestBenchmark.java b/benchmarks/src/benchmarks/regression/MessageDigestBenchmark.java index a98643416..40819f898 100644 --- a/benchmarks/src/benchmarks/regression/MessageDigestBenchmark.java +++ b/benchmarks/src/benchmarks/regression/MessageDigestBenchmark.java @@ -17,6 +17,7 @@ package benchmarks.regression; import com.google.caliper.Param; +import java.nio.ByteBuffer; import java.security.MessageDigest; public class MessageDigestBenchmark { @@ -29,6 +30,29 @@ public class MessageDigestBenchmark { } } + private static final int LARGE_DATA_SIZE = 256 * 1024; + private static final byte[] LARGE_DATA = new byte[LARGE_DATA_SIZE]; + static { + for (int i = 0; i < LARGE_DATA_SIZE; i++) { + LARGE_DATA[i] = (byte)i; + } + } + + private static final ByteBuffer SMALL_BUFFER = ByteBuffer.wrap(DATA); + private static final ByteBuffer SMALL_DIRECT_BUFFER = ByteBuffer.allocateDirect(DATA_SIZE); + static { + SMALL_DIRECT_BUFFER.put(DATA); + SMALL_DIRECT_BUFFER.flip(); + } + + private static final ByteBuffer LARGE_BUFFER = ByteBuffer.wrap(LARGE_DATA); + private static final ByteBuffer LARGE_DIRECT_BUFFER = + ByteBuffer.allocateDirect(LARGE_DATA_SIZE); + static { + LARGE_DIRECT_BUFFER.put(LARGE_DATA); + LARGE_DIRECT_BUFFER.flip(); + } + @Param private Algorithm algorithm; public enum Algorithm { MD5, SHA1, SHA256, SHA384, SHA512 }; @@ -45,4 +69,88 @@ public void time(int reps) throws Exception { digest.digest(); } } + + public void timeLargeArray(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), + provider.toString()); + digest.update(LARGE_DATA, 0, LARGE_DATA_SIZE); + digest.digest(); + } + } + + public void timeSmallChunkOfLargeArray(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), + provider.toString()); + digest.update(LARGE_DATA, LARGE_DATA_SIZE / 2, DATA_SIZE); + digest.digest(); + } + } + + public void timeSmallByteBuffer(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), + provider.toString()); + SMALL_BUFFER.position(0); + SMALL_BUFFER.limit(SMALL_BUFFER.capacity()); + digest.update(SMALL_BUFFER); + digest.digest(); + } + } + + public void timeSmallDirectByteBuffer(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), + provider.toString()); + SMALL_DIRECT_BUFFER.position(0); + SMALL_DIRECT_BUFFER.limit(SMALL_DIRECT_BUFFER.capacity()); + digest.update(SMALL_DIRECT_BUFFER); + digest.digest(); + } + } + + public void timeLargeByteBuffer(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), + provider.toString()); + LARGE_BUFFER.position(0); + LARGE_BUFFER.limit(LARGE_BUFFER.capacity()); + digest.update(LARGE_BUFFER); + digest.digest(); + } + } + + public void timeLargeDirectByteBuffer(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), + provider.toString()); + LARGE_DIRECT_BUFFER.position(0); + LARGE_DIRECT_BUFFER.limit(LARGE_DIRECT_BUFFER.capacity()); + digest.update(LARGE_DIRECT_BUFFER); + digest.digest(); + } + } + + public void timeSmallChunkOfLargeByteBuffer(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), + provider.toString()); + LARGE_BUFFER.position(LARGE_BUFFER.capacity() / 2); + LARGE_BUFFER.limit(LARGE_BUFFER.position() + DATA_SIZE); + digest.update(LARGE_BUFFER); + digest.digest(); + } + } + + public void timeSmallChunkOfLargeDirectByteBuffer(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), + provider.toString()); + LARGE_DIRECT_BUFFER.position(LARGE_DIRECT_BUFFER.capacity() / 2); + LARGE_DIRECT_BUFFER.limit(LARGE_DIRECT_BUFFER.position() + DATA_SIZE); + digest.update(LARGE_DIRECT_BUFFER); + digest.digest(); + } + } } diff --git a/benchmarks/src/benchmarks/regression/NativeMethodBenchmark.java b/benchmarks/src/benchmarks/regression/NativeMethodBenchmark.java index c30ea0833..dbb630829 100644 --- a/benchmarks/src/benchmarks/regression/NativeMethodBenchmark.java +++ b/benchmarks/src/benchmarks/regression/NativeMethodBenchmark.java @@ -32,16 +32,38 @@ public void time_emptyJniSynchronizedMethod0(int reps) throws Exception { } } - public void time_emptyJniStaticMethod0(int reps) throws Exception { + + public void time_emptyJniMethod0(int reps) throws Exception { + NativeTestTarget n = new NativeTestTarget(); for (int i = 0; i < reps; ++i) { - NativeTestTarget.emptyJniStaticMethod0(); + n.emptyJniMethod0(); } } - public void time_emptyJniMethod0(int reps) throws Exception { + public void time_emptyJniMethod6(int reps) throws Exception { + int a = -1; + int b = 0; NativeTestTarget n = new NativeTestTarget(); for (int i = 0; i < reps; ++i) { - n.emptyJniMethod0(); + n.emptyJniMethod6(a, b, 1, 2, 3, i); + } + } + + public void time_emptyJniMethod6L(int reps) throws Exception { + NativeTestTarget n = new NativeTestTarget(); + for (int i = 0; i < reps; ++i) { + n.emptyJniMethod6L(null, null, null, null, null, null); + } + } + + public void time_emptyJniStaticMethod6L(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + NativeTestTarget.emptyJniStaticMethod6L(null, null, null, null, null, null); + } + } + public void time_emptyJniStaticMethod0(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + NativeTestTarget.emptyJniStaticMethod0(); } } @@ -53,26 +75,59 @@ public void time_emptyJniStaticMethod6(int reps) throws Exception { } } - public void time_emptyJniMethod6(int reps) throws Exception { + public void time_emptyJniMethod0_Fast(int reps) throws Exception { + NativeTestTarget n = new NativeTestTarget(); + for (int i = 0; i < reps; ++i) { + n.emptyJniMethod0_Fast(); + } + } + + public void time_emptyJniMethod6_Fast(int reps) throws Exception { int a = -1; int b = 0; NativeTestTarget n = new NativeTestTarget(); for (int i = 0; i < reps; ++i) { - n.emptyJniMethod6(a, b, 1, 2, 3, i); + n.emptyJniMethod6_Fast(a, b, 1, 2, 3, i); } } - public void time_emptyJniStaticMethod6L(int reps) throws Exception { + public void time_emptyJniMethod6L_Fast(int reps) throws Exception { + NativeTestTarget n = new NativeTestTarget(); for (int i = 0; i < reps; ++i) { - NativeTestTarget.emptyJniStaticMethod6L(null, null, null, null, null, null); + n.emptyJniMethod6L_Fast(null, null, null, null, null, null); } } - public void time_emptyJniMethod6L(int reps) throws Exception { - NativeTestTarget n = new NativeTestTarget(); + public void time_emptyJniStaticMethod6L_Fast(int reps) throws Exception { for (int i = 0; i < reps; ++i) { - n.emptyJniMethod6L(null, null, null, null, null, null); + NativeTestTarget.emptyJniStaticMethod6L_Fast(null, null, null, null, null, null); + } + } + public void time_emptyJniStaticMethod0_Fast(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + NativeTestTarget.emptyJniStaticMethod0_Fast(); } } + public void time_emptyJniStaticMethod6_Fast(int reps) throws Exception { + int a = -1; + int b = 0; + for (int i = 0; i < reps; ++i) { + NativeTestTarget.emptyJniStaticMethod6_Fast(a, b, 1, 2, 3, i); + } + } + + public void time_emptyJniStaticMethod0_Critical(int reps) throws Exception { + for (int i = 0; i < reps; ++i) { + NativeTestTarget.emptyJniStaticMethod0_Critical(); + } + } + + public void time_emptyJniStaticMethod6_Critical(int reps) throws Exception { + int a = -1; + int b = 0; + for (int i = 0; i < reps; ++i) { + NativeTestTarget.emptyJniStaticMethod6_Critical(a, b, 1, 2, 3, i); + } + } } diff --git a/benchmarks/src/benchmarks/regression/SimpleDateFormatBenchmark.java b/benchmarks/src/benchmarks/regression/SimpleDateFormatBenchmark.java new file mode 100644 index 000000000..b9becc768 --- /dev/null +++ b/benchmarks/src/benchmarks/regression/SimpleDateFormatBenchmark.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2016 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. + */ +package benchmarks.regression; + +import android.icu.text.TimeZoneNames; + +import java.text.DateFormatSymbols; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +/** + * Benchmark for java.text.SimpleDateFormat. This tests common formatting, parsing and creation + * operations with a specific focus on TimeZone handling. + */ +public class SimpleDateFormatBenchmark { + public void time_createFormatWithTimeZone(int reps) { + for (int i = 0; i < reps; i++) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd z"); + } + } + + public void time_parseWithTimeZoneShort(int reps) throws ParseException { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd z"); + for (int i = 0; i < reps; i++) { + sdf.parse("2000.01.01 PST"); + } + } + + public void time_parseWithTimeZoneLong(int reps) throws ParseException { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd zzzz"); + for (int i = 0; i < reps; i++) { + sdf.parse("2000.01.01 Pacific Standard Time"); + } + } + + public void time_parseWithoutTimeZone(int reps) throws ParseException { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd"); + for (int i = 0; i < reps; i++) { + sdf.parse("2000.01.01"); + } + } + + public void time_createAndParseWithTimeZoneShort(int reps) throws ParseException { + for (int i = 0; i < reps; i++) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd z"); + sdf.parse("2000.01.01 PST"); + } + } + + public void time_createAndParseWithTimeZoneLong(int reps) throws ParseException { + for (int i = 0; i < reps; i++) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd zzzz"); + sdf.parse("2000.01.01 Pacific Standard Time"); + } + } + + public void time_formatWithTimeZoneShort(int reps) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd z"); + for (int i = 0; i < reps; i++) { + sdf.format(new Date()); + } + } + + public void time_formatWithTimeZoneLong(int reps) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd zzzz"); + for (int i = 0; i < reps; i++) { + sdf.format(new Date()); + } + } + + /** + * Times first-time execution to measure effects of initial loading of data that's lost in + * full caliper benchmarks. + */ + public static void main(String[] args) throws ParseException { + long start, end; + + Locale locale = Locale.GERMAN; + start = System.nanoTime(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd zzzz", locale); + end = System.nanoTime(); + System.out.printf("Creating first SDF: %,d ns\n", end-start); + + // N code had special cases for currently-set and for default timezone. We want to measure + // the generic case. + sdf.setTimeZone(TimeZone.getTimeZone("Hongkong")); + + start = System.nanoTime(); + sdf.parse("2000.1.1 Kubanische Normalzeit"); + end = System.nanoTime(); + System.out.printf("First parse: %,d ns\n", end-start); + + start = System.nanoTime(); + sdf.format(new Date()); + end = System.nanoTime(); + System.out.printf("First format: %,d ns\n", end-start); + } +} diff --git a/check-ojluni-files b/check-ojluni-files new file mode 100755 index 000000000..c0066fcc3 --- /dev/null +++ b/check-ojluni-files @@ -0,0 +1,38 @@ +#!/bin/bash + + +# Copyright (C) 2016 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. + + +##### Script to check whether the files openjdk_java_files.mk match +##### those in the corresponding directory. +COMMAND='diff <(for i in $(openjdk_java_files); do echo "\$$i"; done | sort) ' +COMMAND=${COMMAND}'<( find ojluni/src/main/java -type f | grep '\''\.java$$'\'' | sort )' + +# Need to do it this nasty way (creating a Makefile on the fly and +# executing the bash command inside it) as to read the openjdk_java_files +# variable from an .mk file. +make -s -f <(cat <The annotation can be omitted from a method / constructor safely when the parameter metadata + * is not needed / desired at runtime. {@link Parameter#isNamePresent()} can be used to check + * whether metadata is present for a parameter, and the associated reflection methods like + * {@link java.lang.reflect.Parameter#getName()} will fall back to default behavior at runtime if + * the information is not present. + * + *

When including parameter metadata, compilers should include parameter metadata for generated + * classes like enums, since the parameter metadata includes whether or not a parameter is + * synthetic or mandated. + * + *

MethodParameters currently only describes individual method parameters and there is no + * mechanism to detect whether parameter method data is generally present for an + * {@link java.lang.reflect.Executable}. Therefore, it is code-size and runtime efficient to omit + * the annotation entirely for constructors and methods that have no parameters. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) +@interface MethodParameters { + + /* + * This annotation is never used in source code; it is expected to be generated in .dex + * files by tools like compilers. Commented definitions for the annotation members expected + * by the runtime / reflection code can be found below for reference. + * + * The arrays documented below must be the same size as for the method_id_item dex structure + * associated with the method otherwise a java.lang.reflect.MalformedParametersException will + * be thrown at runtime. + * + * That is: method_id_item.proto_idx -> proto_id_item.parameters_off -> type_list.size must + * be the same as names().length and accessFlags().length. + * + * Because MethodParameters describes all formal method parameters, even those not explicitly + * or implicitly declared in source code, the size of the arrays may differ from the Signature + * or other metadata information that can be based only on explicit parameters declared in + * source code. MethodParameters will also not include any information about type annotation + * receiver parameters that do not exist in the actual method signature. + */ + + + /* + * The names of formal parameters for the associated method. The array cannot be null, but can + * be empty if there are no formal parameters. A value in the array can be null if the formal + * parameter with that index has no name. + * + * If parameter name Strings are empty or contain '.', ';', '[' or '/' then a + * java.lang.reflect.MalformedParametersException will be thrown at runtime. + */ + // String[] names(); + + /* + * The access flags of the formal parameters for the associated method. The array cannot be + * null, but can be empty if there are no formal parameters. + * + * The value is a bit mask with the follow values: + * 0x0010 : final, the parameter was declared final + * 0x1000 : synthetic, the parameter was introduced by the compiler. + * 0x8000 : mandated, the parameter is synthetic but also implied by the language + * specification. + * + * If any bits are set outside of this set then a java.lang.reflect.MalformedParametersException + * will be thrown at runtime. + */ + // int[] accessFlags(); +} + diff --git a/dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java b/dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java new file mode 100644 index 000000000..4564b1899 --- /dev/null +++ b/dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 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. + */ + +package dalvik.annotation.optimization; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Applied to native methods to enable an ART runtime built-in optimization: + * methods that are annotated this way can speed up JNI transitions for methods that contain no + * objects (in parameters or return values, or as an implicit {@code this}). + * + *

+ * The native implementation must exclude the {@code JNIEnv} and {@code jclass} parameters from its + * function signature. As an additional limitation, the method must be explicitly registered with + * {@code RegisterNatives} instead of relying on the built-in dynamic JNI linking. + *

+ * + *

+ * Performance of JNI transitions: + *

+ * (Measured on angler-userdebug in 07/2016). + *

+ * + *

+ * A similar annotation, {@literal @}{@link FastNative}, exists with similar performance guarantees. + * However, unlike {@code @CriticalNative} it supports non-statics, object return values, and object + * parameters. If a method absolutely must have access to a {@code jobject}, then use + * {@literal @}{@link FastNative} instead of this. + *

+ * + *

+ * This has the side-effect of disabling all garbage collections while executing a critical native + * method. Use with extreme caution. Any long-running methods must not be marked with + * {@code @CriticalNative} (including usually-fast but generally unbounded methods)! + *

+ * + *

+ * Deadlock Warning: As a rule of thumb, do not acquire any locks during a critical native + * call if they aren't also locally released [before returning to managed code]. + *

+ * + *

+ * Say some code does: + * + * + * critical_native_call_to_grab_a_lock(); + * does_some_java_work(); + * critical_native_call_to_release_a_lock(); + * + * + *

+ * This code can lead to deadlocks. Say thread 1 just finishes + * {@code critical_native_call_to_grab_a_lock()} and is in {@code does_some_java_work()}. + * GC kicks in and suspends thread 1. Thread 2 now is in + * {@code critical_native_call_to_grab_a_lock()} but is blocked on grabbing the + * native lock since it's held by thread 1. Now thread suspension can't finish + * since thread 2 can't be suspended since it's doing CriticalNative JNI. + *

+ * + *

+ * Normal natives don't have the issue since once it's executing in native code, + * it is considered suspended from the runtime's point of view. + * CriticalNative natives however don't do the state transition done by the normal natives. + *

+ * + *

+ * This annotation has no effect when used with non-native methods. + * The runtime must throw a {@code VerifierError} upon class loading if this is used with a native + * method that contains object parameters, an object return value, or a non-static. + *

+ * + * @hide + */ +@Retention(RetentionPolicy.CLASS) // Save memory, don't instantiate as an object at runtime. +@Target(ElementType.METHOD) +public @interface CriticalNative {} diff --git a/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java b/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java new file mode 100644 index 000000000..605df4d58 --- /dev/null +++ b/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 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. + */ + +package dalvik.annotation.optimization; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An ART runtime built-in optimization for "native" methods to speed up JNI transitions. + * + *

+ * This has the side-effect of disabling all garbage collections while executing a fast native + * method. Use with extreme caution. Any long-running methods must not be marked with + * {@code @FastNative} (including usually-fast but generally unbounded methods)!

+ * + *

Deadlock Warning:As a rule of thumb, do not acquire any locks during a fast native + * call if they aren't also locally released [before returning to managed code].

+ * + *

+ * Say some code does: + * + * + * fast_jni_call_to_grab_a_lock(); + * does_some_java_work(); + * fast_jni_call_to_release_a_lock(); + * + * + *

+ * This code can lead to deadlocks. Say thread 1 just finishes + * {@code fast_jni_call_to_grab_a_lock()} and is in {@code does_some_java_work()}. + * GC kicks in and suspends thread 1. Thread 2 now is in {@code fast_jni_call_to_grab_a_lock()} + * but is blocked on grabbing the native lock since it's held by thread 1. + * Now thread suspension can't finish since thread 2 can't be suspended since it's doing + * FastNative JNI. + *

+ * + *

+ * Normal JNI doesn't have the issue since once it's in native code, + * it is considered suspended from java's point of view. + * FastNative JNI however doesn't do the state transition done by JNI. + *

+ * + *

+ * Has no effect when used with non-native methods. + *

+ * + * @hide + */ +@Retention(RetentionPolicy.CLASS) // Save memory, don't instantiate as an object at runtime. +@Target(ElementType.METHOD) +public @interface FastNative {} diff --git a/dalvik/src/main/java/dalvik/bytecode/Opcodes.java b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java index f758d65bc..7ce09c9a6 100644 --- a/dalvik/src/main/java/dalvik/bytecode/Opcodes.java +++ b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java @@ -245,6 +245,10 @@ public interface Opcodes { int OP_SHL_INT_LIT8 = 0x00e0; int OP_SHR_INT_LIT8 = 0x00e1; int OP_USHR_INT_LIT8 = 0x00e2; + int OP_INVOKE_POLYMORPHIC = 0x00fa; + int OP_INVOKE_POLYMORPHIC_RANGE = 0x00fb; + int OP_INVOKE_CUSTOM = 0x00fc; + int OP_INVOKE_CUSTOM_RANGE = 0x00fd; // END(libcore-opcodes) /** Never implemented; do not use. */ diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java index 1932ae3c1..bab2f4699 100644 --- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java +++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java @@ -18,6 +18,7 @@ import java.io.File; import java.net.URL; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @@ -27,16 +28,32 @@ * {@link ClassLoader} implementations. */ public class BaseDexClassLoader extends ClassLoader { + + /** + * Hook for customizing how dex files loads are reported. + * + * This enables the framework to monitor the use of dex files. The + * goal is to simplify the mechanism for optimizing foreign dex files and + * enable further optimizations of secondary dex files. + * + * The reporting happens only when new instances of BaseDexClassLoader + * are constructed and will be active only after this field is set with + * {@link BaseDexClassLoader#setReporter}. + */ + /* @NonNull */ private static volatile Reporter reporter = null; + private final DexPathList pathList; /** * Constructs an instance. + * Note that all the *.jar and *.apk files from {@code dexPath} might be + * first extracted in-memory before the code is loaded. This can be avoided + * by passing raw dex files (*.dex) in the {@code dexPath}. * * @param dexPath the list of jar/apk files containing classes and * resources, delimited by {@code File.pathSeparator}, which - * defaults to {@code ":"} on Android - * @param optimizedDirectory directory where optimized dex files - * should be written; may be {@code null} + * defaults to {@code ":"} on Android. + * @param optimizedDirectory this parameter is deprecated and has no effect * @param librarySearchPath the list of directories containing native * libraries, delimited by {@code File.pathSeparator}; may be * {@code null} @@ -45,7 +62,27 @@ public class BaseDexClassLoader extends ClassLoader { public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) { super(parent); - this.pathList = new DexPathList(this, dexPath, librarySearchPath, optimizedDirectory); + this.pathList = new DexPathList(this, dexPath, librarySearchPath, null); + + if (reporter != null) { + reporter.report(this.pathList.getDexPaths()); + } + } + + /** + * Constructs an instance. + * + * dexFile must be an in-memory representation of a full dexFile. + * + * @param dexFiles the array of in-memory dex files containing classes. + * @param parent the parent class loader + * + * @hide + */ + public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) { + // TODO We should support giving this a library search path maybe. + super(parent); + this.pathList = new DexPathList(this, dexFiles); } @Override @@ -53,7 +90,8 @@ protected Class findClass(String name) throws ClassNotFoundException { List suppressedExceptions = new ArrayList(); Class c = pathList.findClass(name, suppressedExceptions); if (c == null) { - ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList); + ClassNotFoundException cnfe = new ClassNotFoundException( + "Didn't find class \"" + name + "\" on path: " + pathList); for (Throwable t : suppressedExceptions) { cnfe.addSuppressed(t); } @@ -144,4 +182,30 @@ public String getLdLibraryPath() { @Override public String toString() { return getClass().getName() + "[" + pathList + "]"; } + + /** + * Sets the reporter for dex load notifications. + * Once set, all new instances of BaseDexClassLoader will report upon + * constructions the loaded dex files. + * + * @param newReporter the new Reporter. Setting null will cancel reporting. + * @hide + */ + public static void setReporter(Reporter newReporter) { + reporter = newReporter; + } + + /** + * @hide + */ + public static Reporter getReporter() { + return reporter; + } + + /** + * @hide + */ + public interface Reporter { + public void report(List dexPaths); + } } diff --git a/dalvik/src/main/java/dalvik/system/BlockGuard.java b/dalvik/src/main/java/dalvik/system/BlockGuard.java index b9de236c0..6426bd821 100644 --- a/dalvik/src/main/java/dalvik/system/BlockGuard.java +++ b/dalvik/src/main/java/dalvik/system/BlockGuard.java @@ -65,6 +65,11 @@ public interface Policy { */ void onNetwork(); + /** + * Called on unbuffered input/ouput operations. + */ + void onUnbufferedIO(); + /** * Returns the policy bitmask, for shipping over Binder calls * to remote threads/processes and reinstantiating the policy @@ -118,6 +123,7 @@ public String getMessage() { public void onWriteToDisk() {} public void onReadFromDisk() {} public void onNetwork() {} + public void onUnbufferedIO() {} public int getPolicyMask() { return 0; } diff --git a/dalvik/src/main/java/dalvik/system/CloseGuard.java b/dalvik/src/main/java/dalvik/system/CloseGuard.java index a45ffa10d..e718ee785 100644 --- a/dalvik/src/main/java/dalvik/system/CloseGuard.java +++ b/dalvik/src/main/java/dalvik/system/CloseGuard.java @@ -117,6 +117,16 @@ public final class CloseGuard { */ private static volatile Reporter REPORTER = new DefaultReporter(); + /** + * The default {@link Tracker}. + */ + private static final DefaultTracker DEFAULT_TRACKER = new DefaultTracker(); + + /** + * Hook for customizing how CloseGuard issues are tracked. + */ + private static volatile Tracker currentTracker = DEFAULT_TRACKER; + /** * Returns a CloseGuard instance. If CloseGuard is enabled, {@code * #open(String)} can be used to set up the instance to warn on @@ -138,6 +148,13 @@ public static void setEnabled(boolean enabled) { ENABLED = enabled; } + /** + * True if CloseGuard mechanism is enabled. + */ + public static boolean isEnabled() { + return ENABLED; + } + /** * Used to replace default Reporter used to warn of CloseGuard * violations. Must be non-null. @@ -156,6 +173,32 @@ public static Reporter getReporter() { return REPORTER; } + /** + * Sets the {@link Tracker} that is notified when resources are allocated and released. + * + *

This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so + * MUST NOT be used for any other purposes. + * + * @throws NullPointerException if tracker is null + */ + public static void setTracker(Tracker tracker) { + if (tracker == null) { + throw new NullPointerException("tracker == null"); + } + currentTracker = tracker; + } + + /** + * Returns {@link #setTracker(Tracker) last Tracker that was set}, or otherwise a default + * Tracker that does nothing. + * + *

This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so + * MUST NOT be used for any other purposes. + */ + public static Tracker getTracker() { + return currentTracker; + } + private CloseGuard() {} /** @@ -178,6 +221,7 @@ public void open(String closer) { } String message = "Explicit termination method '" + closer + "' not called"; allocationSite = new Throwable(message); + currentTracker.open(allocationSite); } private Throwable allocationSite; @@ -187,6 +231,7 @@ public void open(String closer) { * finalization. */ public void close() { + currentTracker.close(allocationSite); allocationSite = null; } @@ -208,11 +253,36 @@ public void warnIfOpen() { REPORTER.report(message, allocationSite); } + /** + * Interface to allow customization of tracking behaviour. + * + *

This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so + * MUST NOT be used for any other purposes. + */ + public interface Tracker { + void open(Throwable allocationSite); + void close(Throwable allocationSite); + } + + /** + * Default tracker which does nothing special and simply leaves it up to the GC to detect a + * leak. + */ + private static final class DefaultTracker implements Tracker { + @Override + public void open(Throwable allocationSite) { + } + + @Override + public void close(Throwable allocationSite) { + } + } + /** * Interface to allow customization of reporting behavior. */ - public static interface Reporter { - public void report (String message, Throwable allocationSite); + public interface Reporter { + void report (String message, Throwable allocationSite); } /** diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java index f1ec29da7..2a95450da 100644 --- a/dalvik/src/main/java/dalvik/system/DexFile.java +++ b/dalvik/src/main/java/dalvik/system/DexFile.java @@ -21,7 +21,9 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; import java.util.Enumeration; import java.util.List; import libcore.io.Libcore; @@ -41,7 +43,6 @@ public final class DexFile { private Object mCookie; private Object mInternalCookie; private final String mFileName; - private final CloseGuard guard = CloseGuard.get(); /** * Opens a DEX file from a given File object. This will usually be a ZIP/JAR @@ -113,10 +114,15 @@ public DexFile(String fileName) throws IOException { mCookie = openDexFile(fileName, null, 0, loader, elements); mInternalCookie = mCookie; mFileName = fileName; - guard.open("close"); //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName); } + DexFile(ByteBuffer buf) throws IOException { + mCookie = openInMemoryDexFile(buf); + mInternalCookie = mCookie; + mFileName = null; + } + /** * Opens a DEX file from a given filename, using a specified file * to hold the optimized data. @@ -148,6 +154,7 @@ private DexFile(String sourceName, String outputName, int flags, ClassLoader loa } mCookie = openDexFile(sourceName, outputName, flags, loader, elements); + mInternalCookie = mCookie; mFileName = sourceName; //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName); } @@ -231,7 +238,11 @@ public String getName() { } @Override public String toString() { - return getName(); + if (mFileName != null) { + return getName(); + } else { + return "InMemoryDexFile[cookie=" + Arrays.toString((long[]) mCookie) + "]"; + } } /** @@ -250,7 +261,6 @@ public void close() throws IOException { if (closeDexFile(mInternalCookie)) { mInternalCookie = null; } - guard.close(); mCookie = null; } } @@ -322,13 +332,13 @@ public Enumeration entries() { /* * Helper class. */ - private class DFEnum implements Enumeration { + private static class DFEnum implements Enumeration { private int mIndex; private String[] mNameList; DFEnum(DexFile df) { mIndex = 0; - mNameList = getClassNameList(mCookie); + mNameList = getClassNameList(df.mCookie); } public boolean hasMoreElements() { @@ -349,9 +359,6 @@ public String nextElement() { */ @Override protected void finalize() throws Throwable { try { - if (guard != null) { - guard.warnIfOpen(); - } if (mInternalCookie != null && !closeDexFile(mInternalCookie)) { throw new AssertionError("Failed to close dex file in finalizer."); } @@ -379,6 +386,17 @@ private static Object openDexFile(String sourceName, String outputName, int flag elements); } + private static Object openInMemoryDexFile(ByteBuffer buf) throws IOException { + if (buf.isDirect()) { + return createCookieWithDirectBuffer(buf, buf.position(), buf.limit()); + } else { + return createCookieWithArray(buf.array(), buf.position(), buf.limit()); + } + } + + private static native Object createCookieWithDirectBuffer(ByteBuffer buf, int start, int end); + private static native Object createCookieWithArray(byte[] buf, int start, int end); + /* * Returns true if the dex file is backed by a valid oat file. */ @@ -418,6 +436,8 @@ public static native boolean isDexOptNeeded(String fileName) throws FileNotFoundException, IOException; /** + * No dexopt should (or can) be done to update the apk/jar. + * * See {@link #getDexOptNeeded(String, String, int)}. * * @hide @@ -425,48 +445,43 @@ public static native boolean isDexOptNeeded(String fileName) public static final int NO_DEXOPT_NEEDED = 0; /** + * dex2oat should be run to update the apk/jar from scratch. + * * See {@link #getDexOptNeeded(String, String, int)}. * * @hide */ - public static final int DEX2OAT_NEEDED = 1; + public static final int DEX2OAT_FROM_SCRATCH = 1; /** + * dex2oat should be run to update the apk/jar because the existing code + * is out of date with respect to the boot image. + * * See {@link #getDexOptNeeded(String, String, int)}. * * @hide */ - public static final int PATCHOAT_NEEDED = 2; + public static final int DEX2OAT_FOR_BOOT_IMAGE = 2; /** + * dex2oat should be run to update the apk/jar because the existing code + * is out of date with respect to the target compiler filter. + * * See {@link #getDexOptNeeded(String, String, int)}. * * @hide */ - public static final int SELF_PATCHOAT_NEEDED = 3; + public static final int DEX2OAT_FOR_FILTER = 3; /** - * Returns whether the given filter is a valid filter. + * dex2oat should be run to update the apk/jar because the existing code + * is not relocated to match the boot image. * - * @hide - */ - public native static boolean isValidCompilerFilter(String filter); - - /** - * Returns whether the given filter is based on profiles. + * See {@link #getDexOptNeeded(String, String, int)}. * * @hide */ - public native static boolean isProfileGuidedCompilerFilter(String filter); - - /** - * Returns the version of the compiler filter that is not based on profiles. - * If the input is not a valid filter, or the filter is already not based on - * profiles, this returns the input. - * - * @hide - */ - public native static String getNonProfileGuidedCompilerFilter(String filter); + public static final int DEX2OAT_FOR_RELOCATION = 4; /** * Returns the VM's opinion of what kind of dexopt is needed to make the @@ -479,12 +494,11 @@ public static native boolean isDexOptNeeded(String fileName) * @param newProfile flag that describes whether a profile corresponding * to the dex file has been recently updated and should be considered * in the state of the file. - * @return NO_DEXOPT_NEEDED if the apk/jar is already up to date. - * DEX2OAT_NEEDED if dex2oat should be called on the apk/jar file. - * PATCHOAT_NEEDED if patchoat should be called on the apk/jar - * file to patch the odex file along side the apk/jar. - * SELF_PATCHOAT_NEEDED if selfpatchoat should be called on the - * apk/jar file to patch the oat file in the dalvik cache. + * @return NO_DEXOPT_NEEDED, or DEX2OAT_*. See documentation + * of the particular status code for more information on its + * meaning. Returns a positive status code if the status refers to + * the oat file in the oat location. Returns a negative status + * code if the status refers to the oat file in the odex location. * @throws java.io.FileNotFoundException if fileName is not readable, * not a file, or not present. * @throws java.io.IOException if fileName is not a valid apk/jar file or @@ -507,4 +521,36 @@ public static native int getDexOptNeeded(String fileName, */ public static native String getDexFileStatus(String fileName, String instructionSet) throws FileNotFoundException; + + /** + * Returns the full file path of the optimized dex file {@code fileName}. The returned string + * is the full file name including path of optimized dex file, if it exists. + * @hide + */ + public static native String getDexFileOutputPath(String fileName, String instructionSet) + throws FileNotFoundException; + + /** + * Returns whether the given filter is a valid filter. + * + * @hide + */ + public native static boolean isValidCompilerFilter(String filter); + + /** + * Returns whether the given filter is based on profiles. + * + * @hide + */ + public native static boolean isProfileGuidedCompilerFilter(String filter); + + /** + * Returns the version of the compiler filter that is not based on profiles. + * If the input is not a valid filter, or the filter is already not based on + * profiles, this returns the input. + * + * @hide + */ + public native static String getNonProfileGuidedCompilerFilter(String filter); + } diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java index 48cb792f6..3693bb2e2 100644 --- a/dalvik/src/main/java/dalvik/system/DexPathList.java +++ b/dalvik/src/main/java/dalvik/system/DexPathList.java @@ -22,15 +22,15 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.List; -import java.util.zip.ZipEntry; +import libcore.io.ClassPathURLStreamHandler; import libcore.io.IoUtils; import libcore.io.Libcore; -import libcore.io.ClassPathURLStreamHandler; import static android.system.OsConstants.S_ISDIR; @@ -62,7 +62,7 @@ private Element[] dexElements; /** List of native library path elements. */ - private final Element[] nativeLibraryPathElements; + private final NativeLibraryElement[] nativeLibraryPathElements; /** List of application native library directories. */ private final List nativeLibraryDirectories; @@ -75,6 +75,42 @@ */ private IOException[] dexElementsSuppressedExceptions; + /** + * Construct an instance. + * + * @param definingContext the context in which any as-yet unresolved + * classes should be defined + * + * @param dexFiles the bytebuffers containing the dex files that we should load classes from. + */ + public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles) { + if (definingContext == null) { + throw new NullPointerException("definingContext == null"); + } + if (dexFiles == null) { + throw new NullPointerException("dexFiles == null"); + } + if (Arrays.stream(dexFiles).anyMatch(v -> v == null)) { + throw new NullPointerException("dexFiles contains a null Buffer!"); + } + + this.definingContext = definingContext; + // TODO It might be useful to let in-memory dex-paths have native libraries. + this.nativeLibraryDirectories = Collections.emptyList(); + this.systemNativeLibraryDirectories = + splitPaths(System.getProperty("java.library.path"), true); + this.nativeLibraryPathElements = makePathElements(this.systemNativeLibraryDirectories); + + ArrayList suppressedExceptions = new ArrayList(); + this.dexElements = makeInMemoryDexElements(dexFiles, suppressedExceptions); + if (suppressedExceptions.size() > 0) { + this.dexElementsSuppressedExceptions = + suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]); + } else { + dexElementsSuppressedExceptions = null; + } + } + /** * Constructs an instance. * @@ -84,11 +120,6 @@ * {@code File.pathSeparator} * @param librarySearchPath list of native library directory path elements, * separated by {@code File.pathSeparator} - * @param libraryPermittedPath is path containing permitted directories for - * linker isolated namespaces (in addition to librarySearchPath which is allowed - * implicitly). Note that this path does not affect the search order for the library - * and intended for white-listing additional paths when loading native libraries - * by absolute path. * @param optimizedDirectory directory where optimized {@code .dex} files * should be found and written to, or {@code null} to use the default * system directory for same @@ -142,9 +173,7 @@ public DexPathList(ClassLoader definingContext, String dexPath, List allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories); allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories); - this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories, - suppressedExceptions, - definingContext); + this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories); if (suppressedExceptions.size() > 0) { this.dexElementsSuppressedExceptions = @@ -253,98 +282,84 @@ private static List splitPaths(String searchPath, boolean directoriesOnly) return result; } + private static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles, + List suppressedExceptions) { + Element[] elements = new Element[dexFiles.length]; + int elementPos = 0; + for (ByteBuffer buf : dexFiles) { + try { + DexFile dex = new DexFile(buf); + elements[elementPos++] = new Element(dex); + } catch (IOException suppressed) { + System.logE("Unable to load dex file: " + buf, suppressed); + suppressedExceptions.add(suppressed); + } + } + if (elementPos != elements.length) { + elements = Arrays.copyOf(elements, elementPos); + } + return elements; + } + /** * Makes an array of dex/resource path elements, one per element of * the given array. */ private static Element[] makeDexElements(List files, File optimizedDirectory, - List suppressedExceptions, - ClassLoader loader) { - return makeElements(files, optimizedDirectory, suppressedExceptions, false, loader); - } - - /** - * Makes an array of directory/zip path elements, one per element of the given array. - */ - private static Element[] makePathElements(List files, - List suppressedExceptions, - ClassLoader loader) { - return makeElements(files, null, suppressedExceptions, true, loader); - } - - /* - * TODO (dimitry): Revert after apps stops relying on the existence of this - * method (see http://b/21957414 and http://b/26317852 for details) - */ - private static Element[] makePathElements(List files, File optimizedDirectory, - List suppressedExceptions) { - return makeElements(files, optimizedDirectory, suppressedExceptions, false, null); - } - - private static Element[] makeElements(List files, File optimizedDirectory, - List suppressedExceptions, - boolean ignoreDexFiles, - ClassLoader loader) { - Element[] elements = new Element[files.size()]; - int elementsPos = 0; - /* - * Open all files and load the (direct or contained) dex files - * up front. - */ - for (File file : files) { - File zip = null; - File dir = new File(""); - DexFile dex = null; - String path = file.getPath(); - String name = file.getName(); - - if (path.contains(zipSeparator)) { - String split[] = path.split(zipSeparator, 2); - zip = new File(split[0]); - dir = new File(split[1]); - } else if (file.isDirectory()) { - // We support directories for looking up resources and native libraries. - // Looking up resources in directories is useful for running libcore tests. - elements[elementsPos++] = new Element(file, true, null, null); - } else if (file.isFile()) { - if (!ignoreDexFiles && name.endsWith(DEX_SUFFIX)) { - // Raw dex file (not inside a zip/jar). - try { - dex = loadDexFile(file, optimizedDirectory, loader, elements); - } catch (IOException suppressed) { - System.logE("Unable to load dex file: " + file, suppressed); - suppressedExceptions.add(suppressed); - } - } else { - zip = file; - - if (!ignoreDexFiles) { - try { - dex = loadDexFile(file, optimizedDirectory, loader, elements); - } catch (IOException suppressed) { - /* - * IOException might get thrown "legitimately" by the DexFile constructor if - * the zip file turns out to be resource-only (that is, no classes.dex file - * in it). - * Let dex == null and hang on to the exception to add to the tea-leaves for - * when findClass returns null. - */ - suppressedExceptions.add(suppressed); - } - } - } - } else { - System.logW("ClassLoader referenced unknown path: " + file); - } - - if ((zip != null) || (dex != null)) { - elements[elementsPos++] = new Element(dir, false, zip, dex); - } - } - if (elementsPos != elements.length) { - elements = Arrays.copyOf(elements, elementsPos); - } - return elements; + List suppressedExceptions, ClassLoader loader) { + Element[] elements = new Element[files.size()]; + int elementsPos = 0; + /* + * Open all files and load the (direct or contained) dex files up front. + */ + for (File file : files) { + if (file.isDirectory()) { + // We support directories for looking up resources. Looking up resources in + // directories is useful for running libcore tests. + elements[elementsPos++] = new Element(file); + } else if (file.isFile()) { + String name = file.getName(); + + if (name.endsWith(DEX_SUFFIX)) { + // Raw dex file (not inside a zip/jar). + try { + DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements); + if (dex != null) { + elements[elementsPos++] = new Element(dex, null); + } + } catch (IOException suppressed) { + System.logE("Unable to load dex file: " + file, suppressed); + suppressedExceptions.add(suppressed); + } + } else { + DexFile dex = null; + try { + dex = loadDexFile(file, optimizedDirectory, loader, elements); + } catch (IOException suppressed) { + /* + * IOException might get thrown "legitimately" by the DexFile constructor if + * the zip file turns out to be resource-only (that is, no classes.dex file + * in it). + * Let dex == null and hang on to the exception to add to the tea-leaves for + * when findClass returns null. + */ + suppressedExceptions.add(suppressed); + } + + if (dex == null) { + elements[elementsPos++] = new Element(file); + } else { + elements[elementsPos++] = new Element(dex, file); + } + } + } else { + System.logW("ClassLoader referenced unknown path: " + file); + } + } + if (elementsPos != elements.length) { + elements = Arrays.copyOf(elements, elementsPos); + } + return elements; } /** @@ -398,6 +413,42 @@ private static String optimizedPathFor(File path, return result.getPath(); } + /* + * TODO (dimitry): Revert after apps stops relying on the existence of this + * method (see http://b/21957414 and http://b/26317852 for details) + */ + @SuppressWarnings("unused") + private static Element[] makePathElements(List files, File optimizedDirectory, + List suppressedExceptions) { + return makeDexElements(files, optimizedDirectory, suppressedExceptions, null); + } + + /** + * Makes an array of directory/zip path elements for the native library search path, one per + * element of the given array. + */ + private static NativeLibraryElement[] makePathElements(List files) { + NativeLibraryElement[] elements = new NativeLibraryElement[files.size()]; + int elementsPos = 0; + for (File file : files) { + String path = file.getPath(); + + if (path.contains(zipSeparator)) { + String split[] = path.split(zipSeparator, 2); + File zip = new File(split[0]); + String dir = split[1]; + elements[elementsPos++] = new NativeLibraryElement(zip, dir); + } else if (file.isDirectory()) { + // We support directories for looking up native libraries. + elements[elementsPos++] = new NativeLibraryElement(file); + } + } + if (elementsPos != elements.length) { + elements = Arrays.copyOf(elements, elementsPos); + } + return elements; + } + /** * Finds the named class in one of the dex files pointed at by * this instance. This will find the one in the earliest listed @@ -410,17 +461,14 @@ private static String optimizedPathFor(File path, * @return the named class or {@code null} if the class is not * found in any of the dex files */ - public Class findClass(String name, List suppressed) { + public Class findClass(String name, List suppressed) { for (Element element : dexElements) { - DexFile dex = element.dexFile; - - if (dex != null) { - Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); - if (clazz != null) { - return clazz; - } + Class clazz = element.findClass(name, definingContext, suppressed); + if (clazz != null) { + return clazz; } } + if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } @@ -476,7 +524,7 @@ public Enumeration findResources(String name) { public String findLibrary(String libraryName) { String fileName = System.mapLibraryName(libraryName); - for (Element element : nativeLibraryPathElements) { + for (NativeLibraryElement element : nativeLibraryPathElements) { String path = element.findNativeLibrary(fileName); if (path != null) { @@ -488,32 +536,108 @@ public String findLibrary(String libraryName) { } /** - * Element of the dex/resource/native library path + * Returns the list of all individual dex files paths from the current list. + * The list will contain only file paths (i.e. no directories). + */ + /*package*/ List getDexPaths() { + List dexPaths = new ArrayList(); + for (Element e : dexElements) { + String dexPath = e.getDexPath(); + if (dexPath != null) { + // Add the element to the list only if it is a file. A null dex path signals the + // element is a resource directory or an in-memory dex file. + dexPaths.add(dexPath); + } + } + return dexPaths; + } + + /** + * Element of the dex/resource path. Note: should be called DexElement, but apps reflect on + * this. */ /*package*/ static class Element { - private final File dir; - private final boolean isDirectory; - private final File zip; + /** + * A file denoting a zip file (in case of a resource jar or a dex jar), or a directory + * (only when dexFile is null). + */ + private final File path; + private final DexFile dexFile; private ClassPathURLStreamHandler urlHandler; private boolean initialized; - public Element(File dir, boolean isDirectory, File zip, DexFile dexFile) { - this.dir = dir; - this.isDirectory = isDirectory; - this.zip = zip; + /** + * Element encapsulates a dex file. This may be a plain dex file (in which case dexZipPath + * should be null), or a jar (in which case dexZipPath should denote the zip file). + */ + public Element(DexFile dexFile, File dexZipPath) { + this.dexFile = dexFile; + this.path = dexZipPath; + } + + public Element(DexFile dexFile) { this.dexFile = dexFile; + this.path = null; + } + + public Element(File path) { + this.path = path; + this.dexFile = null; + } + + /** + * Constructor for a bit of backwards compatibility. Some apps use reflection into + * internal APIs. Warn, and emulate old behavior if we can. See b/33399341. + * + * @deprecated The Element class has been split. Use new Element constructors for + * classes and resources, and NativeLibraryElement for the library + * search path. + */ + @Deprecated + public Element(File dir, boolean isDirectory, File zip, DexFile dexFile) { + System.err.println("Warning: Using deprecated Element constructor. Do not use internal" + + " APIs, this constructor will be removed in the future."); + if (dir != null && (zip != null || dexFile != null)) { + throw new IllegalArgumentException("Using dir and zip|dexFile no longer" + + " supported."); + } + if (isDirectory && (zip != null || dexFile != null)) { + throw new IllegalArgumentException("Unsupported argument combination."); + } + if (dir != null) { + this.path = dir; + this.dexFile = null; + } else { + this.path = zip; + this.dexFile = dexFile; + } } - @Override public String toString() { - if (isDirectory) { - return "directory \"" + dir + "\""; - } else if (zip != null) { - return "zip file \"" + zip + "\"" + - (dir != null && !dir.getPath().isEmpty() ? ", dir \"" + dir + "\"" : ""); + /* + * Returns the dex path of this element or null if the element refers to a directory. + */ + private String getDexPath() { + if (path != null) { + return path.isDirectory() ? null : path.getAbsolutePath(); + } else if (dexFile != null) { + // DexFile.getName() returns the path of the dex file. + return dexFile.getName(); + } + return null; + } + + @Override + public String toString() { + if (dexFile == null) { + return (path.isDirectory() ? "directory \"" : "zip file \"") + path + "\""; } else { + if (path == null) { return "dex file \"" + dexFile + "\""; + } else { + return "zip file \"" + path + "\""; + } } } @@ -522,14 +646,13 @@ public synchronized void maybeInit() { return; } - initialized = true; - - if (isDirectory || zip == null) { + if (path == null || path.isDirectory()) { + initialized = true; return; } try { - urlHandler = new ClassPathURLStreamHandler(zip.getPath()); + urlHandler = new ClassPathURLStreamHandler(path.getPath()); } catch (IOException ioe) { /* * Note: ZipException (a subclass of IOException) @@ -537,40 +660,35 @@ public synchronized void maybeInit() { * (e.g. if the file isn't actually a zip/jar * file). */ - System.logE("Unable to open zip file: " + zip, ioe); + System.logE("Unable to open zip file: " + path, ioe); urlHandler = null; } - } - public String findNativeLibrary(String name) { - maybeInit(); - - if (isDirectory) { - String path = new File(dir, name).getPath(); - if (IoUtils.canOpenReadOnly(path)) { - return path; - } - } else if (urlHandler != null) { - // Having a urlHandler means the element has a zip file. - // In this case Android supports loading the library iff - // it is stored in the zip uncompressed. - - String entryName = new File(dir, name).getPath(); - if (urlHandler.isEntryStored(entryName)) { - return zip.getPath() + zipSeparator + entryName; - } - } + // Mark this element as initialized only after we've successfully created + // the associated ClassPathURLStreamHandler. That way, we won't leave this + // element in an inconsistent state if an exception is thrown during initialization. + // + // See b/35633614. + initialized = true; + } - return null; + public Class findClass(String name, ClassLoader definingContext, + List suppressed) { + return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed) + : null; } public URL findResource(String name) { maybeInit(); + if (urlHandler != null) { + return urlHandler.getEntryUrlOrNull(name); + } + // We support directories so we can run tests and/or legacy code // that uses Class.getResource. - if (isDirectory) { - File resourceFile = new File(dir, name); + if (path != null && path.isDirectory()) { + File resourceFile = new File(path, name); if (resourceFile.exists()) { try { return resourceFile.toURI().toURL(); @@ -580,12 +698,105 @@ public URL findResource(String name) { } } - if (urlHandler == null) { - /* This element has no zip/jar file. + return null; + } + } + + /** + * Element of the native library path + */ + /*package*/ static class NativeLibraryElement { + /** + * A file denoting a directory or zip file. + */ + private final File path; + + /** + * If path denotes a zip file, this denotes a base path inside the zip. + */ + private final String zipDir; + + private ClassPathURLStreamHandler urlHandler; + private boolean initialized; + + public NativeLibraryElement(File dir) { + this.path = dir; + this.zipDir = null; + + // We should check whether path is a directory, but that is non-eliminatable overhead. + } + + public NativeLibraryElement(File zip, String zipDir) { + this.path = zip; + this.zipDir = zipDir; + + // Simple check that should be able to be eliminated by inlining. We should also + // check whether path is a file, but that is non-eliminatable overhead. + if (zipDir == null) { + throw new IllegalArgumentException(); + } + } + + @Override + public String toString() { + if (zipDir == null) { + return "directory \"" + path + "\""; + } else { + return "zip file \"" + path + "\"" + + (!zipDir.isEmpty() ? ", dir \"" + zipDir + "\"" : ""); + } + } + + public synchronized void maybeInit() { + if (initialized) { + return; + } + + if (zipDir == null) { + initialized = true; + return; + } + + try { + urlHandler = new ClassPathURLStreamHandler(path.getPath()); + } catch (IOException ioe) { + /* + * Note: ZipException (a subclass of IOException) + * might get thrown by the ZipFile constructor + * (e.g. if the file isn't actually a zip/jar + * file). */ - return null; + System.logE("Unable to open zip file: " + path, ioe); + urlHandler = null; } - return urlHandler.getEntryUrlOrNull(name); + + // Mark this element as initialized only after we've successfully created + // the associated ClassPathURLStreamHandler. That way, we won't leave this + // element in an inconsistent state if an exception is thrown during initialization. + // + // See b/35633614. + initialized = true; + } + + public String findNativeLibrary(String name) { + maybeInit(); + + if (zipDir == null) { + String entryPath = new File(path, name).getPath(); + if (IoUtils.canOpenReadOnly(entryPath)) { + return entryPath; + } + } else if (urlHandler != null) { + // Having a urlHandler means the element has a zip file. + // In this case Android supports loading the library iff + // it is stored in the zip uncompressed. + String entryName = zipDir + '/' + name; + if (urlHandler.isEntryStored(entryName)) { + return path.getPath() + zipSeparator + entryName; + } + } + + return null; } } } diff --git a/dalvik/src/main/java/dalvik/system/EmulatedStackFrame.java b/dalvik/src/main/java/dalvik/system/EmulatedStackFrame.java new file mode 100644 index 000000000..b479d6fd8 --- /dev/null +++ b/dalvik/src/main/java/dalvik/system/EmulatedStackFrame.java @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2016 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 + */ + +package dalvik.system; + +import java.lang.invoke.MethodType; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Provides typed (read-only) access to method arguments and a slot to store a return value. + * + * Used to implement method handle transforms. See {@link java.lang.invoke.Transformers}. + * + * @hide + */ +public class EmulatedStackFrame { + /** + * The type of this stack frame, i.e, the types of its arguments and the type of its + * return value. + */ + private final MethodType type; + + /** + * The type of the callsite that produced this stack frame. This contains the types of + * the original arguments, before any conversions etc. were performed. + */ + private final MethodType callsiteType; + + /** + * All reference arguments and reference return values that belong to this argument array. + * + * If the return type is a reference, it will be the last element of this array. + */ + private final Object[] references; + + /** + * Contains all primitive values on the stack. Primitive values always take 4 or 8 bytes of + * space and all {@code short}, {@code char} and {@code boolean} arguments are promoted to ints. + * + * Reference values do not appear on the stack frame but they appear (in order) + * in the {@code references} array. No additional slots or space for reference arguments or + * return values are reserved in the stackFrame. + * + * By convention, if the return value is a primitive, it will occupy the last 4 or 8 bytes + * of the stack frame, depending on the type. + * + * The size of this array is known at the time of creation of this {@code EmulatedStackFrame} + * and is determined by the {@code MethodType} of the frame. + * + * Example : + *

+     *     Function : String foo(String a, String b, int c, long d) { }
+     *
+     *     EmulatedStackFrame :
+     *     references = { a, b, [return_value] }
+     *     stackFrame = { c0, c1, c2, c3, d0, d1, d2, d3, d4, d5, d6, d7 }
+     *
+     *     Function : int foo(String a)
+     *
+     *     EmulatedStackFrame :
+     *     references = { a }
+     *     stackFrame = { rv0, rv1, rv2, rv3 }  // rv is the return value.
+     *
+     * 
+ * + */ + private final byte[] stackFrame; + + private EmulatedStackFrame(MethodType type, MethodType callsiteType, Object[] references, + byte[] stackFrame) { + this.type = type; + this.callsiteType = callsiteType; + this.references = references; + this.stackFrame = stackFrame; + } + + /** + * Returns the {@code MethodType} that the frame was created for. + */ + public final MethodType getMethodType() { return type; } + + /** + * Returns the {@code MethodType} corresponding to the callsite of the + */ + public final MethodType getCallsiteType() { return callsiteType; } + + /** + * Represents a range of arguments on an {@code EmulatedStackFrame}. + * + * @hide + */ + public static final class Range { + public final int referencesStart; + public final int numReferences; + + public final int stackFrameStart; + public final int numBytes; + + private Range(int referencesStart, int numReferences, int stackFrameStart, int numBytes) { + this.referencesStart = referencesStart; + this.numReferences = numReferences; + this.stackFrameStart = stackFrameStart; + this.numBytes = numBytes; + } + + public static Range all(MethodType frameType) { + return of(frameType, 0, frameType.parameterCount()); + } + + public static Range of(MethodType frameType, int startArg, int endArg) { + final Class[] ptypes = frameType.ptypes(); + + int referencesStart = 0; + int numReferences = 0; + int stackFrameStart = 0; + int numBytes = 0; + + for (int i = 0; i < startArg; ++i) { + Class cl = ptypes[i]; + if (!cl.isPrimitive()) { + referencesStart++; + } else { + stackFrameStart += getSize(cl); + } + } + + for (int i = startArg; i < endArg; ++i) { + Class cl = ptypes[i]; + if (!cl.isPrimitive()) { + numReferences++; + } else { + numBytes += getSize(cl); + } + } + + return new Range(referencesStart, numReferences, stackFrameStart, numBytes); + } + } + + /** + * Creates an emulated stack frame for a given {@code MethodType}. + */ + public static EmulatedStackFrame create(MethodType frameType) { + int numRefs = 0; + int frameSize = 0; + for (Class ptype : frameType.ptypes()) { + if (!ptype.isPrimitive()) { + numRefs++; + } else { + frameSize += getSize(ptype); + } + } + + final Class rtype = frameType.rtype(); + if (!rtype.isPrimitive()) { + numRefs++; + } else { + frameSize += getSize(rtype); + } + + return new EmulatedStackFrame(frameType, frameType, new Object[numRefs], + new byte[frameSize]); + } + + /** + * Sets the {@code idx} to {@code reference}. Type checks are performed. + */ + public void setReference(int idx, Object reference) { + final Class[] ptypes = type.ptypes(); + if (idx < 0 || idx >= ptypes.length) { + throw new IllegalArgumentException("Invalid index: " + idx); + } + + if (reference != null && !ptypes[idx].isInstance(reference)) { + throw new IllegalStateException("reference is not of type: " + type.ptypes()[idx]); + } + + references[idx] = reference; + } + + /** + * Gets the reference at {@code idx}, checking that it's of type {@code referenceType}. + */ + public T getReference(int idx, Class referenceType) { + if (referenceType != type.ptypes()[idx]) { + throw new IllegalArgumentException("Argument: " + idx + + " is of type " + type.ptypes()[idx] + " expected " + referenceType + ""); + } + + return (T) references[idx]; + } + + /** + * Copies a specified range of arguments, given by {@code fromRange} to a specified + * EmulatedStackFrame {@code other}, with references starting at {@code referencesStart} + * and primitives starting at {@code primitivesStart}. + */ + public void copyRangeTo(EmulatedStackFrame other, Range fromRange, int referencesStart, + int primitivesStart) { + if (fromRange.numReferences > 0) { + System.arraycopy(references, fromRange.referencesStart, + other.references, referencesStart, fromRange.numReferences); + } + + if (fromRange.numBytes > 0) { + System.arraycopy(stackFrame, fromRange.stackFrameStart, + other.stackFrame, primitivesStart, fromRange.numBytes); + } + } + + /** + * Copies the return value from this stack frame to {@code other}. + */ + public void copyReturnValueTo(EmulatedStackFrame other) { + final Class returnType = type.returnType(); + if (!returnType.isPrimitive()) { + other.references[other.references.length - 1] = references[references.length - 1]; + } else if (!is64BitPrimitive(returnType)) { + System.arraycopy(stackFrame, stackFrame.length - 4, + other.stackFrame, other.stackFrame.length - 4, 4); + } else { + System.arraycopy(stackFrame, stackFrame.length - 8, + other.stackFrame, other.stackFrame.length - 8, 8); + } + } + + public void setReturnValueTo(Object reference) { + final Class returnType = type.returnType(); + if (returnType.isPrimitive()) { + throw new IllegalStateException("return type is not a reference type: " + returnType); + } + + if (reference != null && !returnType.isInstance(reference)) { + throw new IllegalArgumentException("reference is not of type " + returnType); + } + + references[references.length - 1] = reference; + } + + /** + * Returns true iff. the input {@code type} needs 64 bits (8 bytes) of storage on an + * {@code EmulatedStackFrame}. + */ + private static boolean is64BitPrimitive(Class type) { + return type == double.class || type == long.class; + } + + /** + * Returns the size (in bytes) occupied by a given primitive type on an + * {@code EmulatedStackFrame}. + */ + public static int getSize(Class type) { + if (!type.isPrimitive()) { + throw new IllegalArgumentException("type.isPrimitive() == false: " + type); + } + + if (is64BitPrimitive(type)) { + return 8; + } else { + return 4; + } + } + + /** + * Base class for readers and writers to stack frames. + * + * @hide + */ + public static class StackFrameAccessor { + /** + * The current offset into the references array. + */ + protected int referencesOffset; + + /** + * The index of the current argument being processed. For a function of arity N, + * values [0, N) correspond to input arguments, and the special index {@code -2} + * maps to the return value. All other indices are invalid. + */ + protected int argumentIdx; + + /** + * Wrapper for {@code EmulatedStackFrame.this.stackFrame}. + */ + protected ByteBuffer frameBuf; + + /** + * The number of arguments that this stack frame expects. + */ + private int numArgs; + + /** + * The stack frame we're currently accessing. + */ + protected EmulatedStackFrame frame; + + /** + * The value of {@code argumentIdx} when this accessor's cursor is pointing to the + * frame's return value. + */ + private static final int RETURN_VALUE_IDX = -2; + + protected StackFrameAccessor() { + referencesOffset = 0; + argumentIdx = 0; + + frameBuf = null; + numArgs = 0; + } + + /** + * Attaches this accessor to a given {@code EmulatedStackFrame} to read or write + * values to it. Also resets all state associated with the current accessor. + */ + public StackFrameAccessor attach(EmulatedStackFrame stackFrame) { + return attach(stackFrame, 0 /* argumentIdx */, 0 /* referencesOffset */, + 0 /* frameOffset */); + } + + public StackFrameAccessor attach(EmulatedStackFrame stackFrame, int argumentIdx, + int referencesOffset, int frameOffset) { + frame = stackFrame; + frameBuf = ByteBuffer.wrap(frame.stackFrame).order(ByteOrder.LITTLE_ENDIAN); + numArgs = frame.type.ptypes().length; + if (frameOffset != 0) { + frameBuf.position(frameOffset); + } + + this.referencesOffset = referencesOffset; + this.argumentIdx = argumentIdx; + + return this; + } + + protected void checkType(Class type) { + if (argumentIdx >= numArgs || argumentIdx == (RETURN_VALUE_IDX + 1)) { + throw new IllegalArgumentException("Invalid argument index: " + argumentIdx); + } + + final Class expectedType = (argumentIdx == RETURN_VALUE_IDX) ? + frame.type.rtype() : frame.type.ptypes()[argumentIdx]; + + if (expectedType != type) { + throw new IllegalArgumentException("Incorrect type: " + type + + ", expected: " + expectedType); + } + } + + /** + * Positions the cursor at the return value location, either in the references array + * or in the stack frame array. The next put* or next* call will result in a read or + * write to the return value. + */ + public void makeReturnValueAccessor() { + Class rtype = frame.type.rtype(); + argumentIdx = RETURN_VALUE_IDX; + + // Position the cursor appropriately. The return value is either the last element + // of the references array, or the last 4 or 8 bytes of the stack frame. + if (rtype.isPrimitive()) { + frameBuf.position(frameBuf.capacity() - getSize(rtype)); + } else { + referencesOffset = frame.references.length - 1; + } + } + + public static void copyNext(StackFrameReader reader, StackFrameWriter writer, + Class type) { + if (!type.isPrimitive()) { + writer.putNextReference(reader.nextReference(type), type); + } else if (type == boolean.class) { + writer.putNextBoolean(reader.nextBoolean()); + } else if (type == byte.class) { + writer.putNextByte(reader.nextByte()); + } else if (type == char.class) { + writer.putNextChar(reader.nextChar()); + } else if (type == short.class) { + writer.putNextShort(reader.nextShort()); + } else if (type == int.class) { + writer.putNextInt(reader.nextInt()); + } else if (type == long.class) { + writer.putNextLong(reader.nextLong()); + } else if (type == float.class) { + writer.putNextFloat(reader.nextFloat()); + } else if (type == double.class) { + writer.putNextDouble(reader.nextDouble()); + } + } + } + + /** + * Provides sequential write access to an emulated stack frame. Allows writes to + * argument slots as well as return value slots. + */ + public static class StackFrameWriter extends StackFrameAccessor { + public void putNextByte(byte value) { + checkType(byte.class); + argumentIdx++; + frameBuf.putInt(value); + } + + public void putNextInt(int value) { + checkType(int.class); + argumentIdx++; + frameBuf.putInt(value); + } + + public void putNextLong(long value) { + checkType(long.class); + argumentIdx++; + frameBuf.putLong(value); + } + + public void putNextChar(char value) { + checkType(char.class); + argumentIdx++; + frameBuf.putInt((int) value); + } + + public void putNextBoolean(boolean value) { + checkType(boolean.class); + argumentIdx++; + frameBuf.putInt(value ? 1 : 0); + } + + public void putNextShort(short value) { + checkType(short.class); + argumentIdx++; + frameBuf.putInt((int) value); + } + + public void putNextFloat(float value) { + checkType(float.class); + argumentIdx++; + frameBuf.putFloat(value); + } + + public void putNextDouble(double value) { + checkType(double.class); + argumentIdx++; + frameBuf.putDouble(value); + } + + public void putNextReference(Object value, Class expectedType) { + checkType(expectedType); + argumentIdx++; + frame.references[referencesOffset++] = value; + } + } + + /** + * Provides sequential read access to an emulated stack frame. Allows reads to + * argument slots as well as to return value slots. + */ + public static class StackFrameReader extends StackFrameAccessor { + public byte nextByte() { + checkType(byte.class); + argumentIdx++; + return (byte) frameBuf.getInt(); + } + + public int nextInt() { + checkType(int.class); + argumentIdx++; + return frameBuf.getInt(); + } + + public long nextLong() { + checkType(long.class); + argumentIdx++; + return frameBuf.getLong(); + } + + public char nextChar() { + checkType(char.class); + argumentIdx++; + return (char) frameBuf.getInt(); + } + + public boolean nextBoolean() { + checkType(boolean.class); + argumentIdx++; + return (frameBuf.getInt() != 0); + } + + public short nextShort() { + checkType(short.class); + argumentIdx++; + return (short) frameBuf.getInt(); + } + + public float nextFloat() { + checkType(float.class); + argumentIdx++; + return frameBuf.getFloat(); + } + + public double nextDouble() { + checkType(double.class); + argumentIdx++; + return frameBuf.getDouble(); + } + + public T nextReference(Class expectedType) { + checkType(expectedType); + argumentIdx++; + return (T) frame.references[referencesOffset++]; + } + } +} diff --git a/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java b/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java new file mode 100644 index 000000000..0fa1e45a7 --- /dev/null +++ b/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 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. + */ + +package dalvik.system; + +import java.nio.ByteBuffer; + +/** + * A {@link ClassLoader} implementation that loads classes from a + * buffer containing a DEX file. This can be used to execute code that + * has not been written to the local file system. + */ +public final class InMemoryDexClassLoader extends BaseDexClassLoader { + /** + * Create an in-memory DEX class loader with the given dex buffers. + * + * @param dexBuffers array of buffers containing DEX files between + * buffer.position() and buffer.limit(). + * @param parent the parent class loader for delegation. + * @hide + */ + public InMemoryDexClassLoader(ByteBuffer[] dexBuffers, ClassLoader parent) { + super(dexBuffers, parent); + } + + /** + * Creates a new in-memory DEX class loader. + * + * @param dexBuffer buffer containing DEX file contents between + * buffer.position() and buffer.limit(). + * @param parent the parent class loader for delegation. + */ + public InMemoryDexClassLoader(ByteBuffer dexBuffer, ClassLoader parent) { + this(new ByteBuffer[] { dexBuffer }, parent); + } +} diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java index 23d740783..85b52f8e9 100644 --- a/dalvik/src/main/java/dalvik/system/VMDebug.java +++ b/dalvik/src/main/java/dalvik/system/VMDebug.java @@ -16,6 +16,7 @@ package dalvik.system; +import dalvik.annotation.optimization.FastNative; import java.io.FileDescriptor; import java.io.IOException; import java.util.HashMap; @@ -106,6 +107,7 @@ private VMDebug() {} * * @return the time in milliseconds, or -1 if the debugger is not connected */ + @FastNative public static native long lastDebuggerActivity(); /** @@ -114,6 +116,7 @@ private VMDebug() {} * * @return true if debugging is enabled */ + @FastNative public static native boolean isDebuggingEnabled(); /** @@ -121,6 +124,7 @@ private VMDebug() {} * * @return true if (and only if) a debugger is connected */ + @FastNative public static native boolean isDebuggerConnected(); /** @@ -172,11 +176,26 @@ public static void startMethodTracing(String traceFileName, int bufferSize, int * FileDescriptor in which the trace is written. The file name is also * supplied simply for logging. Makes a dup of the file descriptor. */ - public static void startMethodTracing(String traceFileName, FileDescriptor fd, int bufferSize, int flags, boolean samplingEnabled, int intervalUs) { + public static void startMethodTracing(String traceFileName, FileDescriptor fd, int bufferSize, + int flags, boolean samplingEnabled, int intervalUs) { + startMethodTracing(traceFileName, fd, bufferSize, flags, samplingEnabled, intervalUs, + false); + } + + /** + * Like startMethodTracing(String, int, int), but taking an already-opened + * FileDescriptor in which the trace is written. The file name is also + * supplied simply for logging. Makes a dup of the file descriptor. + * Streams tracing data to the file if streamingOutput is true. + */ + public static void startMethodTracing(String traceFileName, FileDescriptor fd, int bufferSize, + int flags, boolean samplingEnabled, int intervalUs, + boolean streamingOutput) { if (fd == null) { throw new NullPointerException("fd == null"); } - startMethodTracingFd(traceFileName, fd, checkBufferSize(bufferSize), flags, samplingEnabled, intervalUs); + startMethodTracingFd(traceFileName, fd, checkBufferSize(bufferSize), flags, + samplingEnabled, intervalUs, streamingOutput); } /** @@ -200,7 +219,7 @@ private static int checkBufferSize(int bufferSize) { } private static native void startMethodTracingDdmsImpl(int bufferSize, int flags, boolean samplingEnabled, int intervalUs); - private static native void startMethodTracingFd(String traceFileName, FileDescriptor fd, int bufferSize, int flags, boolean samplingEnabled, int intervalUs); + private static native void startMethodTracingFd(String traceFileName, FileDescriptor fd, int bufferSize, int flags, boolean samplingEnabled, int intervalUs, boolean streamingOutput); private static native void startMethodTracingFilename(String traceFileName, int bufferSize, int flags, boolean samplingEnabled, int intervalUs); /** @@ -236,6 +255,7 @@ private static int checkBufferSize(int bufferSize) { * @return the CPU usage. A value of -1 means the system does not support * this feature. */ + @FastNative public static native long threadCpuTimeNanos(); /** @@ -276,6 +296,7 @@ public static int setGlobalAllocationLimit(int limit) { /** * Dumps a list of loaded class to the log file. */ + @FastNative public static native void printLoadedClasses(int flags); /** @@ -283,6 +304,7 @@ public static int setGlobalAllocationLimit(int limit) { * * @return the number of loaded classes */ + @FastNative public static native int getLoadedClassCount(); /** @@ -461,4 +483,11 @@ public static Map getRuntimeStats() { private static native String getRuntimeStatInternal(int statId); private static native String[] getRuntimeStatsInternal(); + + /** + * Attaches an agent to the VM. + * + * @param agent The path to the agent .so file plus optional agent arguments. + */ + public static native void attachAgent(String agent) throws IOException; } diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java b/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java index 5daf6a02f..a9efabe88 100644 --- a/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java +++ b/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java @@ -16,6 +16,9 @@ package org.apache.harmony.dalvik; +import dalvik.annotation.optimization.CriticalNative; +import dalvik.annotation.optimization.FastNative; + /** * Methods used to test calling into native code. The methods in this * class are all effectively no-ops and may be used to test the mechanisms @@ -25,16 +28,42 @@ public final class NativeTestTarget { public NativeTestTarget() { } - public static native synchronized void emptyJniStaticSynchronizedMethod0(); + /** + * This is used to benchmark dalvik's inline natives. + */ + public static void emptyInlineMethod() { + } + + /** + * This is used to benchmark dalvik's inline natives. + */ + public static native void emptyInternalStaticMethod(); + // Synchronized methods. Test normal JNI only. + public static native synchronized void emptyJniStaticSynchronizedMethod0(); public native synchronized void emptyJniSynchronizedMethod0(); + // Static methods without object parameters. Test all optimization combinations. + + // Normal native. public static native void emptyJniStaticMethod0(); + // Normal native. + public static native void emptyJniStaticMethod6(int a, int b, int c, int d, int e, int f); - public native void emptyJniMethod0(); + @FastNative + public static native void emptyJniStaticMethod0_Fast(); + @FastNative + public static native void emptyJniStaticMethod6_Fast(int a, int b, int c, int d, int e, int f); - public static native void emptyJniStaticMethod6(int a, int b, int c, int d, int e, int f); + @CriticalNative + public static native void emptyJniStaticMethod0_Critical(); + @CriticalNative + public static native void emptyJniStaticMethod6_Critical(int a, int b, int c, int d, int e, int f); + // Instance methods or methods with object parameters. Test {Normal, @FastNative} combinations. + // Normal native. + public native void emptyJniMethod0(); + // Normal native. public native void emptyJniMethod6(int a, int b, int c, int d, int e, int f); /** @@ -43,20 +72,30 @@ public NativeTestTarget() { * parsing the signature. All six values should be null * references. */ + // Normal native. public static native void emptyJniStaticMethod6L(String a, String[] b, int[][] c, Object d, Object[] e, Object[][][][] f); + // Normal native. public native void emptyJniMethod6L(String a, String[] b, int[][] c, Object d, Object[] e, Object[][][][] f); - /** - * This is used to benchmark dalvik's inline natives. - */ - public static void emptyInlineMethod() { - } + @FastNative + public native void emptyJniMethod0_Fast(); + @FastNative + public native void emptyJniMethod6_Fast(int a, int b, int c, int d, int e, int f); /** - * This is used to benchmark dalvik's inline natives. + * This is an empty native static method with six args, hooked up + * using JNI. These have more complex args to show the cost of + * parsing the signature. All six values should be null + * references. */ - public static native void emptyInternalStaticMethod(); + @FastNative + public static native void emptyJniStaticMethod6L_Fast(String a, String[] b, + int[][] c, Object d, Object[] e, Object[][][][] f); + + @FastNative + public native void emptyJniMethod6L_Fast(String a, String[] b, + int[][] c, Object d, Object[] e, Object[][][][] f); } diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java index 7717fd999..5a2c06dff 100644 --- a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java +++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java @@ -16,6 +16,7 @@ package org.apache.harmony.dalvik.ddmc; +import dalvik.annotation.optimization.FastNative; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -97,6 +98,7 @@ public static void sendChunk(Chunk chunk) { } /* send a chunk to the DDM server */ + @FastNative native private static void nativeSendChunk(int type, byte[] data, int offset, int length); diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java index 01293b057..786efe7f1 100644 --- a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java +++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java @@ -16,6 +16,8 @@ package org.apache.harmony.dalvik.ddmc; +import dalvik.annotation.optimization.FastNative; + /** * Declarations for some VM-internal DDM stuff. */ @@ -40,6 +42,7 @@ private DdmVmInternal() {} * @return true on success. false if 'when' is bad or if there was * an internal error. */ + @FastNative native public static boolean heapInfoNotify(int when); /** @@ -74,11 +77,13 @@ native public static boolean heapSegmentNotify(int when, int what, * Return a boolean indicating whether or not the "recent allocation" * feature is currently enabled. */ + @FastNative native public static boolean getRecentAllocationStatus(); /** * Fill a buffer with data on recent heap allocations. */ + @FastNative native public static byte[] getRecentAllocations(); } diff --git a/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp b/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp index 52f22a80a..9a934f6f9 100644 --- a/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp +++ b/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp @@ -19,25 +19,62 @@ #include "JNIHelp.h" #include "JniConstants.h" +static void NativeTestTarget_emptyJniStaticSynchronizedMethod0(JNIEnv*, jclass) { } +static void NativeTestTarget_emptyJniSynchronizedMethod0(JNIEnv*, jclass) { } + +static JNINativeMethod gMethods_NormalOnly[] = { + NATIVE_METHOD(NativeTestTarget, emptyJniStaticSynchronizedMethod0, "()V"), + NATIVE_METHOD(NativeTestTarget, emptyJniSynchronizedMethod0, "()V"), +}; + + static void NativeTestTarget_emptyJniMethod0(JNIEnv*, jobject) { } -static void NativeTestTarget_emptyJniMethod6(JNIEnv*, jclass, int, int, int, int, int, int) { } -static void NativeTestTarget_emptyJniMethod6L(JNIEnv*, jclass, jobject, jarray, jarray, jobject, jarray, jarray) { } +static void NativeTestTarget_emptyJniMethod6(JNIEnv*, jobject, int, int, int, int, int, int) { } +static void NativeTestTarget_emptyJniMethod6L(JNIEnv*, jobject, jobject, jarray, jarray, jobject, jarray, jarray) { } +static void NativeTestTarget_emptyJniStaticMethod6L(JNIEnv*, jclass, jobject, jarray, jarray, jobject, jarray, jarray) { } + static void NativeTestTarget_emptyJniStaticMethod0(JNIEnv*, jclass) { } static void NativeTestTarget_emptyJniStaticMethod6(JNIEnv*, jclass, int, int, int, int, int, int) { } -static void NativeTestTarget_emptyJniStaticMethod6L(JNIEnv*, jclass, jobject, jarray, jarray, jobject, jarray, jarray) { } -static void NativeTestTarget_emptyJniStaticSynchronizedMethod0(JNIEnv*, jclass) { } -static void NativeTestTarget_emptyJniSynchronizedMethod0(JNIEnv*, jclass) { } static JNINativeMethod gMethods[] = { NATIVE_METHOD(NativeTestTarget, emptyJniMethod0, "()V"), NATIVE_METHOD(NativeTestTarget, emptyJniMethod6, "(IIIIII)V"), NATIVE_METHOD(NativeTestTarget, emptyJniMethod6L, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"), + NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6L, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"), NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod0, "()V"), NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6, "(IIIIII)V"), - NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6L, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"), - NATIVE_METHOD(NativeTestTarget, emptyJniStaticSynchronizedMethod0, "()V"), - NATIVE_METHOD(NativeTestTarget, emptyJniSynchronizedMethod0, "()V"), +}; + +static void NativeTestTarget_emptyJniMethod0_Fast(JNIEnv*, jobject) { } +static void NativeTestTarget_emptyJniMethod6_Fast(JNIEnv*, jobject, int, int, int, int, int, int) { } +static void NativeTestTarget_emptyJniMethod6L_Fast(JNIEnv*, jobject, jobject, jarray, jarray, jobject, jarray, jarray) { } +static void NativeTestTarget_emptyJniStaticMethod6L_Fast(JNIEnv*, jclass, jobject, jarray, jarray, jobject, jarray, jarray) { } + +static void NativeTestTarget_emptyJniStaticMethod0_Fast(JNIEnv*, jclass) { } +static void NativeTestTarget_emptyJniStaticMethod6_Fast(JNIEnv*, jclass, int, int, int, int, int, int) { } + +static JNINativeMethod gMethods_Fast[] = { + NATIVE_METHOD(NativeTestTarget, emptyJniMethod0_Fast, "()V"), + NATIVE_METHOD(NativeTestTarget, emptyJniMethod6_Fast, "(IIIIII)V"), + NATIVE_METHOD(NativeTestTarget, emptyJniMethod6L_Fast, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"), + NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6L_Fast, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"), + NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod0_Fast, "()V"), + NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6_Fast, "(IIIIII)V"), +}; + + +static void NativeTestTarget_emptyJniStaticMethod0_Critical() { } +static void NativeTestTarget_emptyJniStaticMethod6_Critical( int, int, int, int, int, int) { } + +static JNINativeMethod gMethods_Critical[] = { + NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod0_Critical, "()V"), + NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6_Critical, "(IIIIII)V"), }; int register_org_apache_harmony_dalvik_NativeTestTarget(JNIEnv* env) { - return jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/NativeTestTarget", gMethods, NELEM(gMethods)); + jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/NativeTestTarget", gMethods_NormalOnly, NELEM(gMethods_NormalOnly)); + jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/NativeTestTarget", gMethods, NELEM(gMethods)); + jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/NativeTestTarget", gMethods_Fast, NELEM(gMethods_Fast)); + jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/NativeTestTarget", gMethods_Critical, NELEM(gMethods_Critical)); + + return 0; } diff --git a/dalvik/src/test/java/dalvik/system/CloseGuardMonitor.java b/dalvik/src/test/java/dalvik/system/CloseGuardMonitor.java deleted file mode 100644 index b5bf380e2..000000000 --- a/dalvik/src/test/java/dalvik/system/CloseGuardMonitor.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2014 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. - */ -package dalvik.system; - -import dalvik.system.CloseGuard.Reporter; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.ref.WeakReference; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * Provides support for detecting issues found by {@link CloseGuard} from within tests. - * - *

This is a best effort as it relies on both {@link CloseGuard} being enabled and being able to - * force a GC and finalization, none of which are directly controllable by this. - * - *

This is loaded using reflection by the AbstractResourceLeakageDetectorTestCase class as that - * class needs to run on the reference implementation which does not have this class. It implements - * {@link Runnable} because that is simpler than trying to manage a specialized interface. - * - * @hide - */ -public class CloseGuardMonitor implements Runnable { - /** - * The {@link Reporter} instance used to receive warnings from {@link CloseGuard}. - */ - private final Reporter closeGuardReporter; - - /** - * The list of allocation sites that {@link CloseGuard} has reported as not being released. - * - *

Is thread safe as this will be called during finalization and so there are no guarantees - * as to whether it will be called concurrently or not. - */ - private final List closeGuardAllocationSites = new CopyOnWriteArrayList<>(); - - /** - * Default constructor required for reflection. - */ - public CloseGuardMonitor() { - System.logI("Creating CloseGuard monitor"); - - // Save current reporter. - closeGuardReporter = CloseGuard.getReporter(); - - // Override the reporter with our own which collates the allocation sites. - CloseGuard.setReporter(new Reporter() { - @Override - public void report(String message, Throwable allocationSite) { - // Ignore message as it's always the same. - closeGuardAllocationSites.add(allocationSite); - } - }); - } - - /** - * Check to see whether any resources monitored by {@link CloseGuard} were not released before - * they were garbage collected. - */ - @Override - public void run() { - // Create a weak reference to an object so that we can detect when it is garbage collected. - WeakReference reference = new WeakReference<>(new Object()); - - try { - // 'Force' a GC and finalize to cause CloseGuards to report warnings. Doesn't loop - // forever as there are no guarantees that the following code does anything at all so - // don't want a potential infinite loop. - Runtime runtime = Runtime.getRuntime(); - for (int i = 0; i < 20; ++i) { - runtime.gc(); - System.runFinalization(); - try { - Thread.sleep(1); - } catch (InterruptedException e) { - throw new AssertionError(e); - } - - // Check to see if the weak reference has been garbage collected. - if (reference.get() == null) { - System.logI("Sentry object has been freed so assuming CloseGuards have reported" - + " any resource leakages"); - break; - } - } - } finally { - // Restore the reporter. - CloseGuard.setReporter(closeGuardReporter); - } - - if (!closeGuardAllocationSites.isEmpty()) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - int i = 0; - for (Throwable allocationSite : closeGuardAllocationSites) { - printWriter.print(++i); - printWriter.print(") "); - allocationSite.printStackTrace(printWriter); - printWriter.println(" --------------------------------"); - } - throw new AssertionError("Potential resource leakage detected:\n" + writer); - } - } -} diff --git a/dalvik/src/test/java/dalvik/system/CloseGuardTest.java b/dalvik/src/test/java/dalvik/system/CloseGuardTest.java new file mode 100644 index 000000000..a1d1f42b6 --- /dev/null +++ b/dalvik/src/test/java/dalvik/system/CloseGuardTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2016 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. + */ +package dalvik.system; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * Tests {@link CloseGuard}. + */ +public class CloseGuardTest { + + /** + * Resets the {@link CloseGuard#ENABLED} state back to the value it had when the test started. + */ + @Rule + public TestRule rule = this::preserveEnabledState; + + private Statement preserveEnabledState(final Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + boolean oldEnabledState = CloseGuard.isEnabled(); + try { + base.evaluate(); + } finally { + CloseGuard.setEnabled(oldEnabledState); + } + } + }; + } + + @Test + public void testEnabled_NotOpen() throws Throwable { + CloseGuard.setEnabled(true); + ResourceOwner owner = new ResourceOwner(); + assertUnreleasedResources(owner, 0); + } + + @Test + public void testEnabled_OpenNotClosed() throws Throwable { + CloseGuard.setEnabled(true); + ResourceOwner owner = new ResourceOwner(); + owner.open(); + assertUnreleasedResources(owner, 1); + } + + @Test + public void testEnabled_OpenThenClosed() throws Throwable { + CloseGuard.setEnabled(true); + ResourceOwner owner = new ResourceOwner(); + owner.open(); + owner.close(); + assertUnreleasedResources(owner, 0); + } + + @Test + public void testEnabledWhenCreated_DisabledWhenOpen() throws Throwable { + CloseGuard.setEnabled(true); + ResourceOwner owner = new ResourceOwner(); + CloseGuard.setEnabled(false); + owner.open(); + + // Although the resource was not released it should not report it because CloseGuard was + // not enabled when the CloseGuard was opened. + assertUnreleasedResources(owner, 0); + } + + @Test + public void testEnabledWhenOpened_DisabledWhenFinalized() throws Throwable { + CloseGuard.setEnabled(true); + ResourceOwner owner = new ResourceOwner(); + owner.open(); + CloseGuard.setEnabled(false); + + // Although the resource was not released it should not report it because CloseGuard was + // not enabled when the CloseGuard was finalized. + assertUnreleasedResources(owner, 0); + } + + @Test + public void testDisabled_NotOpen() throws Throwable { + CloseGuard.setEnabled(false); + ResourceOwner owner = new ResourceOwner(); + assertUnreleasedResources(owner, 0); + } + + @Test + public void testDisabled_OpenNotClosed() throws Throwable { + CloseGuard.setEnabled(false); + ResourceOwner owner = new ResourceOwner(); + owner.open(); + assertUnreleasedResources(owner, 0); + } + + @Test + public void testDisabled_OpenThenClosed() throws Throwable { + CloseGuard.setEnabled(false); + ResourceOwner owner = new ResourceOwner(); + owner.open(); + owner.close(); + assertUnreleasedResources(owner, 0); + } + + @Test + public void testDisabledWhenCreated_EnabledWhenOpen() throws Throwable { + CloseGuard.setEnabled(false); + ResourceOwner owner = new ResourceOwner(); + CloseGuard.setEnabled(true); + owner.open(); + + // Although the resource was not released it should not report it because CloseGuard was + // not enabled when the CloseGuard was created. + assertUnreleasedResources(owner, 0); + } + + private void assertUnreleasedResources(ResourceOwner owner, int expectedCount) + throws Throwable { + try { + CloseGuardSupport.getFinalizerChecker().accept(owner, expectedCount); + } finally { + // Close the resource so that CloseGuard does not generate a warning for real when it + // is actually finalized. + owner.close(); + } + } + + /** + * A test user of {@link CloseGuard}. + */ + private static class ResourceOwner { + + private final CloseGuard closeGuard; + + ResourceOwner() { + closeGuard = CloseGuard.get(); + } + + public void open() { + closeGuard.open("close"); + } + + public void close() { + closeGuard.close(); + } + + /** + * Make finalize public so that it can be tested directly without relying on garbage + * collection to trigger it. + */ + @Override + public void finalize() throws Throwable { + closeGuard.warnIfOpen(); + super.finalize(); + } + } +} diff --git a/dalvik/test-rules/src/main/java/dalvik/system/CloseGuardSupport.java b/dalvik/test-rules/src/main/java/dalvik/system/CloseGuardSupport.java new file mode 100644 index 000000000..7871795b6 --- /dev/null +++ b/dalvik/test-rules/src/main/java/dalvik/system/CloseGuardSupport.java @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2016 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. + */ +package dalvik.system; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * Provides support for testing classes that use {@link CloseGuard} in order to detect resource + * leakages. + * + *

This class should not be used directly by tests as that will prevent them from being + * compilable and testable on OpenJDK platform. Instead they should use + * {@code libcore.junit.util.ResourceLeakageDetector} which accesses the capabilities of this using + * reflection and if it cannot find it (because it is running on OpenJDK) then it will just skip + * leakage detection. + * + *

This provides two entry points that are accessed reflectively: + *

    + *
  • + *

    The {@link #getRule()} method. This returns a {@link TestRule} that will fail a test if it + * detects any resources that were allocated during the test but were not released. + * + *

    This only tracks resources that were allocated on the test thread, although it does not care + * what thread they were released on. This avoids flaky false positives where a background thread + * allocates a resource during a test but releases it after the test. + * + *

    It is still possible to have a false positive in the case where the test causes a caching + * mechanism to open a resource and hold it open past the end of the test. In that case if there is + * no way to clear the cached data then it should be relatively simple to move the code that invokes + * the caching mechanism to outside the scope of this rule. i.e. + * + *

    {@code
    + *     @Rule
    + *     public final TestRule ruleChain = org.junit.rules.RuleChain
    + *         .outerRule(new ...invoke caching mechanism...)
    + *         .around(CloseGuardSupport.getRule());
    + * }
    + *
  • + *
  • + *

    The {@link #getFinalizerChecker()} method. This returns a {@link BiConsumer} that takes an + * object that owns resources and an expected number of unreleased resources. It will call the + * {@link Object#finalize()} method on the object using reflection and throw an + * {@link AssertionError} if the number of reported unreleased resources does not match the + * expected number. + *

  • + *
+ */ +public class CloseGuardSupport { + + private static final TestRule CLOSE_GUARD_RULE = new FailTestWhenResourcesNotClosedRule(); + + /** + * Get a {@link TestRule} that will detect when resources that use the {@link CloseGuard} + * mechanism are not cleaned up properly by a test. + * + *

If the {@link CloseGuard} mechanism is not supported, e.g. on OpenJDK, then the returned + * rule does nothing. + */ + public static TestRule getRule() { + return CLOSE_GUARD_RULE; + } + + private CloseGuardSupport() { + } + + /** + * Fails a test when resources are not cleaned up properly. + */ + private static class FailTestWhenResourcesNotClosedRule implements TestRule { + /** + * Returns a {@link Statement} that will fail the test if it ends with unreleased resources. + * @param base the test to be run. + */ + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + // Get the previous tracker so that it can be restored afterwards. + CloseGuard.Tracker previousTracker = CloseGuard.getTracker(); + // Get the previous enabled state so that it can be restored afterwards. + boolean previousEnabled = CloseGuard.isEnabled(); + TestCloseGuardTracker tracker = new TestCloseGuardTracker(); + Throwable thrown = null; + try { + // Set the test tracker and enable close guard detection. + CloseGuard.setTracker(tracker); + CloseGuard.setEnabled(true); + base.evaluate(); + } catch (Throwable throwable) { + // Catch and remember the throwable so that it can be rethrown in the + // finally block. + thrown = throwable; + } finally { + // Restore the previous tracker and enabled state. + CloseGuard.setEnabled(previousEnabled); + CloseGuard.setTracker(previousTracker); + + Collection allocationSites = + tracker.getAllocationSitesForUnreleasedResources(); + if (!allocationSites.isEmpty()) { + if (thrown == null) { + thrown = new IllegalStateException( + "Unreleased resources found in test"); + } + for (Throwable allocationSite : allocationSites) { + thrown.addSuppressed(allocationSite); + } + } + if (thrown != null) { + throw thrown; + } + } + } + }; + } + } + + /** + * A tracker that keeps a record of the allocation sites for all resources allocated but not + * yet released. + * + *

It only tracks resources allocated for the test thread. + */ + private static class TestCloseGuardTracker implements CloseGuard.Tracker { + + /** + * A set would be preferable but this is the closest that matches the concurrency + * requirements for the use case which prioritise speed of addition and removal over + * iteration and access. + */ + private final Set allocationSites = + Collections.newSetFromMap(new ConcurrentHashMap<>()); + + private final Thread testThread = Thread.currentThread(); + + @Override + public void open(Throwable allocationSite) { + if (Thread.currentThread() == testThread) { + allocationSites.add(allocationSite); + } + } + + @Override + public void close(Throwable allocationSite) { + // Closing the resource twice could pass null into here. + if (allocationSite != null) { + allocationSites.remove(allocationSite); + } + } + + /** + * Get the collection of allocation sites for any unreleased resources. + */ + Collection getAllocationSitesForUnreleasedResources() { + return new ArrayList<>(allocationSites); + } + } + + private static final BiConsumer FINALIZER_CHECKER + = new BiConsumer() { + @Override + public void accept(Object resourceOwner, Integer expectedCount) { + finalizerChecker(resourceOwner, expectedCount); + } + }; + + /** + * Get access to a {@link BiConsumer} that will determine how many unreleased resources the + * first parameter owns and throw a {@link AssertionError} if that does not match the + * expected number of resources specified by the second parameter. + * + *

This uses a {@link BiConsumer} as it is a standard interface that is available in all + * environments. That helps avoid the caller from having compile time dependencies on this + * class which will not be available on OpenJDK. + */ + public static BiConsumer getFinalizerChecker() { + return FINALIZER_CHECKER; + } + + /** + * Checks that the supplied {@code resourceOwner} has overridden the {@link Object#finalize()} + * method and uses {@link CloseGuard#warnIfOpen()} correctly to detect when the resource is + * not released. + * + * @param resourceOwner the owner of the resource protected by {@link CloseGuard}. + * @param expectedCount the expected number of unreleased resources to be held by the owner. + * + */ + private static void finalizerChecker(Object resourceOwner, int expectedCount) { + Class clazz = resourceOwner.getClass(); + Method finalizer = null; + while (clazz != null && clazz != Object.class) { + try { + finalizer = clazz.getDeclaredMethod("finalize"); + break; + } catch (NoSuchMethodException e) { + // Carry on up the class hierarchy. + clazz = clazz.getSuperclass(); + } + } + + if (finalizer == null) { + // No finalizer method could be found. + throw new AssertionError("Class " + resourceOwner.getClass().getName() + + " does not have a finalize() method"); + } + + // Make the method accessible. + finalizer.setAccessible(true); + + CloseGuard.Reporter oldReporter = CloseGuard.getReporter(); + try { + CollectingReporter reporter = new CollectingReporter(); + CloseGuard.setReporter(reporter); + + // Invoke the finalizer to cause it to get CloseGuard to report a problem if it has + // not yet been closed. + try { + finalizer.invoke(resourceOwner); + } catch (ReflectiveOperationException e) { + throw new AssertionError( + "Could not invoke the finalizer() method on " + resourceOwner, e); + } + + reporter.assertUnreleasedResources(expectedCount); + } finally { + CloseGuard.setReporter(oldReporter); + } + } + + /** + * A {@link CloseGuard.Reporter} that collects any reports about unreleased resources. + */ + private static class CollectingReporter implements CloseGuard.Reporter { + + private final Thread callingThread = Thread.currentThread(); + + private final List unreleasedResourceAllocationSites = new ArrayList<>(); + + @Override + public void report(String message, Throwable allocationSite) { + // Only care about resources that are not reported on this thread. + if (callingThread == Thread.currentThread()) { + unreleasedResourceAllocationSites.add(allocationSite); + } + } + + void assertUnreleasedResources(int expectedCount) { + int unreleasedResourceCount = unreleasedResourceAllocationSites.size(); + if (unreleasedResourceCount == expectedCount) { + return; + } + + AssertionError error = new AssertionError( + "Expected " + expectedCount + " unreleased resources, found " + + unreleasedResourceCount + "; see suppressed exceptions for details"); + for (Throwable unreleasedResourceAllocationSite : unreleasedResourceAllocationSites) { + error.addSuppressed(unreleasedResourceAllocationSite); + } + throw error; + } + } +} diff --git a/dalvik/test-rules/src/test/java/dalvik/system/CloseGuardSupportTest.java b/dalvik/test-rules/src/test/java/dalvik/system/CloseGuardSupportTest.java new file mode 100644 index 000000000..fe05710b2 --- /dev/null +++ b/dalvik/test-rules/src/test/java/dalvik/system/CloseGuardSupportTest.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2016 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. + */ +package dalvik.system; + +import java.util.Collections; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.JUnitCore; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; +import org.junit.runners.JUnit4; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@RunWith(JUnit4.class) +public class CloseGuardSupportTest { + + @Test + public void testDoesReleaseResource() { + List failures = JUnitCore.runClasses(DoesReleaseResource.class).getFailures(); + assertEquals(Collections.emptyList(), failures); + } + + public static class DoesReleaseResource { + @Rule public TestRule rule = CloseGuardSupport.getRule(); + @Test public void test() { + CloseGuard closeGuard = CloseGuard.get(); + closeGuard.open("test resource"); + closeGuard.close(); + } + } + + @Test + public void testDoesReleaseResourceTwice() { + List failures = JUnitCore.runClasses(DoesReleaseResourceTwice.class).getFailures(); + assertEquals(Collections.emptyList(), failures); + } + + public static class DoesReleaseResourceTwice { + @Rule public TestRule rule = CloseGuardSupport.getRule(); + @Test public void test() { + CloseGuard closeGuard = CloseGuard.get(); + closeGuard.open("test resource"); + closeGuard.close(); + closeGuard.close(); + } + } + + @Test + public void testDoesNotReleaseResource() { + List failures = JUnitCore.runClasses(DoesNotReleaseResource.class).getFailures(); + assertEquals("Failure count", 1, failures.size()); + Failure failure = failures.get(0); + checkResourceNotReleased(failure, "Unreleased resources found in test"); + } + + public static class DoesNotReleaseResource { + @Rule public TestRule rule = CloseGuardSupport.getRule(); + @Test public void test() { + CloseGuard closeGuard = CloseGuard.get(); + closeGuard.open("test resource"); + } + } + + @Test + public void testDoesNotReleaseResourceDueToFailure() { + List failures = JUnitCore + .runClasses(DoesNotReleaseResourceDueToFailure.class) + .getFailures(); + assertEquals("Failure count", 1, failures.size()); + Failure failure = failures.get(0); + checkResourceNotReleased(failure, "failure"); + } + + public static class DoesNotReleaseResourceDueToFailure { + @Rule public TestRule rule = CloseGuardSupport.getRule(); + @Test public void test() { + CloseGuard closeGuard = CloseGuard.get(); + closeGuard.open("test resource"); + fail("failure"); + } + } + + @Test + public void testResourceOwnerDoesNotOverrideFinalize() { + List failures = JUnitCore + .runClasses(ResourceOwnerDoesNotOverrideFinalize.class) + .getFailures(); + assertEquals("Failure count", 1, failures.size()); + Failure failure = failures.get(0); + assertEquals("Class java.lang.String does not have a finalize() method", + failure.getMessage()); + } + + public static class ResourceOwnerDoesNotOverrideFinalize { + @Rule public TestRule rule = CloseGuardSupport.getRule(); + @Test + public void test() { + CloseGuardSupport.getFinalizerChecker().accept("not resource owner", 0); + } + } + + @Test + public void testResourceOwnerOverridesFinalizeButDoesNotReportLeak() { + List failures = JUnitCore + .runClasses(ResourceOwnerOverridesFinalizeButDoesNotReportLeak.class) + .getFailures(); + assertEquals("Failure count", 1, failures.size()); + Failure failure = failures.get(0); + assertEquals("Expected 1 unreleased resources, found 0;" + + " see suppressed exceptions for details", + failure.getMessage()); + } + + public static class ResourceOwnerOverridesFinalizeButDoesNotReportLeak { + @Rule public TestRule rule = CloseGuardSupport.getRule(); + @Test + public void test() { + CloseGuardSupport.getFinalizerChecker().accept(new Object() { + @Override + protected void finalize() throws Throwable { + super.finalize(); + } + }, 1); + } + } + + @Test + public void testResourceOwnerOverridesFinalizeAndReportsLeak() { + List failures = JUnitCore + .runClasses(ResourceOwnerOverridesFinalizeAndReportsLeak.class) + .getFailures(); + assertEquals("Failure count", 1, failures.size()); + Failure failure = failures.get(0); + checkResourceNotReleased(failure, "Unreleased resources found in test"); + } + + public static class ResourceOwnerOverridesFinalizeAndReportsLeak { + @Rule public TestRule rule = CloseGuardSupport.getRule(); + @Test + public void test() { + CloseGuardSupport.getFinalizerChecker().accept(new Object() { + private CloseGuard guard = CloseGuard.get(); + { + guard.open("test resource"); + } + @Override + protected void finalize() throws Throwable { + guard.warnIfOpen(); + super.finalize(); + } + }, 1); + } + } + + private void checkResourceNotReleased(Failure failure, String expectedMessage) { + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + Throwable exception = failure.getException(); + assertEquals(expectedMessage, exception.getMessage()); + Throwable[] suppressed = exception.getSuppressed(); + assertEquals("Suppressed count", 1, suppressed.length); + exception = suppressed[0]; + assertEquals("Explicit termination method 'test resource' not called", + exception.getMessage()); + } +} diff --git a/dex/src/main/java/com/android/dex/Annotation.java b/dex/src/main/java/com/android/dex/Annotation.java deleted file mode 100644 index e5ef9783b..000000000 --- a/dex/src/main/java/com/android/dex/Annotation.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import static com.android.dex.EncodedValueReader.ENCODED_ANNOTATION; - -/** - * An annotation. - */ -public final class Annotation implements Comparable { - private final Dex dex; - private final byte visibility; - private final EncodedValue encodedAnnotation; - - public Annotation(Dex dex, byte visibility, EncodedValue encodedAnnotation) { - this.dex = dex; - this.visibility = visibility; - this.encodedAnnotation = encodedAnnotation; - } - - public byte getVisibility() { - return visibility; - } - - public EncodedValueReader getReader() { - return new EncodedValueReader(encodedAnnotation, ENCODED_ANNOTATION); - } - - public int getTypeIndex() { - EncodedValueReader reader = getReader(); - reader.readAnnotation(); - return reader.getAnnotationType(); - } - - public void writeTo(Dex.Section out) { - out.writeByte(visibility); - encodedAnnotation.writeTo(out); - } - - @Override public int compareTo(Annotation other) { - return encodedAnnotation.compareTo(other.encodedAnnotation); - } - - @Override public String toString() { - return dex == null - ? visibility + " " + getTypeIndex() - : visibility + " " + dex.typeNames().get(getTypeIndex()); - } -} diff --git a/dex/src/main/java/com/android/dex/ClassData.java b/dex/src/main/java/com/android/dex/ClassData.java deleted file mode 100644 index 840756c5b..000000000 --- a/dex/src/main/java/com/android/dex/ClassData.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -public final class ClassData { - private final Field[] staticFields; - private final Field[] instanceFields; - private final Method[] directMethods; - private final Method[] virtualMethods; - - public ClassData(Field[] staticFields, Field[] instanceFields, - Method[] directMethods, Method[] virtualMethods) { - this.staticFields = staticFields; - this.instanceFields = instanceFields; - this.directMethods = directMethods; - this.virtualMethods = virtualMethods; - } - - public Field[] getStaticFields() { - return staticFields; - } - - public Field[] getInstanceFields() { - return instanceFields; - } - - public Method[] getDirectMethods() { - return directMethods; - } - - public Method[] getVirtualMethods() { - return virtualMethods; - } - - public Field[] allFields() { - Field[] result = new Field[staticFields.length + instanceFields.length]; - System.arraycopy(staticFields, 0, result, 0, staticFields.length); - System.arraycopy(instanceFields, 0, result, staticFields.length, instanceFields.length); - return result; - } - - public Method[] allMethods() { - Method[] result = new Method[directMethods.length + virtualMethods.length]; - System.arraycopy(directMethods, 0, result, 0, directMethods.length); - System.arraycopy(virtualMethods, 0, result, directMethods.length, virtualMethods.length); - return result; - } - - public static class Field { - private final int fieldIndex; - private final int accessFlags; - - public Field(int fieldIndex, int accessFlags) { - this.fieldIndex = fieldIndex; - this.accessFlags = accessFlags; - } - - public int getFieldIndex() { - return fieldIndex; - } - - public int getAccessFlags() { - return accessFlags; - } - } - - public static class Method { - private final int methodIndex; - private final int accessFlags; - private final int codeOffset; - - public Method(int methodIndex, int accessFlags, int codeOffset) { - this.methodIndex = methodIndex; - this.accessFlags = accessFlags; - this.codeOffset = codeOffset; - } - - public int getMethodIndex() { - return methodIndex; - } - - public int getAccessFlags() { - return accessFlags; - } - - public int getCodeOffset() { - return codeOffset; - } - } -} diff --git a/dex/src/main/java/com/android/dex/ClassDef.java b/dex/src/main/java/com/android/dex/ClassDef.java deleted file mode 100644 index b3225ec0e..000000000 --- a/dex/src/main/java/com/android/dex/ClassDef.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -/** - * A type definition. - */ -public final class ClassDef { - public static final int NO_INDEX = -1; - private final Dex buffer; - private final int offset; - private final int typeIndex; - private final int accessFlags; - private final int supertypeIndex; - private final int interfacesOffset; - private final int sourceFileIndex; - private final int annotationsOffset; - private final int classDataOffset; - private final int staticValuesOffset; - - public ClassDef(Dex buffer, int offset, int typeIndex, int accessFlags, - int supertypeIndex, int interfacesOffset, int sourceFileIndex, - int annotationsOffset, int classDataOffset, int staticValuesOffset) { - this.buffer = buffer; - this.offset = offset; - this.typeIndex = typeIndex; - this.accessFlags = accessFlags; - this.supertypeIndex = supertypeIndex; - this.interfacesOffset = interfacesOffset; - this.sourceFileIndex = sourceFileIndex; - this.annotationsOffset = annotationsOffset; - this.classDataOffset = classDataOffset; - this.staticValuesOffset = staticValuesOffset; - } - - public int getOffset() { - return offset; - } - - public int getTypeIndex() { - return typeIndex; - } - - public int getSupertypeIndex() { - return supertypeIndex; - } - - public int getInterfacesOffset() { - return interfacesOffset; - } - - public short[] getInterfaces() { - return buffer.readTypeList(interfacesOffset).getTypes(); - } - - public int getAccessFlags() { - return accessFlags; - } - - public int getSourceFileIndex() { - return sourceFileIndex; - } - - public int getAnnotationsOffset() { - return annotationsOffset; - } - - public int getClassDataOffset() { - return classDataOffset; - } - - public int getStaticValuesOffset() { - return staticValuesOffset; - } - - @Override public String toString() { - if (buffer == null) { - return typeIndex + " " + supertypeIndex; - } - - StringBuilder result = new StringBuilder(); - result.append(buffer.typeNames().get(typeIndex)); - if (supertypeIndex != NO_INDEX) { - result.append(" extends ").append(buffer.typeNames().get(supertypeIndex)); - } - return result.toString(); - } -} diff --git a/dex/src/main/java/com/android/dex/Code.java b/dex/src/main/java/com/android/dex/Code.java deleted file mode 100644 index 9258af795..000000000 --- a/dex/src/main/java/com/android/dex/Code.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -public final class Code { - private final int registersSize; - private final int insSize; - private final int outsSize; - private final int debugInfoOffset; - private final short[] instructions; - private final Try[] tries; - private final CatchHandler[] catchHandlers; - - public Code(int registersSize, int insSize, int outsSize, int debugInfoOffset, - short[] instructions, Try[] tries, CatchHandler[] catchHandlers) { - this.registersSize = registersSize; - this.insSize = insSize; - this.outsSize = outsSize; - this.debugInfoOffset = debugInfoOffset; - this.instructions = instructions; - this.tries = tries; - this.catchHandlers = catchHandlers; - } - - public int getRegistersSize() { - return registersSize; - } - - public int getInsSize() { - return insSize; - } - - public int getOutsSize() { - return outsSize; - } - - public int getDebugInfoOffset() { - return debugInfoOffset; - } - - public short[] getInstructions() { - return instructions; - } - - public Try[] getTries() { - return tries; - } - - public CatchHandler[] getCatchHandlers() { - return catchHandlers; - } - - public static class Try { - final int startAddress; - final int instructionCount; - final int catchHandlerIndex; - - Try(int startAddress, int instructionCount, int catchHandlerIndex) { - this.startAddress = startAddress; - this.instructionCount = instructionCount; - this.catchHandlerIndex = catchHandlerIndex; - } - - public int getStartAddress() { - return startAddress; - } - - public int getInstructionCount() { - return instructionCount; - } - - /** - * Returns this try's catch handler index. Note that - * this is distinct from the its catch handler offset. - */ - public int getCatchHandlerIndex() { - return catchHandlerIndex; - } - } - - public static class CatchHandler { - final int[] typeIndexes; - final int[] addresses; - final int catchAllAddress; - final int offset; - - public CatchHandler(int[] typeIndexes, int[] addresses, int catchAllAddress, int offset) { - this.typeIndexes = typeIndexes; - this.addresses = addresses; - this.catchAllAddress = catchAllAddress; - this.offset = offset; - } - - public int[] getTypeIndexes() { - return typeIndexes; - } - - public int[] getAddresses() { - return addresses; - } - - public int getCatchAllAddress() { - return catchAllAddress; - } - - public int getOffset() { - return offset; - } - } -} diff --git a/dex/src/main/java/com/android/dex/Dex.java b/dex/src/main/java/com/android/dex/Dex.java deleted file mode 100644 index ea9b627b2..000000000 --- a/dex/src/main/java/com/android/dex/Dex.java +++ /dev/null @@ -1,983 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.Code.CatchHandler; -import com.android.dex.Code.Try; -import com.android.dex.util.ByteInput; -import com.android.dex.util.ByteOutput; -import com.android.dex.util.FileUtils; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UTFDataFormatException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.AbstractList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.RandomAccess; -import java.util.zip.Adler32; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * The bytes of a dex file in memory for reading and writing. All int offsets - * are unsigned. - */ -public final class Dex { - private static final int CHECKSUM_OFFSET = 8; - private static final int CHECKSUM_SIZE = 4; - private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE; - private static final int SIGNATURE_SIZE = 20; - // Provided as a convenience to avoid a memory allocation to benefit Dalvik. - // Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik. - static final short[] EMPTY_SHORT_ARRAY = new short[0]; - - private ByteBuffer data; - private final TableOfContents tableOfContents = new TableOfContents(); - private int nextSectionStart = 0; - private final StringTable strings = new StringTable(); - private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable(); - private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable(); - private final ProtoIdTable protoIds = new ProtoIdTable(); - private final FieldIdTable fieldIds = new FieldIdTable(); - private final MethodIdTable methodIds = new MethodIdTable(); - - /** - * Creates a new dex that reads from {@code data}. It is an error to modify - * {@code data} after using it to create a dex buffer. - */ - public Dex(byte[] data) throws IOException { - this(ByteBuffer.wrap(data)); - } - - private Dex(ByteBuffer data) throws IOException { - this.data = data; - this.data.order(ByteOrder.LITTLE_ENDIAN); - this.tableOfContents.readFrom(this); - } - - /** - * Creates a new empty dex of the specified size. - */ - public Dex(int byteCount) throws IOException { - this.data = ByteBuffer.wrap(new byte[byteCount]); - this.data.order(ByteOrder.LITTLE_ENDIAN); - } - - /** - * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}. - */ - public Dex(InputStream in) throws IOException { - loadFrom(in); - } - - /** - * Creates a new dex buffer from the dex file {@code file}. - */ - public Dex(File file) throws IOException { - if (FileUtils.hasArchiveSuffix(file.getName())) { - ZipFile zipFile = new ZipFile(file); - ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME); - if (entry != null) { - loadFrom(zipFile.getInputStream(entry)); - zipFile.close(); - } else { - throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file); - } - } else if (file.getName().endsWith(".dex")) { - loadFrom(new FileInputStream(file)); - } else { - throw new DexException("unknown output extension: " + file); - } - } - - /** - * Creates a new dex from the contents of {@code bytes}. This API supports - * both {@code .dex} and {@code .odex} input. Calling this constructor - * transfers ownership of {@code bytes} to the returned Dex: it is an error - * to access the buffer after calling this method. - */ - public static Dex create(ByteBuffer data) throws IOException { - data.order(ByteOrder.LITTLE_ENDIAN); - - // if it's an .odex file, set position and limit to the .dex section - if (data.get(0) == 'd' - && data.get(1) == 'e' - && data.get(2) == 'y' - && data.get(3) == '\n') { - data.position(8); - int offset = data.getInt(); - int length = data.getInt(); - data.position(offset); - data.limit(offset + length); - data = data.slice(); - } - - return new Dex(data); - } - - private void loadFrom(InputStream in) throws IOException { - ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - byte[] buffer = new byte[8192]; - - int count; - while ((count = in.read(buffer)) != -1) { - bytesOut.write(buffer, 0, count); - } - in.close(); - - this.data = ByteBuffer.wrap(bytesOut.toByteArray()); - this.data.order(ByteOrder.LITTLE_ENDIAN); - this.tableOfContents.readFrom(this); - } - - private static void checkBounds(int index, int length) { - if (index < 0 || index >= length) { - throw new IndexOutOfBoundsException("index:" + index + ", length=" + length); - } - } - - public void writeTo(OutputStream out) throws IOException { - byte[] buffer = new byte[8192]; - ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe - data.clear(); - while (data.hasRemaining()) { - int count = Math.min(buffer.length, data.remaining()); - data.get(buffer, 0, count); - out.write(buffer, 0, count); - } - } - - public void writeTo(File dexOut) throws IOException { - OutputStream out = new FileOutputStream(dexOut); - writeTo(out); - out.close(); - } - - public TableOfContents getTableOfContents() { - return tableOfContents; - } - - public Section open(int position) { - if (position < 0 || position >= data.capacity()) { - throw new IllegalArgumentException("position=" + position - + " length=" + data.capacity()); - } - ByteBuffer sectionData = data.duplicate(); - sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary? - sectionData.position(position); - sectionData.limit(data.capacity()); - return new Section("section", sectionData); - } - - public Section appendSection(int maxByteCount, String name) { - if ((maxByteCount & 3) != 0) { - throw new IllegalStateException("Not four byte aligned!"); - } - int limit = nextSectionStart + maxByteCount; - ByteBuffer sectionData = data.duplicate(); - sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary? - sectionData.position(nextSectionStart); - sectionData.limit(limit); - Section result = new Section(name, sectionData); - nextSectionStart = limit; - return result; - } - - public int getLength() { - return data.capacity(); - } - - public int getNextSectionStart() { - return nextSectionStart; - } - - /** - * Returns a copy of the the bytes of this dex. - */ - public byte[] getBytes() { - ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe - byte[] result = new byte[data.capacity()]; - data.position(0); - data.get(result); - return result; - } - - public List strings() { - return strings; - } - - public List typeIds() { - return typeIds; - } - - public List typeNames() { - return typeNames; - } - - public List protoIds() { - return protoIds; - } - - public List fieldIds() { - return fieldIds; - } - - public List methodIds() { - return methodIds; - } - - public Iterable classDefs() { - return new ClassDefIterable(); - } - - public TypeList readTypeList(int offset) { - if (offset == 0) { - return TypeList.EMPTY; - } - return open(offset).readTypeList(); - } - - public ClassData readClassData(ClassDef classDef) { - int offset = classDef.getClassDataOffset(); - if (offset == 0) { - throw new IllegalArgumentException("offset == 0"); - } - return open(offset).readClassData(); - } - - public Code readCode(ClassData.Method method) { - int offset = method.getCodeOffset(); - if (offset == 0) { - throw new IllegalArgumentException("offset == 0"); - } - return open(offset).readCode(); - } - - /** - * Returns the signature of all but the first 32 bytes of this dex. The - * first 32 bytes of dex files are not specified to be included in the - * signature. - */ - public byte[] computeSignature() throws IOException { - MessageDigest digest; - try { - digest = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(); - } - byte[] buffer = new byte[8192]; - ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe - data.limit(data.capacity()); - data.position(SIGNATURE_OFFSET + SIGNATURE_SIZE); - while (data.hasRemaining()) { - int count = Math.min(buffer.length, data.remaining()); - data.get(buffer, 0, count); - digest.update(buffer, 0, count); - } - return digest.digest(); - } - - /** - * Returns the checksum of all but the first 12 bytes of {@code dex}. - */ - public int computeChecksum() throws IOException { - Adler32 adler32 = new Adler32(); - byte[] buffer = new byte[8192]; - ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe - data.limit(data.capacity()); - data.position(CHECKSUM_OFFSET + CHECKSUM_SIZE); - while (data.hasRemaining()) { - int count = Math.min(buffer.length, data.remaining()); - data.get(buffer, 0, count); - adler32.update(buffer, 0, count); - } - return (int) adler32.getValue(); - } - - /** - * Generates the signature and checksum of the dex file {@code out} and - * writes them to the file. - */ - public void writeHashes() throws IOException { - open(SIGNATURE_OFFSET).write(computeSignature()); - open(CHECKSUM_OFFSET).writeInt(computeChecksum()); - } - - /** - * Look up a field id name index from a field index. Cheaper than: - * {@code fieldIds().get(fieldDexIndex).getNameIndex();} - */ - public int nameIndexFromFieldIndex(int fieldIndex) { - checkBounds(fieldIndex, tableOfContents.fieldIds.size); - int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex); - position += SizeOf.USHORT; // declaringClassIndex - position += SizeOf.USHORT; // typeIndex - return data.getInt(position); // nameIndex - } - - public int findStringIndex(String s) { - return Collections.binarySearch(strings, s); - } - - public int findTypeIndex(String descriptor) { - return Collections.binarySearch(typeNames, descriptor); - } - - public int findFieldIndex(FieldId fieldId) { - return Collections.binarySearch(fieldIds, fieldId); - } - - public int findMethodIndex(MethodId methodId) { - return Collections.binarySearch(methodIds, methodId); - } - - public int findClassDefIndexFromTypeIndex(int typeIndex) { - checkBounds(typeIndex, tableOfContents.typeIds.size); - if (!tableOfContents.classDefs.exists()) { - return -1; - } - for (int i = 0; i < tableOfContents.classDefs.size; i++) { - if (typeIndexFromClassDefIndex(i) == typeIndex) { - return i; - } - } - return -1; - } - - /** - * Look up a field id type index from a field index. Cheaper than: - * {@code fieldIds().get(fieldDexIndex).getTypeIndex();} - */ - public int typeIndexFromFieldIndex(int fieldIndex) { - checkBounds(fieldIndex, tableOfContents.fieldIds.size); - int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex); - position += SizeOf.USHORT; // declaringClassIndex - return data.getShort(position) & 0xFFFF; // typeIndex - } - - /** - * Look up a method id declaring class index from a method index. Cheaper than: - * {@code methodIds().get(methodIndex).getDeclaringClassIndex();} - */ - public int declaringClassIndexFromMethodIndex(int methodIndex) { - checkBounds(methodIndex, tableOfContents.methodIds.size); - int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex); - return data.getShort(position) & 0xFFFF; // declaringClassIndex - } - - /** - * Look up a method id name index from a method index. Cheaper than: - * {@code methodIds().get(methodIndex).getNameIndex();} - */ - public int nameIndexFromMethodIndex(int methodIndex) { - checkBounds(methodIndex, tableOfContents.methodIds.size); - int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex); - position += SizeOf.USHORT; // declaringClassIndex - position += SizeOf.USHORT; // protoIndex - return data.getInt(position); // nameIndex - } - - /** - * Look up a parameter type ids from a method index. Cheaper than: - * {@code readTypeList(protoIds.get(methodIds().get(methodDexIndex).getProtoIndex()).getParametersOffset()).getTypes();} - */ - public short[] parameterTypeIndicesFromMethodIndex(int methodIndex) { - checkBounds(methodIndex, tableOfContents.methodIds.size); - int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex); - position += SizeOf.USHORT; // declaringClassIndex - int protoIndex = data.getShort(position) & 0xFFFF; - checkBounds(protoIndex, tableOfContents.protoIds.size); - position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex); - position += SizeOf.UINT; // shortyIndex - position += SizeOf.UINT; // returnTypeIndex - int parametersOffset = data.getInt(position); - if (parametersOffset == 0) { - return EMPTY_SHORT_ARRAY; - } - position = parametersOffset; - int size = data.getInt(position); - if (size <= 0) { - throw new AssertionError("Unexpected parameter type list size: " + size); - } - position += SizeOf.UINT; - short[] types = new short[size]; - for (int i = 0; i < size; i++) { - types[i] = data.getShort(position); - position += SizeOf.USHORT; - } - return types; - } - - /** - * Look up a method id return type index from a method index. Cheaper than: - * {@code protoIds().get(methodIds().get(methodDexIndex).getProtoIndex()).getReturnTypeIndex();} - */ - public int returnTypeIndexFromMethodIndex(int methodIndex) { - checkBounds(methodIndex, tableOfContents.methodIds.size); - int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex); - position += SizeOf.USHORT; // declaringClassIndex - int protoIndex = data.getShort(position) & 0xFFFF; - checkBounds(protoIndex, tableOfContents.protoIds.size); - position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex); - position += SizeOf.UINT; // shortyIndex - return data.getInt(position); // returnTypeIndex - } - - /** - * Look up a descriptor index from a type index. Cheaper than: - * {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();} - */ - public int descriptorIndexFromTypeIndex(int typeIndex) { - checkBounds(typeIndex, tableOfContents.typeIds.size); - int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex); - return data.getInt(position); - } - - /** - * Look up a type index index from a class def index. - */ - public int typeIndexFromClassDefIndex(int classDefIndex) { - checkBounds(classDefIndex, tableOfContents.classDefs.size); - int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex); - return data.getInt(position); - } - - /** - * Look up an annotation directory offset from a class def index. - */ - public int annotationDirectoryOffsetFromClassDefIndex(int classDefIndex) { - checkBounds(classDefIndex, tableOfContents.classDefs.size); - int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex); - position += SizeOf.UINT; // type - position += SizeOf.UINT; // accessFlags - position += SizeOf.UINT; // superType - position += SizeOf.UINT; // interfacesOffset - position += SizeOf.UINT; // sourceFileIndex - return data.getInt(position); - } - - /** - * Look up interface types indices from a return type index from a method index. Cheaper than: - * {@code ...getClassDef(classDefIndex).getInterfaces();} - */ - public short[] interfaceTypeIndicesFromClassDefIndex(int classDefIndex) { - checkBounds(classDefIndex, tableOfContents.classDefs.size); - int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex); - position += SizeOf.UINT; // type - position += SizeOf.UINT; // accessFlags - position += SizeOf.UINT; // superType - int interfacesOffset = data.getInt(position); - if (interfacesOffset == 0) { - return EMPTY_SHORT_ARRAY; - } - position = interfacesOffset; - int size = data.getInt(position); - if (size <= 0) { - throw new AssertionError("Unexpected interfaces list size: " + size); - } - position += SizeOf.UINT; - short[] types = new short[size]; - for (int i = 0; i < size; i++) { - types[i] = data.getShort(position); - position += SizeOf.USHORT; - } - return types; - } - - public final class Section implements ByteInput, ByteOutput { - private final String name; - private final ByteBuffer data; - private final int initialPosition; - - private Section(String name, ByteBuffer data) { - this.name = name; - this.data = data; - this.initialPosition = data.position(); - } - - public int getPosition() { - return data.position(); - } - - public int readInt() { - return data.getInt(); - } - - public short readShort() { - return data.getShort(); - } - - public int readUnsignedShort() { - return readShort() & 0xffff; - } - - public byte readByte() { - return data.get(); - } - - public byte[] readByteArray(int length) { - byte[] result = new byte[length]; - data.get(result); - return result; - } - - public short[] readShortArray(int length) { - if (length == 0) { - return EMPTY_SHORT_ARRAY; - } - short[] result = new short[length]; - for (int i = 0; i < length; i++) { - result[i] = readShort(); - } - return result; - } - - public int readUleb128() { - return Leb128.readUnsignedLeb128(this); - } - - public int readUleb128p1() { - return Leb128.readUnsignedLeb128(this) - 1; - } - - public int readSleb128() { - return Leb128.readSignedLeb128(this); - } - - public void writeUleb128p1(int i) { - writeUleb128(i + 1); - } - - public TypeList readTypeList() { - int size = readInt(); - short[] types = readShortArray(size); - alignToFourBytes(); - return new TypeList(Dex.this, types); - } - - public String readString() { - int offset = readInt(); - int savedPosition = data.position(); - int savedLimit = data.limit(); - data.position(offset); - data.limit(data.capacity()); - try { - int expectedLength = readUleb128(); - String result = Mutf8.decode(this, new char[expectedLength]); - if (result.length() != expectedLength) { - throw new DexException("Declared length " + expectedLength - + " doesn't match decoded length of " + result.length()); - } - return result; - } catch (UTFDataFormatException e) { - throw new DexException(e); - } finally { - data.position(savedPosition); - data.limit(savedLimit); - } - } - - public FieldId readFieldId() { - int declaringClassIndex = readUnsignedShort(); - int typeIndex = readUnsignedShort(); - int nameIndex = readInt(); - return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex); - } - - public MethodId readMethodId() { - int declaringClassIndex = readUnsignedShort(); - int protoIndex = readUnsignedShort(); - int nameIndex = readInt(); - return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex); - } - - public ProtoId readProtoId() { - int shortyIndex = readInt(); - int returnTypeIndex = readInt(); - int parametersOffset = readInt(); - return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset); - } - - public ClassDef readClassDef() { - int offset = getPosition(); - int type = readInt(); - int accessFlags = readInt(); - int supertype = readInt(); - int interfacesOffset = readInt(); - int sourceFileIndex = readInt(); - int annotationsOffset = readInt(); - int classDataOffset = readInt(); - int staticValuesOffset = readInt(); - return new ClassDef(Dex.this, offset, type, accessFlags, supertype, - interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset, - staticValuesOffset); - } - - private Code readCode() { - int registersSize = readUnsignedShort(); - int insSize = readUnsignedShort(); - int outsSize = readUnsignedShort(); - int triesSize = readUnsignedShort(); - int debugInfoOffset = readInt(); - int instructionsSize = readInt(); - short[] instructions = readShortArray(instructionsSize); - Try[] tries; - CatchHandler[] catchHandlers; - if (triesSize > 0) { - if (instructions.length % 2 == 1) { - readShort(); // padding - } - - /* - * We can't read the tries until we've read the catch handlers. - * Unfortunately they're in the opposite order in the dex file - * so we need to read them out-of-order. - */ - Section triesSection = open(data.position()); - skip(triesSize * SizeOf.TRY_ITEM); - catchHandlers = readCatchHandlers(); - tries = triesSection.readTries(triesSize, catchHandlers); - } else { - tries = new Try[0]; - catchHandlers = new CatchHandler[0]; - } - return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions, - tries, catchHandlers); - } - - private CatchHandler[] readCatchHandlers() { - int baseOffset = data.position(); - int catchHandlersSize = readUleb128(); - CatchHandler[] result = new CatchHandler[catchHandlersSize]; - for (int i = 0; i < catchHandlersSize; i++) { - int offset = data.position() - baseOffset; - result[i] = readCatchHandler(offset); - } - return result; - } - - private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) { - Try[] result = new Try[triesSize]; - for (int i = 0; i < triesSize; i++) { - int startAddress = readInt(); - int instructionCount = readUnsignedShort(); - int handlerOffset = readUnsignedShort(); - int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset); - result[i] = new Try(startAddress, instructionCount, catchHandlerIndex); - } - return result; - } - - private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) { - for (int i = 0; i < catchHandlers.length; i++) { - CatchHandler catchHandler = catchHandlers[i]; - if (catchHandler.getOffset() == offset) { - return i; - } - } - throw new IllegalArgumentException(); - } - - private CatchHandler readCatchHandler(int offset) { - int size = readSleb128(); - int handlersCount = Math.abs(size); - int[] typeIndexes = new int[handlersCount]; - int[] addresses = new int[handlersCount]; - for (int i = 0; i < handlersCount; i++) { - typeIndexes[i] = readUleb128(); - addresses[i] = readUleb128(); - } - int catchAllAddress = size <= 0 ? readUleb128() : -1; - return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset); - } - - private ClassData readClassData() { - int staticFieldsSize = readUleb128(); - int instanceFieldsSize = readUleb128(); - int directMethodsSize = readUleb128(); - int virtualMethodsSize = readUleb128(); - ClassData.Field[] staticFields = readFields(staticFieldsSize); - ClassData.Field[] instanceFields = readFields(instanceFieldsSize); - ClassData.Method[] directMethods = readMethods(directMethodsSize); - ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize); - return new ClassData(staticFields, instanceFields, directMethods, virtualMethods); - } - - private ClassData.Field[] readFields(int count) { - ClassData.Field[] result = new ClassData.Field[count]; - int fieldIndex = 0; - for (int i = 0; i < count; i++) { - fieldIndex += readUleb128(); // field index diff - int accessFlags = readUleb128(); - result[i] = new ClassData.Field(fieldIndex, accessFlags); - } - return result; - } - - private ClassData.Method[] readMethods(int count) { - ClassData.Method[] result = new ClassData.Method[count]; - int methodIndex = 0; - for (int i = 0; i < count; i++) { - methodIndex += readUleb128(); // method index diff - int accessFlags = readUleb128(); - int codeOff = readUleb128(); - result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff); - } - return result; - } - - /** - * Returns a byte array containing the bytes from {@code start} to this - * section's current position. - */ - private byte[] getBytesFrom(int start) { - int end = data.position(); - byte[] result = new byte[end - start]; - data.position(start); - data.get(result); - return result; - } - - public Annotation readAnnotation() { - byte visibility = readByte(); - int start = data.position(); - new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue(); - return new Annotation(Dex.this, visibility, new EncodedValue(getBytesFrom(start))); - } - - public EncodedValue readEncodedArray() { - int start = data.position(); - new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue(); - return new EncodedValue(getBytesFrom(start)); - } - - public void skip(int count) { - if (count < 0) { - throw new IllegalArgumentException(); - } - data.position(data.position() + count); - } - - /** - * Skips bytes until the position is aligned to a multiple of 4. - */ - public void alignToFourBytes() { - data.position((data.position() + 3) & ~3); - } - - /** - * Writes 0x00 until the position is aligned to a multiple of 4. - */ - public void alignToFourBytesWithZeroFill() { - while ((data.position() & 3) != 0) { - data.put((byte) 0); - } - } - - public void assertFourByteAligned() { - if ((data.position() & 3) != 0) { - throw new IllegalStateException("Not four byte aligned!"); - } - } - - public void write(byte[] bytes) { - this.data.put(bytes); - } - - public void writeByte(int b) { - data.put((byte) b); - } - - public void writeShort(short i) { - data.putShort(i); - } - - public void writeUnsignedShort(int i) { - short s = (short) i; - if (i != (s & 0xffff)) { - throw new IllegalArgumentException("Expected an unsigned short: " + i); - } - writeShort(s); - } - - public void write(short[] shorts) { - for (short s : shorts) { - writeShort(s); - } - } - - public void writeInt(int i) { - data.putInt(i); - } - - public void writeUleb128(int i) { - try { - Leb128.writeUnsignedLeb128(this, i); - } catch (ArrayIndexOutOfBoundsException e) { - throw new DexException("Section limit " + data.limit() + " exceeded by " + name); - } - } - - public void writeSleb128(int i) { - try { - Leb128.writeSignedLeb128(this, i); - } catch (ArrayIndexOutOfBoundsException e) { - throw new DexException("Section limit " + data.limit() + " exceeded by " + name); - } - } - - public void writeStringData(String value) { - try { - int length = value.length(); - writeUleb128(length); - write(Mutf8.encode(value)); - writeByte(0); - } catch (UTFDataFormatException e) { - throw new AssertionError(); - } - } - - public void writeTypeList(TypeList typeList) { - short[] types = typeList.getTypes(); - writeInt(types.length); - for (short type : types) { - writeShort(type); - } - alignToFourBytesWithZeroFill(); - } - - /** - * Returns the number of bytes remaining in this section. - */ - public int remaining() { - return data.remaining(); - } - - /** - * Returns the number of bytes used by this section. - */ - public int used() { - return data.position() - initialPosition; - } - } - - private final class StringTable extends AbstractList implements RandomAccess { - @Override public String get(int index) { - checkBounds(index, tableOfContents.stringIds.size); - return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM)) - .readString(); - } - @Override public int size() { - return tableOfContents.stringIds.size; - } - } - - private final class TypeIndexToDescriptorIndexTable extends AbstractList - implements RandomAccess { - @Override public Integer get(int index) { - return descriptorIndexFromTypeIndex(index); - } - @Override public int size() { - return tableOfContents.typeIds.size; - } - } - - private final class TypeIndexToDescriptorTable extends AbstractList - implements RandomAccess { - @Override public String get(int index) { - return strings.get(descriptorIndexFromTypeIndex(index)); - } - @Override public int size() { - return tableOfContents.typeIds.size; - } - } - - private final class ProtoIdTable extends AbstractList implements RandomAccess { - @Override public ProtoId get(int index) { - checkBounds(index, tableOfContents.protoIds.size); - return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index)) - .readProtoId(); - } - @Override public int size() { - return tableOfContents.protoIds.size; - } - } - - private final class FieldIdTable extends AbstractList implements RandomAccess { - @Override public FieldId get(int index) { - checkBounds(index, tableOfContents.fieldIds.size); - return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index)) - .readFieldId(); - } - @Override public int size() { - return tableOfContents.fieldIds.size; - } - } - - private final class MethodIdTable extends AbstractList implements RandomAccess { - @Override public MethodId get(int index) { - checkBounds(index, tableOfContents.methodIds.size); - return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index)) - .readMethodId(); - } - @Override public int size() { - return tableOfContents.methodIds.size; - } - } - - private final class ClassDefIterator implements Iterator { - private final Dex.Section in = open(tableOfContents.classDefs.off); - private int count = 0; - - @Override - public boolean hasNext() { - return count < tableOfContents.classDefs.size; - } - @Override - public ClassDef next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - count++; - return in.readClassDef(); - } - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } - - private final class ClassDefIterable implements Iterable { - public Iterator iterator() { - return !tableOfContents.classDefs.exists() - ? Collections.emptySet().iterator() - : new ClassDefIterator(); - } - } -} diff --git a/dex/src/main/java/com/android/dex/DexException.java b/dex/src/main/java/com/android/dex/DexException.java deleted file mode 100644 index ee0af18f9..000000000 --- a/dex/src/main/java/com/android/dex/DexException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.util.ExceptionWithContext; - -/** - * Thrown when there's a format problem reading, writing, or generally - * processing a dex file. - */ -public class DexException extends ExceptionWithContext { - public DexException(String message) { - super(message); - } - - public DexException(Throwable cause) { - super(cause); - } -} diff --git a/dex/src/main/java/com/android/dex/DexFormat.java b/dex/src/main/java/com/android/dex/DexFormat.java deleted file mode 100644 index c598eee03..000000000 --- a/dex/src/main/java/com/android/dex/DexFormat.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -/** - * Constants that show up in and are otherwise related to {@code .dex} - * files, and helper methods for same. - */ -public final class DexFormat { - private DexFormat() {} - - /** - * API level to target in order to produce the most modern file - * format - */ - public static final int API_CURRENT = 24; - - /** API level to target in order to suppress extended opcode usage */ - public static final int API_NO_EXTENDED_OPCODES = 13; - - /** - * file name of the primary {@code .dex} file inside an - * application or library {@code .jar} file - */ - public static final String DEX_IN_JAR_NAME = "classes.dex"; - - /** common prefix for all dex file "magic numbers" */ - public static final String MAGIC_PREFIX = "dex\n"; - - /** common suffix for all dex file "magic numbers" */ - public static final String MAGIC_SUFFIX = "\0"; - - /** - * Dex file version number for dalvik. - *

- * Note: Dex version 36 was loadable in some versions of Dalvik but was never fully supported or - * completed and is not considered a valid dex file format. - *

- */ - public static final String VERSION_CURRENT = "037"; - - /** dex file version number for API level 13 and earlier */ - public static final String VERSION_FOR_API_13 = "035"; - - /** - * value used to indicate endianness of file contents - */ - public static final int ENDIAN_TAG = 0x12345678; - - /** - * Maximum addressable field or method index. - * The largest addressable member is 0xffff, in the "instruction formats" spec as field@CCCC or - * meth@CCCC. - */ - public static final int MAX_MEMBER_IDX = 0xFFFF; - - /** - * Maximum addressable type index. - * The largest addressable type is 0xffff, in the "instruction formats" spec as type@CCCC. - */ - public static final int MAX_TYPE_IDX = 0xFFFF; - - /** - * Returns the API level corresponding to the given magic number, - * or {@code -1} if the given array is not a well-formed dex file - * magic number. - */ - public static int magicToApi(byte[] magic) { - if (magic.length != 8) { - return -1; - } - - if ((magic[0] != 'd') || (magic[1] != 'e') || (magic[2] != 'x') || (magic[3] != '\n') || - (magic[7] != '\0')) { - return -1; - } - - String version = "" + ((char) magic[4]) + ((char) magic[5]) +((char) magic[6]); - - if (version.equals(VERSION_CURRENT)) { - return API_CURRENT; - } else if (version.equals(VERSION_FOR_API_13)) { - return API_NO_EXTENDED_OPCODES; - } - - return -1; - } - - /** - * Returns the magic number corresponding to the given target API level. - */ - public static String apiToMagic(int targetApiLevel) { - String version; - - if (targetApiLevel >= API_CURRENT) { - version = VERSION_CURRENT; - } else { - version = VERSION_FOR_API_13; - } - - return MAGIC_PREFIX + version + MAGIC_SUFFIX; - } - - public static boolean isSupportedDexMagic(byte[] magic) { - int api = magicToApi(magic); - return api == API_NO_EXTENDED_OPCODES || api == API_CURRENT; - } -} diff --git a/dex/src/main/java/com/android/dex/DexIndexOverflowException.java b/dex/src/main/java/com/android/dex/DexIndexOverflowException.java deleted file mode 100644 index 32262072b..000000000 --- a/dex/src/main/java/com/android/dex/DexIndexOverflowException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -package com.android.dex; - -/** - * Thrown when there's an index overflow writing a dex file. - */ -public final class DexIndexOverflowException extends DexException { - public DexIndexOverflowException(String message) { - super(message); - } - - public DexIndexOverflowException(Throwable cause) { - super(cause); - } -} diff --git a/dex/src/main/java/com/android/dex/EncodedValue.java b/dex/src/main/java/com/android/dex/EncodedValue.java deleted file mode 100644 index 8d0c3adcf..000000000 --- a/dex/src/main/java/com/android/dex/EncodedValue.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.util.ByteArrayByteInput; -import com.android.dex.util.ByteInput; - -/** - * An encoded value or array. - */ -public final class EncodedValue implements Comparable { - private final byte[] data; - - public EncodedValue(byte[] data) { - this.data = data; - } - - public ByteInput asByteInput() { - return new ByteArrayByteInput(data); - } - - public byte[] getBytes() { - return data; - } - - public void writeTo(Dex.Section out) { - out.write(data); - } - - @Override public int compareTo(EncodedValue other) { - int size = Math.min(data.length, other.data.length); - for (int i = 0; i < size; i++) { - if (data[i] != other.data[i]) { - return (data[i] & 0xff) - (other.data[i] & 0xff); - } - } - return data.length - other.data.length; - } - - @Override public String toString() { - return Integer.toHexString(data[0] & 0xff) + "...(" + data.length + ")"; - } -} diff --git a/dex/src/main/java/com/android/dex/EncodedValueCodec.java b/dex/src/main/java/com/android/dex/EncodedValueCodec.java deleted file mode 100644 index 7fc172434..000000000 --- a/dex/src/main/java/com/android/dex/EncodedValueCodec.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.util.ByteInput; -import com.android.dex.util.ByteOutput; - -/** - * Read and write {@code encoded_value} primitives. - */ -public final class EncodedValueCodec { - private EncodedValueCodec() { - } - - /** - * Writes a signed integral to {@code out}. - */ - public static void writeSignedIntegralValue(ByteOutput out, int type, long value) { - /* - * Figure out how many bits are needed to represent the value, - * including a sign bit: The bit count is subtracted from 65 - * and not 64 to account for the sign bit. The xor operation - * has the effect of leaving non-negative values alone and - * unary complementing negative values (so that a leading zero - * count always returns a useful number for our present - * purpose). - */ - int requiredBits = 65 - Long.numberOfLeadingZeros(value ^ (value >> 63)); - - // Round up the requiredBits to a number of bytes. - int requiredBytes = (requiredBits + 0x07) >> 3; - - /* - * Write the header byte, which includes the type and - * requiredBytes - 1. - */ - out.writeByte(type | ((requiredBytes - 1) << 5)); - - // Write the value, per se. - while (requiredBytes > 0) { - out.writeByte((byte) value); - value >>= 8; - requiredBytes--; - } - } - - /** - * Writes an unsigned integral to {@code out}. - */ - public static void writeUnsignedIntegralValue(ByteOutput out, int type, long value) { - // Figure out how many bits are needed to represent the value. - int requiredBits = 64 - Long.numberOfLeadingZeros(value); - if (requiredBits == 0) { - requiredBits = 1; - } - - // Round up the requiredBits to a number of bytes. - int requiredBytes = (requiredBits + 0x07) >> 3; - - /* - * Write the header byte, which includes the type and - * requiredBytes - 1. - */ - out.writeByte(type | ((requiredBytes - 1) << 5)); - - // Write the value, per se. - while (requiredBytes > 0) { - out.writeByte((byte) value); - value >>= 8; - requiredBytes--; - } - } - - /** - * Writes a right-zero-extended value to {@code out}. - */ - public static void writeRightZeroExtendedValue(ByteOutput out, int type, long value) { - // Figure out how many bits are needed to represent the value. - int requiredBits = 64 - Long.numberOfTrailingZeros(value); - if (requiredBits == 0) { - requiredBits = 1; - } - - // Round up the requiredBits to a number of bytes. - int requiredBytes = (requiredBits + 0x07) >> 3; - - // Scootch the first bits to be written down to the low-order bits. - value >>= 64 - (requiredBytes * 8); - - /* - * Write the header byte, which includes the type and - * requiredBytes - 1. - */ - out.writeByte(type | ((requiredBytes - 1) << 5)); - - // Write the value, per se. - while (requiredBytes > 0) { - out.writeByte((byte) value); - value >>= 8; - requiredBytes--; - } - } - - /** - * Read a signed integer. - * - * @param zwidth byte count minus one - */ - public static int readSignedInt(ByteInput in, int zwidth) { - int result = 0; - for (int i = zwidth; i >= 0; i--) { - result = (result >>> 8) | ((in.readByte() & 0xff) << 24); - } - result >>= (3 - zwidth) * 8; - return result; - } - - /** - * Read an unsigned integer. - * - * @param zwidth byte count minus one - * @param fillOnRight true to zero fill on the right; false on the left - */ - public static int readUnsignedInt(ByteInput in, int zwidth, boolean fillOnRight) { - int result = 0; - if (!fillOnRight) { - for (int i = zwidth; i >= 0; i--) { - result = (result >>> 8) | ((in.readByte() & 0xff) << 24); - } - result >>>= (3 - zwidth) * 8; - } else { - for (int i = zwidth; i >= 0; i--) { - result = (result >>> 8) | ((in.readByte() & 0xff) << 24); - } - } - return result; - } - - /** - * Read a signed long. - * - * @param zwidth byte count minus one - */ - public static long readSignedLong(ByteInput in, int zwidth) { - long result = 0; - for (int i = zwidth; i >= 0; i--) { - result = (result >>> 8) | ((in.readByte() & 0xffL) << 56); - } - result >>= (7 - zwidth) * 8; - return result; - } - - /** - * Read an unsigned long. - * - * @param zwidth byte count minus one - * @param fillOnRight true to zero fill on the right; false on the left - */ - public static long readUnsignedLong(ByteInput in, int zwidth, boolean fillOnRight) { - long result = 0; - if (!fillOnRight) { - for (int i = zwidth; i >= 0; i--) { - result = (result >>> 8) | ((in.readByte() & 0xffL) << 56); - } - result >>>= (7 - zwidth) * 8; - } else { - for (int i = zwidth; i >= 0; i--) { - result = (result >>> 8) | ((in.readByte() & 0xffL) << 56); - } - } - return result; - } -} diff --git a/dex/src/main/java/com/android/dex/EncodedValueReader.java b/dex/src/main/java/com/android/dex/EncodedValueReader.java deleted file mode 100644 index 6f60538a2..000000000 --- a/dex/src/main/java/com/android/dex/EncodedValueReader.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.util.ByteInput; - -/** - * Pull parser for encoded values. - */ -public final class EncodedValueReader { - public static final int ENCODED_BYTE = 0x00; - public static final int ENCODED_SHORT = 0x02; - public static final int ENCODED_CHAR = 0x03; - public static final int ENCODED_INT = 0x04; - public static final int ENCODED_LONG = 0x06; - public static final int ENCODED_FLOAT = 0x10; - public static final int ENCODED_DOUBLE = 0x11; - public static final int ENCODED_STRING = 0x17; - public static final int ENCODED_TYPE = 0x18; - public static final int ENCODED_FIELD = 0x19; - public static final int ENCODED_ENUM = 0x1b; - public static final int ENCODED_METHOD = 0x1a; - public static final int ENCODED_ARRAY = 0x1c; - public static final int ENCODED_ANNOTATION = 0x1d; - public static final int ENCODED_NULL = 0x1e; - public static final int ENCODED_BOOLEAN = 0x1f; - - /** placeholder type if the type is not yet known */ - private static final int MUST_READ = -1; - - protected final ByteInput in; - private int type = MUST_READ; - private int annotationType; - private int arg; - - public EncodedValueReader(ByteInput in) { - this.in = in; - } - - public EncodedValueReader(EncodedValue in) { - this(in.asByteInput()); - } - - /** - * Creates a new encoded value reader whose only value is the specified - * known type. This is useful for encoded values without a type prefix, - * such as class_def_item's encoded_array or annotation_item's - * encoded_annotation. - */ - public EncodedValueReader(ByteInput in, int knownType) { - this.in = in; - this.type = knownType; - } - - public EncodedValueReader(EncodedValue in, int knownType) { - this(in.asByteInput(), knownType); - } - - /** - * Returns the type of the next value to read. - */ - public int peek() { - if (type == MUST_READ) { - int argAndType = in.readByte() & 0xff; - type = argAndType & 0x1f; - arg = (argAndType & 0xe0) >> 5; - } - return type; - } - - /** - * Begins reading the elements of an array, returning the array's size. The - * caller must follow up by calling a read method for each element in the - * array. For example, this reads a byte array:
   {@code
-     *   int arraySize = readArray();
-     *   for (int i = 0, i < arraySize; i++) {
-     *     readByte();
-     *   }
-     * }
- */ - public int readArray() { - checkType(ENCODED_ARRAY); - type = MUST_READ; - return Leb128.readUnsignedLeb128(in); - } - - /** - * Begins reading the fields of an annotation, returning the number of - * fields. The caller must follow up by making alternating calls to {@link - * #readAnnotationName()} and another read method. For example, this reads - * an annotation whose fields are all bytes:
   {@code
-     *   int fieldCount = readAnnotation();
-     *   int annotationType = getAnnotationType();
-     *   for (int i = 0; i < fieldCount; i++) {
-     *       readAnnotationName();
-     *       readByte();
-     *   }
-     * }
- */ - public int readAnnotation() { - checkType(ENCODED_ANNOTATION); - type = MUST_READ; - annotationType = Leb128.readUnsignedLeb128(in); - return Leb128.readUnsignedLeb128(in); - } - - /** - * Returns the type of the annotation just returned by {@link - * #readAnnotation()}. This method's value is undefined unless the most - * recent call was to {@link #readAnnotation()}. - */ - public int getAnnotationType() { - return annotationType; - } - - public int readAnnotationName() { - return Leb128.readUnsignedLeb128(in); - } - - public byte readByte() { - checkType(ENCODED_BYTE); - type = MUST_READ; - return (byte) EncodedValueCodec.readSignedInt(in, arg); - } - - public short readShort() { - checkType(ENCODED_SHORT); - type = MUST_READ; - return (short) EncodedValueCodec.readSignedInt(in, arg); - } - - public char readChar() { - checkType(ENCODED_CHAR); - type = MUST_READ; - return (char) EncodedValueCodec.readUnsignedInt(in, arg, false); - } - - public int readInt() { - checkType(ENCODED_INT); - type = MUST_READ; - return EncodedValueCodec.readSignedInt(in, arg); - } - - public long readLong() { - checkType(ENCODED_LONG); - type = MUST_READ; - return EncodedValueCodec.readSignedLong(in, arg); - } - - public float readFloat() { - checkType(ENCODED_FLOAT); - type = MUST_READ; - return Float.intBitsToFloat(EncodedValueCodec.readUnsignedInt(in, arg, true)); - } - - public double readDouble() { - checkType(ENCODED_DOUBLE); - type = MUST_READ; - return Double.longBitsToDouble(EncodedValueCodec.readUnsignedLong(in, arg, true)); - } - - public int readString() { - checkType(ENCODED_STRING); - type = MUST_READ; - return EncodedValueCodec.readUnsignedInt(in, arg, false); - } - - public int readType() { - checkType(ENCODED_TYPE); - type = MUST_READ; - return EncodedValueCodec.readUnsignedInt(in, arg, false); - } - - public int readField() { - checkType(ENCODED_FIELD); - type = MUST_READ; - return EncodedValueCodec.readUnsignedInt(in, arg, false); - } - - public int readEnum() { - checkType(ENCODED_ENUM); - type = MUST_READ; - return EncodedValueCodec.readUnsignedInt(in, arg, false); - } - - public int readMethod() { - checkType(ENCODED_METHOD); - type = MUST_READ; - return EncodedValueCodec.readUnsignedInt(in, arg, false); - } - - public void readNull() { - checkType(ENCODED_NULL); - type = MUST_READ; - } - - public boolean readBoolean() { - checkType(ENCODED_BOOLEAN); - type = MUST_READ; - return arg != 0; - } - - /** - * Skips a single value, including its nested values if it is an array or - * annotation. - */ - public void skipValue() { - switch (peek()) { - case ENCODED_BYTE: - readByte(); - break; - case ENCODED_SHORT: - readShort(); - break; - case ENCODED_CHAR: - readChar(); - break; - case ENCODED_INT: - readInt(); - break; - case ENCODED_LONG: - readLong(); - break; - case ENCODED_FLOAT: - readFloat(); - break; - case ENCODED_DOUBLE: - readDouble(); - break; - case ENCODED_STRING: - readString(); - break; - case ENCODED_TYPE: - readType(); - break; - case ENCODED_FIELD: - readField(); - break; - case ENCODED_ENUM: - readEnum(); - break; - case ENCODED_METHOD: - readMethod(); - break; - case ENCODED_ARRAY: - for (int i = 0, size = readArray(); i < size; i++) { - skipValue(); - } - break; - case ENCODED_ANNOTATION: - for (int i = 0, size = readAnnotation(); i < size; i++) { - readAnnotationName(); - skipValue(); - } - break; - case ENCODED_NULL: - readNull(); - break; - case ENCODED_BOOLEAN: - readBoolean(); - break; - default: - throw new DexException("Unexpected type: " + Integer.toHexString(type)); - } - } - - private void checkType(int expected) { - if (peek() != expected) { - throw new IllegalStateException( - String.format("Expected %x but was %x", expected, peek())); - } - } -} diff --git a/dex/src/main/java/com/android/dex/FieldId.java b/dex/src/main/java/com/android/dex/FieldId.java deleted file mode 100644 index 2f41708c8..000000000 --- a/dex/src/main/java/com/android/dex/FieldId.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.util.Unsigned; - -public final class FieldId implements Comparable { - private final Dex dex; - private final int declaringClassIndex; - private final int typeIndex; - private final int nameIndex; - - public FieldId(Dex dex, int declaringClassIndex, int typeIndex, int nameIndex) { - this.dex = dex; - this.declaringClassIndex = declaringClassIndex; - this.typeIndex = typeIndex; - this.nameIndex = nameIndex; - } - - public int getDeclaringClassIndex() { - return declaringClassIndex; - } - - public int getTypeIndex() { - return typeIndex; - } - - public int getNameIndex() { - return nameIndex; - } - - public int compareTo(FieldId other) { - if (declaringClassIndex != other.declaringClassIndex) { - return Unsigned.compare(declaringClassIndex, other.declaringClassIndex); - } - if (nameIndex != other.nameIndex) { - return Unsigned.compare(nameIndex, other.nameIndex); - } - return Unsigned.compare(typeIndex, other.typeIndex); // should always be 0 - } - - public void writeTo(Dex.Section out) { - out.writeUnsignedShort(declaringClassIndex); - out.writeUnsignedShort(typeIndex); - out.writeInt(nameIndex); - } - - @Override public String toString() { - if (dex == null) { - return declaringClassIndex + " " + typeIndex + " " + nameIndex; - } - return dex.typeNames().get(typeIndex) + "." + dex.strings().get(nameIndex); - } -} diff --git a/dex/src/main/java/com/android/dex/Leb128.java b/dex/src/main/java/com/android/dex/Leb128.java deleted file mode 100644 index 1a82e383e..000000000 --- a/dex/src/main/java/com/android/dex/Leb128.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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. - */ - -package com.android.dex; - -import com.android.dex.util.ByteInput; -import com.android.dex.util.ByteOutput; - -/** - * Reads and writes DWARFv3 LEB 128 signed and unsigned integers. See DWARF v3 - * section 7.6. - */ -public final class Leb128 { - private Leb128() { - } - - /** - * Gets the number of bytes in the unsigned LEB128 encoding of the - * given value. - * - * @param value the value in question - * @return its write size, in bytes - */ - public static int unsignedLeb128Size(int value) { - // TODO: This could be much cleverer. - - int remaining = value >> 7; - int count = 0; - - while (remaining != 0) { - remaining >>= 7; - count++; - } - - return count + 1; - } - - /** - * Gets the number of bytes in the signed LEB128 encoding of the - * given value. - * - * @param value the value in question - * @return its write size, in bytes - */ - public static int signedLeb128Size(int value) { - // TODO: This could be much cleverer. - - int remaining = value >> 7; - int count = 0; - boolean hasMore = true; - int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; - - while (hasMore) { - hasMore = (remaining != end) - || ((remaining & 1) != ((value >> 6) & 1)); - - value = remaining; - remaining >>= 7; - count++; - } - - return count; - } - - /** - * Reads an signed integer from {@code in}. - */ - public static int readSignedLeb128(ByteInput in) { - int result = 0; - int cur; - int count = 0; - int signBits = -1; - - do { - cur = in.readByte() & 0xff; - result |= (cur & 0x7f) << (count * 7); - signBits <<= 7; - count++; - } while (((cur & 0x80) == 0x80) && count < 5); - - if ((cur & 0x80) == 0x80) { - throw new DexException("invalid LEB128 sequence"); - } - - // Sign extend if appropriate - if (((signBits >> 1) & result) != 0 ) { - result |= signBits; - } - - return result; - } - - /** - * Reads an unsigned integer from {@code in}. - */ - public static int readUnsignedLeb128(ByteInput in) { - int result = 0; - int cur; - int count = 0; - - do { - cur = in.readByte() & 0xff; - result |= (cur & 0x7f) << (count * 7); - count++; - } while (((cur & 0x80) == 0x80) && count < 5); - - if ((cur & 0x80) == 0x80) { - throw new DexException("invalid LEB128 sequence"); - } - - return result; - } - - /** - * Writes {@code value} as an unsigned integer to {@code out}, starting at - * {@code offset}. Returns the number of bytes written. - */ - public static void writeUnsignedLeb128(ByteOutput out, int value) { - int remaining = value >>> 7; - - while (remaining != 0) { - out.writeByte((byte) ((value & 0x7f) | 0x80)); - value = remaining; - remaining >>>= 7; - } - - out.writeByte((byte) (value & 0x7f)); - } - - /** - * Writes {@code value} as a signed integer to {@code out}, starting at - * {@code offset}. Returns the number of bytes written. - */ - public static void writeSignedLeb128(ByteOutput out, int value) { - int remaining = value >> 7; - boolean hasMore = true; - int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; - - while (hasMore) { - hasMore = (remaining != end) - || ((remaining & 1) != ((value >> 6) & 1)); - - out.writeByte((byte) ((value & 0x7f) | (hasMore ? 0x80 : 0))); - value = remaining; - remaining >>= 7; - } - } -} diff --git a/dex/src/main/java/com/android/dex/MethodId.java b/dex/src/main/java/com/android/dex/MethodId.java deleted file mode 100644 index e51874026..000000000 --- a/dex/src/main/java/com/android/dex/MethodId.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.util.Unsigned; - -public final class MethodId implements Comparable { - private final Dex dex; - private final int declaringClassIndex; - private final int protoIndex; - private final int nameIndex; - - public MethodId(Dex dex, int declaringClassIndex, int protoIndex, int nameIndex) { - this.dex = dex; - this.declaringClassIndex = declaringClassIndex; - this.protoIndex = protoIndex; - this.nameIndex = nameIndex; - } - - public int getDeclaringClassIndex() { - return declaringClassIndex; - } - - public int getProtoIndex() { - return protoIndex; - } - - public int getNameIndex() { - return nameIndex; - } - - public int compareTo(MethodId other) { - if (declaringClassIndex != other.declaringClassIndex) { - return Unsigned.compare(declaringClassIndex, other.declaringClassIndex); - } - if (nameIndex != other.nameIndex) { - return Unsigned.compare(nameIndex, other.nameIndex); - } - return Unsigned.compare(protoIndex, other.protoIndex); - } - - public void writeTo(Dex.Section out) { - out.writeUnsignedShort(declaringClassIndex); - out.writeUnsignedShort(protoIndex); - out.writeInt(nameIndex); - } - - @Override public String toString() { - if (dex == null) { - return declaringClassIndex + " " + protoIndex + " " + nameIndex; - } - return dex.typeNames().get(declaringClassIndex) - + "." + dex.strings().get(nameIndex) - + dex.readTypeList(dex.protoIds().get(protoIndex).getParametersOffset()); - } -} diff --git a/dex/src/main/java/com/android/dex/Mutf8.java b/dex/src/main/java/com/android/dex/Mutf8.java deleted file mode 100644 index c64da331b..000000000 --- a/dex/src/main/java/com/android/dex/Mutf8.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.util.ByteInput; -import java.io.UTFDataFormatException; - -/** - * Modified UTF-8 as described in the dex file format spec. - * - *

Derived from libcore's MUTF-8 encoder at java.nio.charset.ModifiedUtf8. - */ -public final class Mutf8 { - private Mutf8() {} - - /** - * Decodes bytes from {@code in} into {@code out} until a delimiter 0x00 is - * encountered. Returns a new string containing the decoded characters. - */ - public static String decode(ByteInput in, char[] out) throws UTFDataFormatException { - int s = 0; - while (true) { - char a = (char) (in.readByte() & 0xff); - if (a == 0) { - return new String(out, 0, s); - } - out[s] = a; - if (a < '\u0080') { - s++; - } else if ((a & 0xe0) == 0xc0) { - int b = in.readByte() & 0xff; - if ((b & 0xC0) != 0x80) { - throw new UTFDataFormatException("bad second byte"); - } - out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F)); - } else if ((a & 0xf0) == 0xe0) { - int b = in.readByte() & 0xff; - int c = in.readByte() & 0xff; - if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) { - throw new UTFDataFormatException("bad second or third byte"); - } - out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)); - } else { - throw new UTFDataFormatException("bad byte"); - } - } - } - - /** - * Returns the number of bytes the modified UTF8 representation of 's' would take. - */ - private static long countBytes(String s, boolean shortLength) throws UTFDataFormatException { - long result = 0; - final int length = s.length(); - for (int i = 0; i < length; ++i) { - char ch = s.charAt(i); - if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. - ++result; - } else if (ch <= 2047) { - result += 2; - } else { - result += 3; - } - if (shortLength && result > 65535) { - throw new UTFDataFormatException("String more than 65535 UTF bytes long"); - } - } - return result; - } - - /** - * Encodes the modified UTF-8 bytes corresponding to {@code s} into {@code - * dst}, starting at {@code offset}. - */ - public static void encode(byte[] dst, int offset, String s) { - final int length = s.length(); - for (int i = 0; i < length; i++) { - char ch = s.charAt(i); - if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. - dst[offset++] = (byte) ch; - } else if (ch <= 2047) { - dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6))); - dst[offset++] = (byte) (0x80 | (0x3f & ch)); - } else { - dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12))); - dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6))); - dst[offset++] = (byte) (0x80 | (0x3f & ch)); - } - } - } - - /** - * Returns an array containing the modified UTF-8 form of {@code s}. - */ - public static byte[] encode(String s) throws UTFDataFormatException { - int utfCount = (int) countBytes(s, true); - byte[] result = new byte[utfCount]; - encode(result, 0, s); - return result; - } -} diff --git a/dex/src/main/java/com/android/dex/ProtoId.java b/dex/src/main/java/com/android/dex/ProtoId.java deleted file mode 100644 index 9d9f484f2..000000000 --- a/dex/src/main/java/com/android/dex/ProtoId.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.util.Unsigned; - -public final class ProtoId implements Comparable { - private final Dex dex; - private final int shortyIndex; - private final int returnTypeIndex; - private final int parametersOffset; - - public ProtoId(Dex dex, int shortyIndex, int returnTypeIndex, int parametersOffset) { - this.dex = dex; - this.shortyIndex = shortyIndex; - this.returnTypeIndex = returnTypeIndex; - this.parametersOffset = parametersOffset; - } - - public int compareTo(ProtoId other) { - if (returnTypeIndex != other.returnTypeIndex) { - return Unsigned.compare(returnTypeIndex, other.returnTypeIndex); - } - return Unsigned.compare(parametersOffset, other.parametersOffset); - } - - public int getShortyIndex() { - return shortyIndex; - } - - public int getReturnTypeIndex() { - return returnTypeIndex; - } - - public int getParametersOffset() { - return parametersOffset; - } - - public void writeTo(Dex.Section out) { - out.writeInt(shortyIndex); - out.writeInt(returnTypeIndex); - out.writeInt(parametersOffset); - } - - @Override public String toString() { - if (dex == null) { - return shortyIndex + " " + returnTypeIndex + " " + parametersOffset; - } - - return dex.strings().get(shortyIndex) - + ": " + dex.typeNames().get(returnTypeIndex) - + " " + dex.readTypeList(parametersOffset); - } -} diff --git a/dex/src/main/java/com/android/dex/SizeOf.java b/dex/src/main/java/com/android/dex/SizeOf.java deleted file mode 100644 index 65fab565b..000000000 --- a/dex/src/main/java/com/android/dex/SizeOf.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -public final class SizeOf { - private SizeOf() {} - - public static final int UBYTE = 1; - public static final int USHORT = 2; - public static final int UINT = 4; - - public static final int SIGNATURE = UBYTE * 20; - - /** - * magic ubyte[8] - * checksum uint - * signature ubyte[20] - * file_size uint - * header_size uint - * endian_tag uint - * link_size uint - * link_off uint - * map_off uint - * string_ids_size uint - * string_ids_off uint - * type_ids_size uint - * type_ids_off uint - * proto_ids_size uint - * proto_ids_off uint - * field_ids_size uint - * field_ids_off uint - * method_ids_size uint - * method_ids_off uint - * class_defs_size uint - * class_defs_off uint - * data_size uint - * data_off uint - */ - public static final int HEADER_ITEM = (8 * UBYTE) + UINT + SIGNATURE + (20 * UINT); // 0x70 - - /** - * string_data_off uint - */ - public static final int STRING_ID_ITEM = UINT; - - /** - * descriptor_idx uint - */ - public static final int TYPE_ID_ITEM = UINT; - - /** - * type_idx ushort - */ - public static final int TYPE_ITEM = USHORT; - - /** - * shorty_idx uint - * return_type_idx uint - * return_type_idx uint - */ - public static final int PROTO_ID_ITEM = UINT + UINT + UINT; - - /** - * class_idx ushort - * type_idx/proto_idx ushort - * name_idx uint - */ - public static final int MEMBER_ID_ITEM = USHORT + USHORT + UINT; - - /** - * class_idx uint - * access_flags uint - * superclass_idx uint - * interfaces_off uint - * source_file_idx uint - * annotations_off uint - * class_data_off uint - * static_values_off uint - */ - public static final int CLASS_DEF_ITEM = 8 * UINT; - - /** - * type ushort - * unused ushort - * size uint - * offset uint - */ - public static final int MAP_ITEM = USHORT + USHORT + UINT + UINT; - - /** - * start_addr uint - * insn_count ushort - * handler_off ushort - */ - public static final int TRY_ITEM = UINT + USHORT + USHORT; -} diff --git a/dex/src/main/java/com/android/dex/TableOfContents.java b/dex/src/main/java/com/android/dex/TableOfContents.java deleted file mode 100644 index 583f19508..000000000 --- a/dex/src/main/java/com/android/dex/TableOfContents.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.Arrays; - -/** - * The file header and map. - */ -public final class TableOfContents { - - /* - * TODO: factor out ID constants. - */ - - public final Section header = new Section(0x0000); - public final Section stringIds = new Section(0x0001); - public final Section typeIds = new Section(0x0002); - public final Section protoIds = new Section(0x0003); - public final Section fieldIds = new Section(0x0004); - public final Section methodIds = new Section(0x0005); - public final Section classDefs = new Section(0x0006); - public final Section mapList = new Section(0x1000); - public final Section typeLists = new Section(0x1001); - public final Section annotationSetRefLists = new Section(0x1002); - public final Section annotationSets = new Section(0x1003); - public final Section classDatas = new Section(0x2000); - public final Section codes = new Section(0x2001); - public final Section stringDatas = new Section(0x2002); - public final Section debugInfos = new Section(0x2003); - public final Section annotations = new Section(0x2004); - public final Section encodedArrays = new Section(0x2005); - public final Section annotationsDirectories = new Section(0x2006); - public final Section[] sections = { - header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, - typeLists, annotationSetRefLists, annotationSets, classDatas, codes, stringDatas, - debugInfos, annotations, encodedArrays, annotationsDirectories - }; - - public int apiLevel; - public int checksum; - public byte[] signature; - public int fileSize; - public int linkSize; - public int linkOff; - public int dataSize; - public int dataOff; - - public TableOfContents() { - signature = new byte[20]; - } - - public void readFrom(Dex dex) throws IOException { - readHeader(dex.open(0)); - readMap(dex.open(mapList.off)); - computeSizesFromOffsets(); - } - - private void readHeader(Dex.Section headerIn) throws UnsupportedEncodingException { - byte[] magic = headerIn.readByteArray(8); - - if (!DexFormat.isSupportedDexMagic(magic)) { - throw new DexException("Unexpected magic: " + Arrays.toString(magic)); - } - - apiLevel = DexFormat.magicToApi(magic); - checksum = headerIn.readInt(); - signature = headerIn.readByteArray(20); - fileSize = headerIn.readInt(); - int headerSize = headerIn.readInt(); - if (headerSize != SizeOf.HEADER_ITEM) { - throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize)); - } - int endianTag = headerIn.readInt(); - if (endianTag != DexFormat.ENDIAN_TAG) { - throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag)); - } - linkSize = headerIn.readInt(); - linkOff = headerIn.readInt(); - mapList.off = headerIn.readInt(); - if (mapList.off == 0) { - throw new DexException("Cannot merge dex files that do not contain a map"); - } - stringIds.size = headerIn.readInt(); - stringIds.off = headerIn.readInt(); - typeIds.size = headerIn.readInt(); - typeIds.off = headerIn.readInt(); - protoIds.size = headerIn.readInt(); - protoIds.off = headerIn.readInt(); - fieldIds.size = headerIn.readInt(); - fieldIds.off = headerIn.readInt(); - methodIds.size = headerIn.readInt(); - methodIds.off = headerIn.readInt(); - classDefs.size = headerIn.readInt(); - classDefs.off = headerIn.readInt(); - dataSize = headerIn.readInt(); - dataOff = headerIn.readInt(); - } - - private void readMap(Dex.Section in) throws IOException { - int mapSize = in.readInt(); - Section previous = null; - for (int i = 0; i < mapSize; i++) { - short type = in.readShort(); - in.readShort(); // unused - Section section = getSection(type); - int size = in.readInt(); - int offset = in.readInt(); - - if ((section.size != 0 && section.size != size) - || (section.off != -1 && section.off != offset)) { - throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type)); - } - - section.size = size; - section.off = offset; - - if (previous != null && previous.off > section.off) { - throw new DexException("Map is unsorted at " + previous + ", " + section); - } - - previous = section; - } - Arrays.sort(sections); - } - - public void computeSizesFromOffsets() { - int end = dataOff + dataSize; - for (int i = sections.length - 1; i >= 0; i--) { - Section section = sections[i]; - if (section.off == -1) { - continue; - } - if (section.off > end) { - throw new DexException("Map is unsorted at " + section); - } - section.byteCount = end - section.off; - end = section.off; - } - } - - private Section getSection(short type) { - for (Section section : sections) { - if (section.type == type) { - return section; - } - } - throw new IllegalArgumentException("No such map item: " + type); - } - - public void writeHeader(Dex.Section out, int api) throws IOException { - out.write(DexFormat.apiToMagic(api).getBytes("UTF-8")); - out.writeInt(checksum); - out.write(signature); - out.writeInt(fileSize); - out.writeInt(SizeOf.HEADER_ITEM); - out.writeInt(DexFormat.ENDIAN_TAG); - out.writeInt(linkSize); - out.writeInt(linkOff); - out.writeInt(mapList.off); - out.writeInt(stringIds.size); - out.writeInt(stringIds.off); - out.writeInt(typeIds.size); - out.writeInt(typeIds.off); - out.writeInt(protoIds.size); - out.writeInt(protoIds.off); - out.writeInt(fieldIds.size); - out.writeInt(fieldIds.off); - out.writeInt(methodIds.size); - out.writeInt(methodIds.off); - out.writeInt(classDefs.size); - out.writeInt(classDefs.off); - out.writeInt(dataSize); - out.writeInt(dataOff); - } - - public void writeMap(Dex.Section out) throws IOException { - int count = 0; - for (Section section : sections) { - if (section.exists()) { - count++; - } - } - - out.writeInt(count); - for (Section section : sections) { - if (section.exists()) { - out.writeShort(section.type); - out.writeShort((short) 0); - out.writeInt(section.size); - out.writeInt(section.off); - } - } - } - - public static class Section implements Comparable

{ - public final short type; - public int size = 0; - public int off = -1; - public int byteCount = 0; - - public Section(int type) { - this.type = (short) type; - } - - public boolean exists() { - return size > 0; - } - - public int compareTo(Section section) { - if (off != section.off) { - return off < section.off ? -1 : 1; - } - return 0; - } - - @Override public String toString() { - return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size); - } - } -} diff --git a/dex/src/main/java/com/android/dex/TypeList.java b/dex/src/main/java/com/android/dex/TypeList.java deleted file mode 100644 index 123e82c9a..000000000 --- a/dex/src/main/java/com/android/dex/TypeList.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.util.Unsigned; - -public final class TypeList implements Comparable { - - public static final TypeList EMPTY = new TypeList(null, Dex.EMPTY_SHORT_ARRAY); - - private final Dex dex; - private final short[] types; - - public TypeList(Dex dex, short[] types) { - this.dex = dex; - this.types = types; - } - - public short[] getTypes() { - return types; - } - - @Override public int compareTo(TypeList other) { - for (int i = 0; i < types.length && i < other.types.length; i++) { - if (types[i] != other.types[i]) { - return Unsigned.compare(types[i], other.types[i]); - } - } - return Unsigned.compare(types.length, other.types.length); - } - - @Override public String toString() { - StringBuilder result = new StringBuilder(); - result.append("("); - for (int i = 0, typesLength = types.length; i < typesLength; i++) { - result.append(dex != null ? dex.typeNames().get(types[i]) : types[i]); - } - result.append(")"); - return result.toString(); - } -} diff --git a/dex/src/main/java/com/android/dex/util/ByteArrayByteInput.java b/dex/src/main/java/com/android/dex/util/ByteArrayByteInput.java deleted file mode 100644 index 889a936c5..000000000 --- a/dex/src/main/java/com/android/dex/util/ByteArrayByteInput.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex.util; - -public final class ByteArrayByteInput implements ByteInput { - - private final byte[] bytes; - private int position; - - public ByteArrayByteInput(byte... bytes) { - this.bytes = bytes; - } - - @Override public byte readByte() { - return bytes[position++]; - } -} diff --git a/dex/src/main/java/com/android/dex/util/ByteInput.java b/dex/src/main/java/com/android/dex/util/ByteInput.java deleted file mode 100644 index f1a719614..000000000 --- a/dex/src/main/java/com/android/dex/util/ByteInput.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex.util; - -/** - * A byte source. - */ -public interface ByteInput { - - /** - * Returns a byte. - * - * @throws IndexOutOfBoundsException if all bytes have been read. - */ - byte readByte(); -} diff --git a/dex/src/main/java/com/android/dex/util/ByteOutput.java b/dex/src/main/java/com/android/dex/util/ByteOutput.java deleted file mode 100644 index eb77040ec..000000000 --- a/dex/src/main/java/com/android/dex/util/ByteOutput.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex.util; - -/** - * A byte sink. - */ -public interface ByteOutput { - - /** - * Writes a byte. - * - * @throws IndexOutOfBoundsException if all bytes have been written. - */ - void writeByte(int i); -} diff --git a/dex/src/main/java/com/android/dex/util/ExceptionWithContext.java b/dex/src/main/java/com/android/dex/util/ExceptionWithContext.java deleted file mode 100644 index 5dfd95474..000000000 --- a/dex/src/main/java/com/android/dex/util/ExceptionWithContext.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -package com.android.dex.util; - -import java.io.PrintStream; -import java.io.PrintWriter; - -/** - * Exception which carries around structured context. - */ -public class ExceptionWithContext extends RuntimeException { - /** {@code non-null;} human-oriented context of the exception */ - private StringBuffer context; - - /** - * Augments the given exception with the given context, and return the - * result. The result is either the given exception if it was an - * {@link ExceptionWithContext}, or a newly-constructed exception if it - * was not. - * - * @param ex {@code non-null;} the exception to augment - * @param str {@code non-null;} context to add - * @return {@code non-null;} an appropriate instance - */ - public static ExceptionWithContext withContext(Throwable ex, String str) { - ExceptionWithContext ewc; - - if (ex instanceof ExceptionWithContext) { - ewc = (ExceptionWithContext) ex; - } else { - ewc = new ExceptionWithContext(ex); - } - - ewc.addContext(str); - return ewc; - } - - /** - * Constructs an instance. - * - * @param message human-oriented message - */ - public ExceptionWithContext(String message) { - this(message, null); - } - - /** - * Constructs an instance. - * - * @param cause {@code null-ok;} exception that caused this one - */ - public ExceptionWithContext(Throwable cause) { - this(null, cause); - } - - /** - * Constructs an instance. - * - * @param message human-oriented message - * @param cause {@code null-ok;} exception that caused this one - */ - public ExceptionWithContext(String message, Throwable cause) { - super((message != null) ? message : - (cause != null) ? cause.getMessage() : null, - cause); - - if (cause instanceof ExceptionWithContext) { - String ctx = ((ExceptionWithContext) cause).context.toString(); - context = new StringBuffer(ctx.length() + 200); - context.append(ctx); - } else { - context = new StringBuffer(200); - } - } - - /** {@inheritDoc} */ - @Override - public void printStackTrace(PrintStream out) { - super.printStackTrace(out); - out.println(context); - } - - /** {@inheritDoc} */ - @Override - public void printStackTrace(PrintWriter out) { - super.printStackTrace(out); - out.println(context); - } - - /** - * Adds a line of context to this instance. - * - * @param str {@code non-null;} new context - */ - public void addContext(String str) { - if (str == null) { - throw new NullPointerException("str == null"); - } - - context.append(str); - if (!str.endsWith("\n")) { - context.append('\n'); - } - } - - /** - * Gets the context. - * - * @return {@code non-null;} the context - */ - public String getContext() { - return context.toString(); - } - - /** - * Prints the message and context. - * - * @param out {@code non-null;} where to print to - */ - public void printContext(PrintStream out) { - out.println(getMessage()); - out.print(context); - } - - /** - * Prints the message and context. - * - * @param out {@code non-null;} where to print to - */ - public void printContext(PrintWriter out) { - out.println(getMessage()); - out.print(context); - } -} diff --git a/dex/src/main/java/com/android/dex/util/FileUtils.java b/dex/src/main/java/com/android/dex/util/FileUtils.java deleted file mode 100644 index 4cea95c59..000000000 --- a/dex/src/main/java/com/android/dex/util/FileUtils.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -package com.android.dex.util; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; - -/** - * File I/O utilities. - */ -public final class FileUtils { - private FileUtils() { - } - - /** - * Reads the named file, translating {@link IOException} to a - * {@link RuntimeException} of some sort. - * - * @param fileName {@code non-null;} name of the file to read - * @return {@code non-null;} contents of the file - */ - public static byte[] readFile(String fileName) { - File file = new File(fileName); - return readFile(file); - } - - /** - * Reads the given file, translating {@link IOException} to a - * {@link RuntimeException} of some sort. - * - * @param file {@code non-null;} the file to read - * @return {@code non-null;} contents of the file - */ - public static byte[] readFile(File file) { - if (!file.exists()) { - throw new RuntimeException(file + ": file not found"); - } - - if (!file.isFile()) { - throw new RuntimeException(file + ": not a file"); - } - - if (!file.canRead()) { - throw new RuntimeException(file + ": file not readable"); - } - - long longLength = file.length(); - int length = (int) longLength; - if (length != longLength) { - throw new RuntimeException(file + ": file too long"); - } - - byte[] result = new byte[length]; - - try { - FileInputStream in = new FileInputStream(file); - int at = 0; - while (length > 0) { - int amt = in.read(result, at, length); - if (amt == -1) { - throw new RuntimeException(file + ": unexpected EOF"); - } - at += amt; - length -= amt; - } - in.close(); - } catch (IOException ex) { - throw new RuntimeException(file + ": trouble reading", ex); - } - - return result; - } - - /** - * Returns true if {@code fileName} names a .zip, .jar, or .apk. - */ - public static boolean hasArchiveSuffix(String fileName) { - return fileName.endsWith(".zip") - || fileName.endsWith(".jar") - || fileName.endsWith(".apk"); - } -} diff --git a/dex/src/main/java/com/android/dex/util/Unsigned.java b/dex/src/main/java/com/android/dex/util/Unsigned.java deleted file mode 100644 index cb50d0a40..000000000 --- a/dex/src/main/java/com/android/dex/util/Unsigned.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex.util; - -/** - * Unsigned arithmetic over Java's signed types. - */ -public final class Unsigned { - private Unsigned() {} - - public static int compare(short ushortA, short ushortB) { - if (ushortA == ushortB) { - return 0; - } - int a = ushortA & 0xFFFF; - int b = ushortB & 0xFFFF; - return a < b ? -1 : 1; - } - - public static int compare(int uintA, int uintB) { - if (uintA == uintB) { - return 0; - } - long a = uintA & 0xFFFFFFFFL; - long b = uintB & 0xFFFFFFFFL; - return a < b ? -1 : 1; - } -} diff --git a/dex/src/test/java/com/android/dex/EncodedValueReaderTest.java b/dex/src/test/java/com/android/dex/EncodedValueReaderTest.java deleted file mode 100644 index a4ca37672..000000000 --- a/dex/src/test/java/com/android/dex/EncodedValueReaderTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package com.android.dex; - -import com.android.dex.util.ByteArrayByteInput; -import junit.framework.TestCase; - -public final class EncodedValueReaderTest extends TestCase { - - public void testReadByte() { - assertEquals((byte) 0x80, readerOf(0, 0x80).readByte()); - assertEquals((byte) 0xff, readerOf(0, 0xff).readByte()); - assertEquals((byte) 0x00, readerOf(0, 0x00).readByte()); - assertEquals((byte) 0x01, readerOf(0, 0x01).readByte()); - assertEquals((byte) 0x7f, readerOf(0, 0x7f).readByte()); - } - - public void testReadShort() { - assertEquals((short) 0x8000, readerOf(34, 0x00, 0x80).readShort()); - assertEquals((short) 0, readerOf( 2, 0x00).readShort()); - assertEquals((short) 0xab, readerOf(34, 0xab, 0x00).readShort()); - assertEquals((short) 0xabcd, readerOf(34, 0xcd, 0xab).readShort()); - assertEquals((short) 0x7FFF, readerOf(34, 0xff, 0x7f).readShort()); - } - - public void testReadInt() { - assertEquals(0x80000000, readerOf(100, 0x00, 0x00, 0x00, 0x80).readInt()); - assertEquals( 0x00, readerOf( 4, 0x00).readInt()); - assertEquals( 0xab, readerOf( 36, 0xab, 0x00).readInt()); - assertEquals( 0xabcd, readerOf( 68, 0xcd, 0xab, 0x00).readInt()); - assertEquals( 0xabcdef, readerOf(100, 0xef, 0xcd, 0xab, 0x00).readInt()); - assertEquals(0xabcdef01, readerOf(100, 0x01, 0xef, 0xcd, 0xab).readInt()); - assertEquals(0x7fffffff, readerOf(100, 0xff, 0xff, 0xff, 127).readInt()); - } - - public void testReadLong() { - assertEquals(0x8000000000000000L, readerOf( -26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80).readLong()); - assertEquals( 0x00L, readerOf( 6, 0x00).readLong()); - assertEquals( 0xabL, readerOf( 38, 0xab, 0x00).readLong()); - assertEquals( 0xabcdL, readerOf( 70, 0xcd, 0xab, 0x00).readLong()); - assertEquals( 0xabcdefL, readerOf( 102, 0xef, 0xcd, 0xab, 0x00).readLong()); - assertEquals( 0xabcdef01L, readerOf(-122, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong()); - assertEquals( 0xabcdef0123L, readerOf( -90, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong()); - assertEquals( 0xabcdef012345L, readerOf( -58, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong()); - assertEquals( 0xabcdef01234567L, readerOf( -26, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong()); - assertEquals(0xabcdef0123456789L, readerOf( -26, 0x89, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab).readLong()); - assertEquals(0x7fffffffffffffffL, readerOf( -26, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f).readLong()); - } - - public void testReadFloat() { - assertEquals(Float.NEGATIVE_INFINITY, readerOf(48, -128, -1).readFloat()); - assertEquals(Float.POSITIVE_INFINITY, readerOf(48, -128, 127).readFloat()); - assertEquals(Float.NaN, readerOf(48, -64, 127).readFloat()); - assertEquals(-0.0f, readerOf(16, -128).readFloat()); - assertEquals(0.0f, readerOf(16, 0).readFloat()); - assertEquals(0.5f, readerOf(16, 63).readFloat()); - assertEquals(1f, readerOf(48, -128, 63).readFloat()); - assertEquals(1.0E06f, readerOf(80, 36, 116, 73).readFloat()); - assertEquals(1.0E12f, readerOf(112, -91, -44, 104, 83).readFloat()); - } - - public void testReadDouble() { - assertEquals(Double.NEGATIVE_INFINITY, readerOf(49, -16, -1).readDouble()); - assertEquals(Double.POSITIVE_INFINITY, readerOf(49, -16, 127).readDouble()); - assertEquals(Double.NaN, readerOf(49, -8, 127).readDouble()); - assertEquals(-0.0, readerOf(17, -128).readDouble()); - assertEquals(0.0, readerOf(17, 0).readDouble()); - assertEquals(0.5, readerOf(49, -32, 63).readDouble()); - assertEquals(1.0, readerOf(49, -16, 63).readDouble()); - assertEquals(1.0E06, readerOf(113, -128, -124, 46, 65).readDouble()); - assertEquals(1.0E12, readerOf(-111, -94, -108, 26, 109, 66).readDouble()); - assertEquals(1.0E24, readerOf(-15, -76, -99, -39, 121, 67, 120, -22, 68).readDouble()); - } - - public void testReadChar() { - assertEquals('\u0000', readerOf( 3, 0x00).readChar()); - assertEquals('\u00ab', readerOf( 3, 0xab).readChar()); - assertEquals('\uabcd', readerOf(35, 0xcd, 0xab).readChar()); - assertEquals('\uffff', readerOf(35, 0xff, 0xff).readChar()); - } - - public void testReadBoolean() { - assertEquals(true, readerOf(63).readBoolean()); - assertEquals(false, readerOf(31).readBoolean()); - } - - public void testReadNull() { - readerOf(30).readNull(); - } - - public void testReadReference() { - assertEquals( 0xab, readerOf(0x17, 0xab).readString()); - assertEquals( 0xabcd, readerOf(0x37, 0xcd, 0xab).readString()); - assertEquals( 0xabcdef, readerOf(0x57, 0xef, 0xcd, 0xab).readString()); - assertEquals(0xabcdef01, readerOf(0x77, 0x01, 0xef, 0xcd, 0xab).readString()); - } - - public void testReadWrongType() { - try { - readerOf(0x17, 0xab).readField(); - fail(); - } catch (IllegalStateException expected) { - } - } - - private EncodedValueReader readerOf(int... bytes) { - byte[] data = new byte[bytes.length]; - for (int i = 0; i < bytes.length; i++) { - data[i] = (byte) bytes[i]; - } - return new EncodedValueReader(new ByteArrayByteInput(data)); - } -} diff --git a/dom/src/test/java/org/w3c/domts/JUnitTestCaseAdapter.java b/dom/src/test/java/org/w3c/domts/JUnitTestCaseAdapter.java index 711079236..8d964e47f 100644 --- a/dom/src/test/java/org/w3c/domts/JUnitTestCaseAdapter.java +++ b/dom/src/test/java/org/w3c/domts/JUnitTestCaseAdapter.java @@ -44,7 +44,7 @@ public JUnitTestCaseAdapter(DOMTestCase test) { test.setFramework(this); this.test = test; } -//BEGIN android-added +//BEGIN Android-added public JUnitTestCaseAdapter() { } @@ -150,9 +150,9 @@ public void setName(String name) { } } } -//END android-added +//END Android-added protected void runTest() throws Throwable { - //BEGIN android-added + //BEGIN Android-added if (failed) { if (errorMessage != null) { fail(errorMessage); @@ -160,7 +160,7 @@ protected void runTest() throws Throwable { fail("init failed"); } } - //END android-added + //END Android-added test.runTest(); int mutationCount = test.getMutationCount(); if (mutationCount != 0) { diff --git a/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue1.java b/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue1.java index f3484da4c..5f137a316 100644 --- a/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue1.java +++ b/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue1.java @@ -69,7 +69,7 @@ public void runTest() throws Throwable { attributes = testNode.getAttributes(); titleAttr = (Attr) attributes.getNamedItem("class"); value = titleAttr.getValue(); - assertEquals("attrValue1", "Y\u03b1", value); // android-changed: GREEK LOWER CASE ALPHA + assertEquals("attrValue1", "Y\u03b1", value); // Android-changed: GREEK LOWER CASE ALPHA } /** * Gets URI that identifies the test. diff --git a/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue2.java b/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue2.java index 814b69341..c2bf30c84 100644 --- a/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue2.java +++ b/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue2.java @@ -89,7 +89,7 @@ public void runTest() throws Throwable { firstChild = titleAttr.getFirstChild(); retval = titleAttr.insertBefore(alphaRef, firstChild); value = titleAttr.getValue(); - assertEquals("attrValue1", "\u03b1Y\u03b1", value); // android-changed: GREEK LOWER CASE ALPHA + assertEquals("attrValue1", "\u03b1Y\u03b1", value); // Android-changed: GREEK LOWER CASE ALPHA } } diff --git a/dom/src/test/java/org/w3c/domts/level1/core/hc_attrspecifiedvaluechanged.java b/dom/src/test/java/org/w3c/domts/level1/core/hc_attrspecifiedvaluechanged.java index 8ba4c578b..01ce038ee 100644 --- a/dom/src/test/java/org/w3c/domts/level1/core/hc_attrspecifiedvaluechanged.java +++ b/dom/src/test/java/org/w3c/domts/level1/core/hc_attrspecifiedvaluechanged.java @@ -71,7 +71,7 @@ public void runTest() throws Throwable { doc = (Document) load("hc_staff", true); addressList = doc.getElementsByTagName("acronym"); testNode = addressList.item(2); - ((Element) /*Node */testNode).setAttribute("class", "Y\u03b1"); // android-changed: GREEK LOWER CASE ALPHA + ((Element) /*Node */testNode).setAttribute("class", "Y\u03b1"); // Android-changed: GREEK LOWER CASE ALPHA attributes = testNode.getAttributes(); streetAttr = (Attr) attributes.getNamedItem("class"); state = streetAttr.getSpecified(); diff --git a/dom/src/test/java/org/w3c/domts/level1/core/hc_namednodemapinuseattributeerr.java b/dom/src/test/java/org/w3c/domts/level1/core/hc_namednodemapinuseattributeerr.java index 36dc3f81b..fcb4981c8 100644 --- a/dom/src/test/java/org/w3c/domts/level1/core/hc_namednodemapinuseattributeerr.java +++ b/dom/src/test/java/org/w3c/domts/level1/core/hc_namednodemapinuseattributeerr.java @@ -75,7 +75,7 @@ public void runTest() throws Throwable { elementList = doc.getElementsByTagName("acronym"); firstNode = (Element) elementList.item(0); domesticAttr = doc.createAttribute("title"); - domesticAttr.setValue("Y\u03b1"); // android-changed: GREEK LOWER CASE ALPHA + domesticAttr.setValue("Y\u03b1"); // Android-changed: GREEK LOWER CASE ALPHA setAttr = firstNode.setAttributeNode(domesticAttr); elementList = doc.getElementsByTagName("acronym"); testNode = elementList.item(2); diff --git a/dom/src/test/java/org/w3c/domts/level1/core/hc_textparseintolistofelements.java b/dom/src/test/java/org/w3c/domts/level1/core/hc_textparseintolistofelements.java index 2a10501f2..3364a14d7 100644 --- a/dom/src/test/java/org/w3c/domts/level1/core/hc_textparseintolistofelements.java +++ b/dom/src/test/java/org/w3c/domts/level1/core/hc_textparseintolistofelements.java @@ -72,13 +72,13 @@ public void runTest() throws Throwable { java.util.List result = new java.util.ArrayList(); java.util.List expectedNormal = new java.util.ArrayList(); - expectedNormal.add("\u03b2"); // android-changed: GREEK LOWER CASE BETA + expectedNormal.add("\u03b2"); // Android-changed: GREEK LOWER CASE BETA expectedNormal.add(" Dallas, "); - expectedNormal.add("\u03b3"); // android-changed: GREEK LOWER CASE GAMMA + expectedNormal.add("\u03b3"); // Android-changed: GREEK LOWER CASE GAMMA expectedNormal.add("\n 98554"); java.util.List expectedExpanded = new java.util.ArrayList(); - expectedExpanded.add("\u03b2 Dallas, \u03b3\n 98554"); // android-changed: GREEK LOWER CASE BETA, GREEK LOWER CASE GAMMA + expectedExpanded.add("\u03b2 Dallas, \u03b3\n 98554"); // Android-changed: GREEK LOWER CASE BETA, GREEK LOWER CASE GAMMA doc = (Document) load("hc_staff", false); elementList = doc.getElementsByTagName("acronym"); diff --git a/dom/src/test/java/org/w3c/domts/level2/core/documentcreateattributeNS04.java b/dom/src/test/java/org/w3c/domts/level2/core/documentcreateattributeNS04.java index bae9800dc..4ec52a202 100644 --- a/dom/src/test/java/org/w3c/domts/level2/core/documentcreateattributeNS04.java +++ b/dom/src/test/java/org/w3c/domts/level2/core/documentcreateattributeNS04.java @@ -92,14 +92,14 @@ public void runTest() throws Throwable { qualifiedName = (String) qualifiedNames.get(indexN1004E); { - // BEGIN android-changed + // BEGIN Android-changed // Our exception priorities differ from the spec try { attribute = doc.createAttributeNS(namespaceURI, qualifiedName); fail("documentcreateattributeNS04"); } catch (DOMException expected) { } - // END android-changed + // END Android-changed } } } diff --git a/dom/src/test/java/org/w3c/domts/level2/core/setAttributeNS02.java b/dom/src/test/java/org/w3c/domts/level2/core/setAttributeNS02.java index 9a83561bd..29ed3049e 100644 --- a/dom/src/test/java/org/w3c/domts/level2/core/setAttributeNS02.java +++ b/dom/src/test/java/org/w3c/domts/level2/core/setAttributeNS02.java @@ -75,14 +75,14 @@ public void runTest() throws Throwable { testAddr = elementList.item(0); { - // BEGIN android-changed + // BEGIN Android-changed // Our exception priorities differ from the spec try { ((Element) /*Node */testAddr).setAttributeNS(namespaceURI, qualifiedName, "newValue"); fail("throw_NAMESPACE_ERR"); } catch (DOMException ex) { } - // END android-changed + // END Android-changed } } /** diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt index 5dc7ad8f5..cd24094fd 100644 --- a/expectations/brokentests.txt +++ b/expectations/brokentests.txt @@ -45,37 +45,19 @@ description: "Some tests depend on ICU data, which has changed. Others make assumptions about floating point rounding", result: EXEC_FAILED, names: [ - "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_BigDecimalExceptionOrder", "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_DateTimeConversion", - "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatConversionE", - "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatConversionF", - "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatConversionG", - "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatDoubleBigDecimalExceptionOrder", "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_GeneralConversionOther", "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_LineSeparator", - "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_Percent", - "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_Width" + "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_Percent" ] }, { description: "(Needs investigation) Some tests make assertions that don't make sense, others use broken port allocation logic.", result: EXEC_FAILED, names: [ - "org.apache.harmony.tests.java.net.Inet6AddressTest#test_getByNameLjava_lang_String", - "org.apache.harmony.tests.java.net.InetAddressTest#test_getByNameLjava_lang_String", "org.apache.harmony.tests.java.net.InetAddressTest#test_isReachableLjava_net_NetworkInterfaceII_loopbackInterface" ] }, -{ - description: "(Needs investigation) Test failures from the harmony import of external/apache-harmony/archive", - bug: 12189307, - result: EXEC_FAILED, - names: [ - "org.apache.harmony.tests.java.util.jar.ManifestTest#testNul", - "org.apache.harmony.tests.java.util.jar.ManifestTest#testRead", - "org.apache.harmony.tests.java.util.jar.ManifestTest#testStreamConstructor" - ] -}, { description: "Potentially flakey because they rely on a specific local TCP port being free.", result: EXEC_FAILED, @@ -122,13 +104,6 @@ "org.apache.harmony.tests.api.javax.security.cert.X509CertificateTest#testVerifyPublicKeyString" ] }, -{ - description: "Suffers from side effect of other, currently unknown test", - result: EXEC_FAILED, - names: [ - "org.apache.harmony.luni.tests.internal.net.www.protocol.http.HttpURLConnectionTest#testProxyAuthorization" - ] -}, { description: "Support_TestWebServer requires isolation.", result: EXEC_FAILED, diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt index 396b864a9..1f0ef3269 100644 --- a/expectations/knownfailures.txt +++ b/expectations/knownfailures.txt @@ -18,11 +18,6 @@ name: "org.apache.harmony.crypto.tests.javax.crypto.func.KeyAgreementFunctionalTest#test_KeyAgreement", bug: 3473300 }, -{ - description: "RandomAccessFile missing finalizer", - name: "libcore.java.io.RandomAccessFileTest#testRandomAccessFileHasCleanupFinalizer", - bug: 3015023 -}, { description: "ICU seems to treat unknown and invalid locales differently", name: "libcore.java.text.DateFormatSymbolsTest#test_getInstance_unknown_locale", @@ -69,12 +64,6 @@ ], bug: 2702411 }, -{ - description: "Runtime.getRuntime().traceMethodCalls(true) doesn't return on the host, fails in CTS", - bug: 3447964, - result: EXEC_FAILED, - name: "libcore.java.lang.OldRuntimeTest#test_traceMethodCalls" -}, { description: "It's not allowed to pass null as parent class loader to a new ClassLoader anymore. Maybe we need to change URLClassLoader to allow this? It's not specified.", @@ -1304,19 +1293,6 @@ result: EXEC_FAILED, name: "org.apache.harmony.tests.java.lang.MathTest#test_powDD" }, -{ - description: "Known failures in PropertiesTest: We don't deal with comments in store()", - bug: 11686302, - result: EXEC_FAILED, - names: [ - "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario0", - "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario1", - "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario2", - "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario3", - "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario9", - "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario11" - ] -}, { description: "Known failures in URLTest and URLDecoderTest", bug: 11686814, @@ -1423,27 +1399,11 @@ "com.android.org.apache.harmony.beans.tests.java.beans.PropertyChangeSupportTest#testSerializationCompatibility" ] }, -{ - description: "Known precision issue in DecimalFormat", - bug: 17656132, - names: [ - "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatDouble_bug17656132", - "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatDouble_roundingProblemCases" - ] -}, { description: "Known failure in GregorianCalendarTest", bug: 12778197, name: "org.apache.harmony.tests.java.util.GregorianCalendarTest#test_computeTime" }, -{ - description: "OkHttp tests require SOCKS 5 support. Android PlainSocketImpl implements SOCKS 4", - bug: 96926, - names: [ - "com.squareup.okhttp.SocksProxyTest#proxy", - "com.squareup.okhttp.SocksProxyTest#proxySelector" - ] -}, { description: "OkHttp tests that fail on Wear devices due to a lack of memory", bug: 20055487, @@ -1454,7 +1414,7 @@ }, { description: "libcore.java.text.DecimalFormatSymbolsTest#test_getInstance_unknown_or_invalid_locale assumes fallback to locale other than en_US_POSIX.", - bug: 17374604, + bug: 17422813, names: [ "libcore.java.text.DecimalFormatSymbolsTest#test_getInstance_unknown_or_invalid_locale" ] @@ -1501,15 +1461,6 @@ "libcore.io.OsTest#test_PacketSocketAddress" ] }, -{ - description: "Need to rewrite tests for the client-side of renegotiation", - bug: 21876068, - result: EXEC_FAILED, - names: [ - "com.android.org.conscrypt.NativeCryptoTest#test_SSL_renegotiate", - "com.android.org.conscrypt.NativeCryptoTest#test_SSL_do_handshake_clientCertificateRequested_throws_after_renegotiate" - ] -}, { description: "Failures in OldSHA1PRNGSecureRandomTest", result: EXEC_FAILED, @@ -1552,5 +1503,13 @@ names: [ "com.android.org.apache.harmony.luni.tests.java.net.URLClassLoaderImplTest#test_Constructor$Ljava_net_URLLjava_lang_ClassLoaderLjava_net_URLStreamHandlerFactory" ] +}, +{ + description: "Waiting for ICU 58 to be merged", + bug: 31516121, + result: EXEC_FAILED, + names: [ + "libcore.java.text.OldBidiTest#testUnicode9EmojisAreLtrNeutral" + ] } ] diff --git a/expectations/virtualdeviceknownfailures.txt b/expectations/virtualdeviceknownfailures.txt new file mode 100644 index 000000000..54d0d64f0 --- /dev/null +++ b/expectations/virtualdeviceknownfailures.txt @@ -0,0 +1,16 @@ +/* + * List of test cases known to fail on a virtual device. + */ +[ +{ + description: "IPv6 connectivity not yet supported in virtual device testing infra", + result: EXEC_FAILED, + name: "libcore.java.net.SocketTest#testSocketTestAllAddresses", + bug: 30965313 +}, +{ + description: "Virtual devices do not implement the SELinux policy (forbid hard link) asserted by this test", + name: "libcore.java.nio.file.Files2Test#test_createLink", + bug: 35670953 +} +] diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/CharArrayReaderTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/CharArrayReaderTest.java index 88653327a..6a1ba7135 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/CharArrayReaderTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/CharArrayReaderTest.java @@ -167,6 +167,15 @@ public void test_skipJ() throws IOException { assertEquals("Skip skipped wrong chars", 'W', cr.read()); } + /** + * java.io.CharArrayReader#skip(long) overflow + */ + public void test_skipOverflow() throws IOException { + cr = new CharArrayReader(hw); + assertEquals(1L, cr.skip(1L)); + assertEquals(hw.length - 1, cr.skip(Long.MAX_VALUE)); + } + /** * Tears down the fixture, for example, close a network connection. This * method is called after a test is executed. diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/DataInputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/DataInputStreamTest.java index 67a4e5f54..7d890179e 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/DataInputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/DataInputStreamTest.java @@ -23,6 +23,7 @@ import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; +import java.io.InputStream; public class DataInputStreamTest extends junit.framework.TestCase { @@ -564,6 +565,54 @@ public void test_skipBytesI() throws IOException { + skipped, skipped == fileString.length()); } + // b/30268192 : Some apps rely on the exact calls that + // DataInputStream makes on the wrapped InputStream. This + // test is to prevent *unintentional* regressions but may + // change in future releases. + public void test_readShortUsesMultiByteRead() throws IOException { + ThrowExceptionOnSingleByteReadInputStream + is = new ThrowExceptionOnSingleByteReadInputStream(); + DataInputStream dis = new DataInputStream(is); + dis.readShort(); + is.assertMultiByteReadWasCalled(); + } + + // b/30268192 : Some apps rely on the exact calls that + // DataInputStream makes on the wrapped InputStream. This + // test is to prevent *unintentional* regressions but may + // change in future releases. + public void test_readCharUsesMultiByteRead() throws IOException { + ThrowExceptionOnSingleByteReadInputStream + is = new ThrowExceptionOnSingleByteReadInputStream(); + DataInputStream dis = new DataInputStream(is); + dis.readChar(); + is.assertMultiByteReadWasCalled(); + } + + // b/30268192 : Some apps rely on the exact calls that + // DataInputStream makes on the wrapped InputStream. This + // test is to prevent *unintentional* regressions but may + // change in future releases. + public void test_readIntUsesMultiByteRead() throws IOException { + ThrowExceptionOnSingleByteReadInputStream + is = new ThrowExceptionOnSingleByteReadInputStream(); + DataInputStream dis = new DataInputStream(is); + dis.readInt(); + is.assertMultiByteReadWasCalled(); + } + + // b/30268192 : Some apps rely on the exact calls that + // DataInputStream makes on the wrapped InputStream. This + // test is to prevent *unintentional* regressions but may + // change in future releases. + public void test_readUnsignedShortUsesMultiByteRead() throws IOException { + ThrowExceptionOnSingleByteReadInputStream + is = new ThrowExceptionOnSingleByteReadInputStream(); + DataInputStream dis = new DataInputStream(is); + dis.readUnsignedShort(); + is.assertMultiByteReadWasCalled(); + } + private void openDataInputStream() throws IOException { dis = new DataInputStream(new ByteArrayInputStream(bos.toByteArray())); } @@ -591,4 +640,27 @@ protected void tearDown() { } catch (Exception e) { } } + + public static class ThrowExceptionOnSingleByteReadInputStream extends InputStream { + + private boolean multiByteReadWasCalled = false; + + @Override + public int read() throws IOException { + fail("Should not call single byte read"); + return 0; + } + + @Override + public int read(byte[] b, int i, int j) throws IOException { + multiByteReadWasCalled = true; + return j; + } + + public void assertMultiByteReadWasCalled() { + if (!multiByteReadWasCalled) { + fail("read(byte[], int, int) was not called"); + } + } + } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileInputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileInputStreamTest.java index af54d4b89..4375afd01 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileInputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileInputStreamTest.java @@ -24,9 +24,14 @@ import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import junit.framework.TestCase; - -public class FileInputStreamTest extends TestCase { +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public class FileInputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); private String fileName; diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileOutputStreamTest.java index 11dd8b621..cc0d9539f 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileOutputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileOutputStreamTest.java @@ -25,10 +25,14 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -import junit.framework.TestCase; - -public class FileOutputStreamTest extends TestCase { +public class FileOutputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); private FileOutputStream fos; private FileInputStream fis; @@ -92,6 +96,7 @@ public void test_ConstructorLjava_lang_String() throws IOException { f = File.createTempFile("FileOutputStreamTest", "tst"); String fileName = f.getAbsolutePath(); fos = new FileOutputStream(fileName); + fos.close(); // Harmony 4012. fos = new FileOutputStream("/dev/null"); @@ -285,12 +290,13 @@ public void test_getChannel() throws IOException { // Regression for HARMONY-508 File tmpfile = File.createTempFile("FileOutputStream", "tmp"); tmpfile.deleteOnExit(); - FileOutputStream fos = new FileOutputStream(tmpfile); - fos.write(bytes); - fos.flush(); - fos.close(); - FileOutputStream f = new FileOutputStream(tmpfile, true); - assertEquals(10, f.getChannel().position()); + try (FileOutputStream fos = new FileOutputStream(tmpfile)) { + fos.write(bytes); + fos.flush(); + } + try (FileOutputStream f = new FileOutputStream(tmpfile, true)) { + assertEquals(10, f.getChannel().position()); + } } public void test_getChannel_Append() throws IOException { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java index 7ae46177d..bebeb6ec5 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java @@ -17,7 +17,7 @@ package org.apache.harmony.tests.java.io; -import junit.framework.TestCase; +import dalvik.system.VMRuntime; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; @@ -25,8 +25,10 @@ import java.io.ObjectStreamClass; import java.io.ObjectStreamField; import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import junit.framework.TestCase; public class ObjectStreamClassTest extends TestCase { @@ -221,20 +223,43 @@ public void test_lookupAnyLjava_lang_Class() { // http://b/28106822 public void testBug28106822() throws Exception { - Method getConstructorId = ObjectStreamClass.class.getDeclaredMethod( - "getConstructorId", Class.class); - getConstructorId.setAccessible(true); - - assertEquals(1189998819991197253L, getConstructorId.invoke(null, Object.class)); - assertEquals(1189998819991197253L, getConstructorId.invoke(null, String.class)); - - Method newInstance = ObjectStreamClass.class.getDeclaredMethod("newInstance", - Class.class, Long.TYPE); - newInstance.setAccessible(true); + int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion(); + try { + // Assert behavior up to 24 + VMRuntime.getRuntime().setTargetSdkVersion(24); + Method getConstructorId = ObjectStreamClass.class.getDeclaredMethod( + "getConstructorId", Class.class); + getConstructorId.setAccessible(true); + + assertEquals(1189998819991197253L, getConstructorId.invoke(null, Object.class)); + assertEquals(1189998819991197253L, getConstructorId.invoke(null, String.class)); + + Method newInstance = ObjectStreamClass.class.getDeclaredMethod("newInstance", + Class.class, Long.TYPE); + newInstance.setAccessible(true); + + Object obj = newInstance.invoke(null, String.class, 0 /* ignored */); + assertNotNull(obj); + assertTrue(obj instanceof String); + + // Assert behavior from API 25 + VMRuntime.getRuntime().setTargetSdkVersion(25); + try { + getConstructorId.invoke(null, Object.class); + fail(); + } catch (InvocationTargetException expected) { + assertTrue(expected.getCause() instanceof UnsupportedOperationException); + } + try { + newInstance.invoke(null, String.class, 0 /* ignored */); + fail(); + } catch (InvocationTargetException expected) { + assertTrue(expected.getCause() instanceof UnsupportedOperationException); + } - Object obj = newInstance.invoke(null, String.class, 0 /* ignored */); - assertNotNull(obj); - assertTrue(obj instanceof String); + } finally { + VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion); + } } // Class without method diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/RandomAccessFileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/RandomAccessFileTest.java index f6784fbf4..b6610d13f 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/RandomAccessFileTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/RandomAccessFileTest.java @@ -26,8 +26,14 @@ import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.NonWritableChannelException; - -public class RandomAccessFileTest extends junit.framework.TestCase { +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public class RandomAccessFileTest extends TestCaseWithRules { + @Rule + public TestRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule(); public String fileName; diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java index c5dd4f02f..40706b3a3 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java @@ -30,11 +30,13 @@ import java.text.MessageFormat; import java.text.NumberFormat; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; @@ -187,43 +189,17 @@ public void test_writeObject_Character() { } - public void test_writeObject_Collections_UnmodifiableCollection() { + public void test_writeObject_Collections_UnmodifiableCollection() throws Exception { // Test for method void // java.io.ObjectOutputStream.writeObject(java.util.Collections.UnmodifiableCollection) - Object objToSave = null; - Object objLoaded = null; + Collection objToSave = java.util.Collections.unmodifiableCollection(SET); + Collection objLoaded = (Collection) dumpAndReload(objToSave); - try { - objToSave = Collections.unmodifiableCollection(SET); - if (DEBUG) - System.out.println("Obj = " + objToSave); - objLoaded = dumpAndReload(objToSave); - - // Has to have worked - boolean equals; - equals = ((java.util.Collection) objToSave).size() == ((java.util.Collection) objLoaded) - .size(); - if (equals) { - java.util.Iterator iter1 = ((java.util.Collection) objToSave) - .iterator(), iter2 = ((java.util.Collection) objLoaded) - .iterator(); - while (iter1.hasNext()) - equals = equals && iter1.next().equals(iter2.next()); - } - assertTrue(MSG_TEST_FAILED + objToSave, equals); - } catch (IOException e) { - fail("IOException serializing " + objToSave + " : " - + e.getMessage()); - } catch (ClassNotFoundException e) { - fail("ClassNotFoundException reading Object type : " - + e.getMessage()); - } catch (Error err) { - System.out.println("Error when obj = " + objToSave); - // err.printStackTrace(); - throw err; - } + HashSet objToSaveElements = new HashSet<>(objToSave); + HashSet objLoadedElements = new HashSet<>(objLoaded); + assertEquals(objToSaveElements, objLoadedElements); } public void test_writeObject_Format() { @@ -1420,42 +1396,17 @@ public void test_writeObject_Long() { } - public void test_writeObject_Collections_SynchronizedCollection() { + public void test_writeObject_Collections_SynchronizedCollection() throws Exception { // Test for method void // java.io.ObjectOutputStream.writeObject(java.util.Collections.SynchronizedCollection) - Object objToSave = null; - Object objLoaded = null; + Collection objToSave = java.util.Collections.synchronizedCollection(SET); + Collection objLoaded = (Collection) dumpAndReload(objToSave); - try { - objToSave = java.util.Collections.synchronizedCollection(SET); - if (DEBUG) - System.out.println("Obj = " + objToSave); - objLoaded = dumpAndReload(objToSave); - - // Has to have worked - boolean equals; - equals = ((java.util.Collection) objToSave).size() == ((java.util.Collection) objLoaded) - .size(); - if (equals) { - java.util.Iterator iter1 = ((java.util.Collection) objToSave) - .iterator(), iter2 = ((java.util.Collection) objLoaded) - .iterator(); - while (iter1.hasNext()) - equals = equals && iter1.next().equals(iter2.next()); - } - assertTrue(MSG_TEST_FAILED + objToSave, equals); - } catch (IOException e) { - fail("Exception serializing " + objToSave + " : " + e.getMessage()); - } catch (ClassNotFoundException e) { - fail("ClassNotFoundException reading Object type: " - + e.getMessage()); - } catch (Error err) { - System.out.println("Error when obj = " + objToSave); - // err.printStackTrace(); - throw err; - } + HashSet objToSaveElements = new HashSet<>(objToSave); + HashSet objLoadedElements = new HashSet<>(objLoaded); + assertEquals(objToSaveElements, objLoadedElements); } public void test_writeObject_Random() { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/Character_UnicodeBlockTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/Character_UnicodeBlockTest.java index 792ee3dff..9a4a406e9 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/Character_UnicodeBlockTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/Character_UnicodeBlockTest.java @@ -4,9 +4,9 @@ * The ASF licenses this file to You 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. @@ -232,6 +232,14 @@ public void test_ofC() { assertEquals(Character.UnicodeBlock.SPECIALS, Character.UnicodeBlock.of((char) 0xfff0)); assertEquals(Character.UnicodeBlock.SPECIALS, Character.UnicodeBlock.of((char) 0xffff)); + // Blocks added in 1.8 + assertEquals(Character.UnicodeBlock.ARABIC_EXTENDED_A, Character.UnicodeBlock.of((char) 0x08a0)); + assertEquals(Character.UnicodeBlock.ARABIC_EXTENDED_A, Character.UnicodeBlock.of((char) 0x08ff)); + assertEquals(Character.UnicodeBlock.SUNDANESE_SUPPLEMENT, Character.UnicodeBlock.of((char) 0x1cc0)); + assertEquals(Character.UnicodeBlock.SUNDANESE_SUPPLEMENT, Character.UnicodeBlock.of((char) 0x1ccf)); + assertEquals(Character.UnicodeBlock.MEETEI_MAYEK_EXTENSIONS, Character.UnicodeBlock.of((char) 0xaae0)); + assertEquals(Character.UnicodeBlock.MEETEI_MAYEK_EXTENSIONS, Character.UnicodeBlock.of((char) 0xaaff)); + // Negative test: The range [0x0860, 0x08A0) is currently unassigned. assertEquals(null, Character.UnicodeBlock.of((char) 0x0860)); assertEquals(null, Character.UnicodeBlock.of((char) 0x089F)); @@ -489,6 +497,30 @@ public void test_ofI() { assertEquals(Character.UnicodeBlock.SUPPLEMENTARY_PRIVATE_USE_AREA_B, Character.UnicodeBlock.of(0x100000)); assertEquals(Character.UnicodeBlock.SUPPLEMENTARY_PRIVATE_USE_AREA_B, Character.UnicodeBlock.of(0x10ffff)); + // Blocks added in 1.8 + assertEquals(Character.UnicodeBlock.ARABIC_EXTENDED_A, Character.UnicodeBlock.of(0x08a0)); + assertEquals(Character.UnicodeBlock.ARABIC_EXTENDED_A, Character.UnicodeBlock.of(0x08ff)); + assertEquals(Character.UnicodeBlock.SUNDANESE_SUPPLEMENT, Character.UnicodeBlock.of(0x1cc0)); + assertEquals(Character.UnicodeBlock.SUNDANESE_SUPPLEMENT, Character.UnicodeBlock.of(0x1ccf)); + assertEquals(Character.UnicodeBlock.MEETEI_MAYEK_EXTENSIONS, Character.UnicodeBlock.of(0xaae0)); + assertEquals(Character.UnicodeBlock.MEETEI_MAYEK_EXTENSIONS, Character.UnicodeBlock.of(0xaaff)); + assertEquals(Character.UnicodeBlock.MEROITIC_HIEROGLYPHS, Character.UnicodeBlock.of(0x10980)); + assertEquals(Character.UnicodeBlock.MEROITIC_HIEROGLYPHS, Character.UnicodeBlock.of(0x1099f)); + assertEquals(Character.UnicodeBlock.MEROITIC_CURSIVE, Character.UnicodeBlock.of(0x109a0)); + assertEquals(Character.UnicodeBlock.MEROITIC_CURSIVE, Character.UnicodeBlock.of(0x109ff)); + assertEquals(Character.UnicodeBlock.SORA_SOMPENG, Character.UnicodeBlock.of(0x110d0)); + assertEquals(Character.UnicodeBlock.SORA_SOMPENG, Character.UnicodeBlock.of(0x110ff)); + assertEquals(Character.UnicodeBlock.CHAKMA, Character.UnicodeBlock.of(0x11100)); + assertEquals(Character.UnicodeBlock.CHAKMA, Character.UnicodeBlock.of(0x1114f)); + assertEquals(Character.UnicodeBlock.SHARADA, Character.UnicodeBlock.of(0x11180)); + assertEquals(Character.UnicodeBlock.SHARADA, Character.UnicodeBlock.of(0x111df)); + assertEquals(Character.UnicodeBlock.TAKRI, Character.UnicodeBlock.of(0x11680)); + assertEquals(Character.UnicodeBlock.TAKRI, Character.UnicodeBlock.of(0x116cf)); + assertEquals(Character.UnicodeBlock.MIAO, Character.UnicodeBlock.of(0x16f00)); + assertEquals(Character.UnicodeBlock.MIAO, Character.UnicodeBlock.of(0x16f9f)); + assertEquals(Character.UnicodeBlock.ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS, Character.UnicodeBlock.of(0x1ee00)); + assertEquals(Character.UnicodeBlock.ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS, Character.UnicodeBlock.of(0x1eeff)); + // Negative test: The range [0x0860, 0x08A0) is currently unassigned. assertEquals(null, Character.UnicodeBlock.of((char) 0x0860)); assertEquals(null, Character.UnicodeBlock.of((char) 0x089F)); @@ -793,6 +825,37 @@ public void test_forNameLjava_lang_String() { assertEquals(Character.UnicodeBlock.SUPPLEMENTARY_PRIVATE_USE_AREA_B, Character.UnicodeBlock.forName("SUPPLEMENTARY_PRIVATE_USE_AREA_B")); assertEquals(Character.UnicodeBlock.SUPPLEMENTARY_PRIVATE_USE_AREA_B, Character.UnicodeBlock.forName("Supplementary Private Use Area-B")); assertEquals(Character.UnicodeBlock.SUPPLEMENTARY_PRIVATE_USE_AREA_B, Character.UnicodeBlock.forName("SupplementaryPrivateUseArea-B")); + + // Blocks added in 1.8 + assertEquals(Character.UnicodeBlock.ARABIC_EXTENDED_A, Character.UnicodeBlock.forName("ARABIC_EXTENDED_A")); + assertEquals(Character.UnicodeBlock.ARABIC_EXTENDED_A, Character.UnicodeBlock.forName("arabic extended-A")); + assertEquals(Character.UnicodeBlock.ARABIC_EXTENDED_A, Character.UnicodeBlock.forName("ArabicExtended-A")); + assertEquals(Character.UnicodeBlock.SUNDANESE_SUPPLEMENT, Character.UnicodeBlock.forName("SUNDANESE_SUPPLEMENT")); + assertEquals(Character.UnicodeBlock.SUNDANESE_SUPPLEMENT, Character.UnicodeBlock.forName("Sundanese Supplement")); + assertEquals(Character.UnicodeBlock.SUNDANESE_SUPPLEMENT, Character.UnicodeBlock.forName("SundaneseSupplement")); + assertEquals(Character.UnicodeBlock.MEETEI_MAYEK_EXTENSIONS, Character.UnicodeBlock.forName("MEETEI_MAYEK_EXTENSIONS")); + assertEquals(Character.UnicodeBlock.MEETEI_MAYEK_EXTENSIONS, Character.UnicodeBlock.forName("MEETEI MAYEK EXTENSIONS")); + assertEquals(Character.UnicodeBlock.MEETEI_MAYEK_EXTENSIONS, Character.UnicodeBlock.forName("MeeteiMayekExtensions")); + assertEquals(Character.UnicodeBlock.MEROITIC_HIEROGLYPHS, Character.UnicodeBlock.forName("MEROITIC_HIEROGLYPHS")); + assertEquals(Character.UnicodeBlock.MEROITIC_HIEROGLYPHS, Character.UnicodeBlock.forName("MEROITIC HIEROGLYPHS")); + assertEquals(Character.UnicodeBlock.MEROITIC_HIEROGLYPHS, Character.UnicodeBlock.forName("MeroiticHieroglyphs")); + assertEquals(Character.UnicodeBlock.MEROITIC_CURSIVE, Character.UnicodeBlock.forName("MEROITIC_CURSIVE")); + assertEquals(Character.UnicodeBlock.MEROITIC_CURSIVE, Character.UnicodeBlock.forName("MEROITIC CURSIVE")); + assertEquals(Character.UnicodeBlock.MEROITIC_CURSIVE, Character.UnicodeBlock.forName("MeroiticCursive")); + assertEquals(Character.UnicodeBlock.SORA_SOMPENG, Character.UnicodeBlock.forName("SORA_SOMPENG")); + assertEquals(Character.UnicodeBlock.SORA_SOMPENG, Character.UnicodeBlock.forName("SORA SOMPENG")); + assertEquals(Character.UnicodeBlock.SORA_SOMPENG, Character.UnicodeBlock.forName("SoraSompeng")); + assertEquals(Character.UnicodeBlock.CHAKMA, Character.UnicodeBlock.forName("CHAKMA")); + assertEquals(Character.UnicodeBlock.SHARADA, Character.UnicodeBlock.forName("SHARADA")); + assertEquals(Character.UnicodeBlock.TAKRI, Character.UnicodeBlock.forName("TAKRI")); + assertEquals(Character.UnicodeBlock.MIAO, Character.UnicodeBlock.forName("MIAO")); + assertEquals(Character.UnicodeBlock.ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS, + Character.UnicodeBlock.forName("ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS")); + assertEquals(Character.UnicodeBlock.ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS, + Character.UnicodeBlock.forName("ARABIC MATHEMATICAL ALPHABETIC SYMBOLS")); + assertEquals(Character.UnicodeBlock.ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS, + Character.UnicodeBlock.forName("ArabicMathematicalAlphabeticSymbols")); + } public void test_forNameLjava_lang_StringExceptions() { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ClassTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ClassTest.java index 95dad9a21..5d97393bd 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ClassTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ClassTest.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.Serializable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -34,7 +36,9 @@ import java.security.Security; import java.util.Arrays; import java.util.List; +import java.util.TreeMap; import java.util.Vector; +import java.util.function.Function; public class ClassTest extends junit.framework.TestCase { @@ -545,27 +549,6 @@ public void test_newInstance() throws Exception { } } - /** - * java.lang.Class#toString() - */ - public void test_toString() throws ClassNotFoundException { - assertEquals("Class toString printed wrong value", - "int", int.class.toString()); - Class clazz = null; - clazz = Class.forName("[I"); - assertEquals("Class toString printed wrong value", - "class [I", clazz.toString()); - - clazz = Class.forName("java.lang.Object"); - assertEquals("Class toString printed wrong value", - "class java.lang.Object", clazz.toString()); - - clazz = Class.forName("[Ljava.lang.Object;"); - assertEquals("Class toString printed wrong value", - "class [Ljava.lang.Object;", clazz.toString()); - } - - // Regression Test for JIRA-2047 public void test_getResourceAsStream_withSharpChar() throws Exception { // Class.getResourceAsStream() requires a leading "/" for absolute paths. diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessBuilderTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessBuilderTest.java index 87cf88cbc..2fea31a7b 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessBuilderTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessBuilderTest.java @@ -4,9 +4,9 @@ * The ASF licenses this file to You 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. @@ -168,4 +168,13 @@ public void testStart() throws IOException { assertTrue(err.read(buf) > 0); } } + + public void testNullInCommand() { + ProcessBuilder pb = new ProcessBuilder("ls", "with\u0000inside"); + try { + pb.start(); + fail(); + } catch(IOException expected) {} + } + } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessManagerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessManagerTest.java index 9f7474a75..f5d416351 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessManagerTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessManagerTest.java @@ -20,7 +20,7 @@ import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -174,7 +174,7 @@ private static void stuff() { rt = null; } - InputStream in; + FileOutputStream out; public void testCloseNonStandardFds() throws IOException, InterruptedException { @@ -183,8 +183,9 @@ public void testCloseNonStandardFds() Process process = Runtime.getRuntime().exec(commands, null, null); int before = countLines(process); + File tmpFile = File.createTempFile("testCloseNonStandardFds", ".txt"); // Open a new fd. - this.in = new FileInputStream("/proc/version"); + this.out = new FileOutputStream(tmpFile); try { process = Runtime.getRuntime().exec(commands, null, null); @@ -193,7 +194,8 @@ public void testCloseNonStandardFds() // Assert that the new fd wasn't open in the second run. assertEquals(before, after); } finally { - this.in = null; + this.out.close(); + tmpFile.delete(); } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessTest.java index a5b6509d9..cf6f89ecd 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessTest.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import java.util.ArrayList; import libcore.io.Libcore; +import java.util.concurrent.TimeUnit; public class ProcessTest extends junit.framework.TestCase { // Test that failures to exec don't leave zombies lying around. @@ -140,4 +141,45 @@ public void test_destroy() throws Exception { process.destroy(); process.destroy(); } + + public void test_destroyForcibly() throws Exception { + String[] commands = { "sh", "-c", "sleep 3000"}; + Process process = Runtime.getRuntime().exec(commands, null, null); + assertNotNull(process.destroyForcibly()); + process.waitFor(); // destroy is asynchronous. + assertTrue(process.exitValue() != 0); + } + + public void test_isAlive() throws Exception { + String[] commands = { "sh", "-c", "sleep 3000"}; + Process process = Runtime.getRuntime().exec(commands, null, null); + assertTrue(process.isAlive()); + assertNotNull(process.destroyForcibly()); + process.waitFor(); // destroy is asynchronous. + assertFalse(process.isAlive()); + } + + public void test_waitForTimeout() throws Exception { + String[] commands = { "sh", "-c", "sleep 3000"}; + Process process = Runtime.getRuntime().exec(commands, null, null); + assertFalse(process.waitFor(0, TimeUnit.MICROSECONDS)); + assertTrue(process.isAlive()); + assertFalse(process.waitFor(500, TimeUnit.MICROSECONDS)); + assertTrue(process.isAlive()); + assertNotNull(process.destroyForcibly()); + assertTrue(process.waitFor(2, TimeUnit.SECONDS)); + assertFalse(process.isAlive()); + } + + public void test_waitForTimeout_NPE() throws Exception { + String[] commands = { "sh", "-c", "sleep 3000"}; + Process process = Runtime.getRuntime().exec(commands, null, null); + try { + process.waitFor(500, null); + fail(); + } catch(NullPointerException expected) {} + assertNotNull(process.destroyForcibly()); + process.waitFor(); + } + } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadLocalTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadLocalTest.java index 400ff01a3..dd03a8066 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadLocalTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadLocalTest.java @@ -18,6 +18,7 @@ package org.apache.harmony.tests.java.lang; import junit.framework.TestCase; +import java.util.concurrent.atomic.AtomicReference; public class ThreadLocalTest extends TestCase { @@ -147,4 +148,43 @@ public void run() { THREADVALUE.result); } + + /** + * java.lang.ThreadLocal#withInitial() + */ + public void test_withInitial() { + // The ThreadLocal has to run once for each thread that touches the + // ThreadLocal + final String INITIAL_VALUE = "'foo'"; + final String OTHER_VALUE = "'bar'"; + final ThreadLocal l1 = ThreadLocal.withInitial(() -> INITIAL_VALUE); + + assertSame(INITIAL_VALUE, l1.get()); + + l1.set(OTHER_VALUE); + assertSame(OTHER_VALUE, l1.get()); + + assertTrue("ThreadLocal's value should be " + OTHER_VALUE + + " but is " + l1.get(), l1.get() == OTHER_VALUE); + + AtomicReference threadValue = new AtomicReference(); + + Thread t = new Thread() { + @Override + public void run() { + threadValue.set(l1.get()); + } + }; + + // Wait for the other Thread assign what it observes as the value of the + // variable + t.start(); + try { + t.join(); + } catch (InterruptedException ie) { + fail("Interrupted!!"); + } + + assertSame(INITIAL_VALUE, threadValue.get()); + } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java index 5a80fde8b..34dd0fa56 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java @@ -49,7 +49,24 @@ public void test_get() { /** * java.lang.Runtime#gc() */ - public void test_gcInteraction() { + public void test_gcInteraction_Runtime() { + check_gcInteraction(() -> { Runtime.getRuntime().gc(); } ); + } + + /** + * Checks that the sequence {@link System#gc()}, {@link System#runFinalization()}} + * also has the effect as asserted for {@link Runtime#gc()} elsewhere. The + * conditions under which System.gc() results in a garbage collection are an + * implementation detail not guaranteed by documentation. + */ + public void test_gcInteraction_System() { + check_gcInteraction(() -> { + System.gc(); + System.runFinalization(); + } ); + } + + private void check_gcInteraction(Runnable gc) { class TestPhantomReference extends PhantomReference { public TestPhantomReference(T referent, ReferenceQueue q) { @@ -58,7 +75,7 @@ public TestPhantomReference(T referent, public boolean enqueue() { // Initiate another GC from inside enqueue() to // see if it causes any problems inside the VM. - Runtime.getRuntime().gc(); + gc.run(); return super.enqueue(); } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/GenericArrayTypeTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/GenericArrayTypeTest.java index 4888fd21f..58b15e0e0 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/GenericArrayTypeTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/GenericArrayTypeTest.java @@ -36,6 +36,9 @@ public void testGetGenericComponentType() throws Exception { Field field = clazz.getDeclaredField("array"); Type genericType = field.getGenericType(); assertInstanceOf(GenericArrayType.class, genericType); + assertEquals("T[]", genericType.toString()); + assertEquals("T[]", genericType.getTypeName()); + Type componentType = ((GenericArrayType) genericType).getGenericComponentType(); assertEquals(getTypeParameter(clazz), componentType); assertInstanceOf(TypeVariable.class, componentType); @@ -52,13 +55,17 @@ public void testParameterizedComponentType() throws Exception { Class clazz = GenericArrayTypeTest.B.class; Field field = clazz.getDeclaredField("array"); Type genericType = field.getGenericType(); - assertInstanceOf(GenericArrayType.class, genericType); + + String bName = B.class.getName(); + assertEquals(bName + "[]", genericType.toString()); + assertEquals(bName + "[]", genericType.getTypeName()); + GenericArrayType arrayType = (GenericArrayType) genericType; Type componentType = arrayType.getGenericComponentType(); assertInstanceOf(ParameterizedType.class, componentType); - ParameterizedType parameteriezdType = (ParameterizedType) componentType; - assertEquals(clazz, parameteriezdType.getRawType()); - assertEquals(clazz.getTypeParameters()[0], parameteriezdType.getActualTypeArguments()[0]); + ParameterizedType parameterizedType = (ParameterizedType) componentType; + assertEquals(clazz, parameterizedType.getRawType()); + assertEquals(clazz.getTypeParameters()[0], parameterizedType.getActualTypeArguments()[0]); } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/ParameterizedTypeTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/ParameterizedTypeTest.java index 3b2614eae..6004863b4 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/ParameterizedTypeTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/ParameterizedTypeTest.java @@ -32,6 +32,11 @@ public void testStringParameterizedSuperClass() { Class clazz = B.class; Type genericSuperclass = clazz.getGenericSuperclass(); assertInstanceOf(ParameterizedType.class, genericSuperclass); + + String aName = A.class.getName(); + assertEquals(aName + "", genericSuperclass.toString()); + assertEquals(aName + "", genericSuperclass.getTypeName()); + ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; assertEquals(ParameterizedTypeTest.class, parameterizedType.getOwnerType()); assertEquals(A.class, parameterizedType.getRawType()); @@ -48,6 +53,11 @@ public void testTypeParameterizedSuperClass() { Class clazz = D.class; Type genericSuperclass = clazz.getGenericSuperclass(); assertInstanceOf(ParameterizedType.class, genericSuperclass); + + String cName = C.class.getName(); + assertEquals(cName + "", genericSuperclass.toString()); + assertEquals(cName + "", genericSuperclass.getTypeName()); + ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; assertEquals(ParameterizedTypeTest.class, parameterizedType.getOwnerType()); assertEquals(C.class, parameterizedType.getRawType()); @@ -70,6 +80,10 @@ public void testParameterizedMemeber() throws Exception{ assertEquals(ParameterizedTypeTest.class, parameterizedType.getOwnerType()); assertEquals(E.class, parameterizedType.getRawType()); + String eName = E.class.getName(); + assertEquals(eName + "", parameterizedType.toString()); + assertEquals(eName + "", parameterizedType.getTypeName()); + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); assertLenghtOne(actualTypeArguments); assertEquals(getTypeParameter(clazz), actualTypeArguments[0]); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/TypeVariableTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/TypeVariableTest.java index d1c7ea995..8f93da484 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/TypeVariableTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/TypeVariableTest.java @@ -35,6 +35,8 @@ public void testSimpleTypeVariableOnClass(){ TypeVariable typeVariable = typeParameters[0]; assertEquals(clazz, typeVariable.getGenericDeclaration()); assertEquals("T", typeVariable.getName()); + assertEquals("T", typeVariable.toString()); + assertEquals("T", typeVariable.getTypeName()); Type[] bounds = typeVariable.getBounds(); assertLenghtOne(bounds); assertEquals(Object.class, bounds[0]); @@ -51,6 +53,8 @@ public void testSimpleTypeVariableOnMethod() throws Exception{ TypeVariable typeVariable = typeParameters[0]; assertEquals(method, typeVariable.getGenericDeclaration()); assertEquals("T", typeVariable.getName()); + assertEquals("T", typeVariable.toString()); + assertEquals("T", typeVariable.getTypeName()); Type[] bounds = typeVariable.getBounds(); assertLenghtOne(bounds); assertEquals(Object.class, bounds[0]); @@ -67,6 +71,8 @@ public void testSimpleTypeVariableOnConstructor() throws Exception{ TypeVariable typeVariable = typeParameters[0]; assertEquals(constructor, typeVariable.getGenericDeclaration()); assertEquals("T", typeVariable.getName()); + assertEquals("T", typeVariable.toString()); + assertEquals("T", typeVariable.getTypeName()); Type[] bounds = typeVariable.getBounds(); assertLenghtOne(bounds); assertEquals(Object.class, bounds[0]); @@ -79,13 +85,18 @@ public void testMultipleTypeVariablesOnClass() throws Exception { assertEquals(3, typeParameters.length); assertEquals("Q", typeParameters[0].getName()); assertEquals(clazz, typeParameters[0].getGenericDeclaration()); + assertEquals("Q", typeParameters[0].toString()); + assertEquals("Q", typeParameters[0].getTypeName()); assertEquals("R", typeParameters[1].getName()); assertEquals(clazz, typeParameters[1].getGenericDeclaration()); + assertEquals("R", typeParameters[1].toString()); + assertEquals("R", typeParameters[1].getTypeName()); assertEquals("S", typeParameters[2].getName()); assertEquals(clazz, typeParameters[2].getGenericDeclaration()); - + assertEquals("S", typeParameters[2].toString()); + assertEquals("S", typeParameters[2].getTypeName()); } static class E { @@ -99,12 +110,18 @@ public void testMultipleTypeVariablesOnMethod() throws Exception { assertEquals(3, typeParameters.length); assertEquals("Q", typeParameters[0].getName()); assertEquals(method, typeParameters[0].getGenericDeclaration()); + assertEquals("Q", typeParameters[0].toString()); + assertEquals("Q", typeParameters[0].getTypeName()); assertEquals("R", typeParameters[1].getName()); assertEquals(method, typeParameters[1].getGenericDeclaration()); + assertEquals("R", typeParameters[1].toString()); + assertEquals("R", typeParameters[1].getTypeName()); assertEquals("S", typeParameters[2].getName()); assertEquals(method, typeParameters[2].getGenericDeclaration()); + assertEquals("S", typeParameters[2].toString()); + assertEquals("S", typeParameters[2].getTypeName()); } static class F { @@ -118,12 +135,18 @@ public void testMultipleTypeVariablesOnConstructor() throws Exception { assertEquals(3, typeParameters.length); assertEquals("Q", typeParameters[0].getName()); assertEquals(constructor, typeParameters[0].getGenericDeclaration()); + assertEquals("Q", typeParameters[0].toString()); + assertEquals("Q", typeParameters[0].getTypeName()); assertEquals("R", typeParameters[1].getName()); assertEquals(constructor, typeParameters[1].getGenericDeclaration()); + assertEquals("R", typeParameters[1].toString()); + assertEquals("R", typeParameters[1].getTypeName()); assertEquals("S", typeParameters[2].getName()); assertEquals(constructor, typeParameters[2].getGenericDeclaration()); + assertEquals("S", typeParameters[2].toString()); + assertEquals("S", typeParameters[2].getTypeName()); } static class G {} @@ -135,6 +158,8 @@ public void testSingleBound() throws Exception { Type[] bounds = typeVariable.getBounds(); assertLenghtOne(bounds); assertEquals(Number.class, bounds[0]); + assertEquals("T", typeVariable.toString()); + assertEquals("T", typeVariable.getTypeName()); } static class H {} @@ -146,5 +171,7 @@ public void testMultipleBound() throws Exception { assertEquals(2, bounds.length); assertEquals(Number.class, bounds[0]); assertEquals(Serializable.class, bounds[1]); + assertEquals("T", typeVariable.toString()); + assertEquals("T", typeVariable.getTypeName()); } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/WildcardTypeTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/WildcardTypeTest.java index e29fd474c..9d3a8b0db 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/WildcardTypeTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/reflect/WildcardTypeTest.java @@ -76,6 +76,8 @@ private void checkLowerBoundedParameter(Method method) { assertInstanceOf(WildcardType.class, actualTypeArguments[0]); WildcardType wildcardType = (WildcardType) actualTypeArguments[0]; + assertEquals("? super T", wildcardType.toString()); + assertEquals("? super T", wildcardType.getTypeName()); Type[] lowerBounds = wildcardType.getLowerBounds(); assertLenghtOne(lowerBounds); @@ -97,6 +99,8 @@ private void checkUpperBoundedParameter(Method method) { assertInstanceOf(WildcardType.class, actualTypeArguments[0]); WildcardType wildcardType = (WildcardType) actualTypeArguments[0]; + assertEquals("? extends T", wildcardType.toString()); + assertEquals("? extends T", wildcardType.getTypeName()); assertLenghtZero(wildcardType.getLowerBounds()); Type[] upperBounds = wildcardType.getUpperBounds(); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/BigDecimalTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/BigDecimalTest.java index 20e9237f5..41ef93ece 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/BigDecimalTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/BigDecimalTest.java @@ -912,7 +912,7 @@ public void test_stripTrailingZero() { ((notrailingzerotest.stripTrailingZeros()).scale() == 0) ); - // BEGIN android-changed: preserve RI compatibility, so BigDecimal.equals (which checks + // BEGIN Android-changed: preserve RI compatibility, so BigDecimal.equals (which checks // value *and* scale) continues to work. https://issues.apache.org/jira/browse/HARMONY-4623 /* Zero */ BigDecimal zerotest = new BigDecimal("0.0000"); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/OldBigIntegerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/OldBigIntegerTest.java index 7d1f1b4f3..0b866aed9 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/OldBigIntegerTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/OldBigIntegerTest.java @@ -230,7 +230,7 @@ public void test_probablePrime() { } } -// BEGIN android-added +// BEGIN Android-added // public void testModPowPerformance() { // Random rnd = new Random(); // for (int i = 0; i < 10; i++) { @@ -283,7 +283,7 @@ public void test_probablePrime() { // } // } // } -// END android-added +// END Android-added @@ -342,7 +342,7 @@ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { - throw new AssertionError(e); // android-changed + throw new AssertionError(e); // Android-changed } } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/CookiePolicyTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/CookiePolicyTest.java index 61eff1e63..1e3879880 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/CookiePolicyTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/CookiePolicyTest.java @@ -33,29 +33,10 @@ public class CookiePolicyTest extends TestCase { public void test_ShouldAccept_LURI_LHttpCookie() throws URISyntaxException { HttpCookie cookie = new HttpCookie("Harmony_6", "ongoing"); URI uri = new URI(""); - try { - CookiePolicy.ACCEPT_ORIGINAL_SERVER.shouldAccept(null, cookie); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } - - try { - CookiePolicy.ACCEPT_ORIGINAL_SERVER.shouldAccept(uri, null); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } - - try { - CookiePolicy.ACCEPT_ORIGINAL_SERVER.shouldAccept(null, null); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } + boolean accept; // Policy: ACCEPT_ALL, always returns true - boolean accept = CookiePolicy.ACCEPT_ALL.shouldAccept(null, cookie); + accept = CookiePolicy.ACCEPT_ALL.shouldAccept(null, cookie); assertTrue(accept); accept = CookiePolicy.ACCEPT_ALL.shouldAccept(null, null); @@ -107,6 +88,15 @@ public void test_ShouldAccept_LURI_LHttpCookie() throws URISyntaxException { accept = CookiePolicy.ACCEPT_ORIGINAL_SERVER.shouldAccept(new URI( "s://a.b.c.d"), cookie); assertFalse(accept); + + accept = CookiePolicy.ACCEPT_ORIGINAL_SERVER.shouldAccept(null, cookie); + assertFalse(accept); + + accept = CookiePolicy.ACCEPT_ORIGINAL_SERVER.shouldAccept(uri, null); + assertFalse(accept); + + accept = CookiePolicy.ACCEPT_ORIGINAL_SERVER.shouldAccept(null, null); + assertFalse(accept); } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketImplTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketImplTest.java index e94a963d6..fb2d78b4f 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketImplTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketImplTest.java @@ -25,8 +25,15 @@ import java.net.NetworkInterface; import java.net.SocketAddress; import java.net.SocketException; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public class DatagramSocketImplTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); -public class DatagramSocketImplTest extends junit.framework.TestCase { /** * java.net.DatagramSocketImpl#DatagramSocketImpl() */ diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java index 4998dc528..bb9e806ad 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java @@ -31,10 +31,20 @@ import java.net.SocketException; import java.net.UnknownHostException; import java.nio.channels.DatagramChannel; +import libcore.io.Libcore; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public class DatagramSocketTest extends junit.framework.TestCase { +import static android.system.OsConstants.IPPROTO_IP; +import static android.system.OsConstants.IP_MULTICAST_ALL; - static final class DatagramServer extends Thread { +public class DatagramSocketTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); + + static final class DatagramServer extends Thread implements AutoCloseable { volatile boolean running = true; @@ -70,8 +80,6 @@ public void run() { } } catch (IOException e) { fail(); - } finally { - serverSocket.close(); } } @@ -79,16 +87,28 @@ public int getPort() { return serverSocket.getLocalPort(); } - public void stopServer() { + @Override + public void close() throws Exception { running = false; + try { + join(); + } finally { + serverSocket.close(); + } } } /** * java.net.DatagramSocket#DatagramSocket() */ - public void test_Constructor() throws SocketException { - new DatagramSocket(); + public void test_Constructor() throws Exception { + try (DatagramSocket ds = new DatagramSocket()) { + // Datagram sockets bound to the wildcard INADDR_ANY address should by default only + // receive messages from groups they explicitly joined. + boolean multicastAllEnabled = Libcore.os.getsockoptInt(ds.getFileDescriptor$(), + IPPROTO_IP, IP_MULTICAST_ALL) == 1; + assertFalse(multicastAllEnabled); + } } /** @@ -103,10 +123,11 @@ public void test_ConstructorI() throws SocketException { * java.net.DatagramSocket#DatagramSocket(int, java.net.InetAddress) */ public void test_ConstructorILjava_net_InetAddress() throws IOException { - DatagramSocket ds = new DatagramSocket(0, InetAddress.getLocalHost()); - assertTrue("Created socket with incorrect port", ds.getLocalPort() != 0); - assertEquals("Created socket with incorrect address", InetAddress - .getLocalHost(), ds.getLocalAddress()); + try (DatagramSocket ds = new DatagramSocket(0, InetAddress.getLocalHost())) { + assertTrue("Created socket with incorrect port", ds.getLocalPort() != 0); + assertEquals("Created socket with incorrect address", InetAddress + .getLocalHost(), ds.getLocalAddress()); + } } /** @@ -126,18 +147,21 @@ public void test_close() throws UnknownHostException, SocketException { } public void test_connectLjava_net_InetAddressI() throws Exception { - DatagramSocket ds = new DatagramSocket(); - InetAddress inetAddress = InetAddress.getLocalHost(); - ds.connect(inetAddress, 0); - assertEquals("Incorrect InetAddress", inetAddress, ds.getInetAddress()); - assertEquals("Incorrect Port", 0, ds.getPort()); - ds.disconnect(); + try (DatagramSocket ds = new DatagramSocket()) { + InetAddress inetAddress = InetAddress.getLocalHost(); + ds.connect(inetAddress, 0); + assertEquals("Incorrect InetAddress", inetAddress, ds.getInetAddress()); + assertEquals("Incorrect Port", 0, ds.getPort()); + ds.disconnect(); + } - ds = new java.net.DatagramSocket(); - inetAddress = InetAddress.getByName("FE80:0000:0000:0000:020D:60FF:FE0F:A776%4"); - ds.connect(inetAddress, 0); - assertEquals(inetAddress, ds.getInetAddress()); - ds.disconnect(); + try (DatagramSocket ds = new DatagramSocket()) { + InetAddress inetAddress = + InetAddress.getByName("FE80:0000:0000:0000:020D:60FF:FE0F:A776%4"); + ds.connect(inetAddress, 0); + assertEquals(inetAddress, ds.getInetAddress()); + ds.disconnect(); + } } public void testConnect_connectToSelf() throws Exception { @@ -177,189 +201,182 @@ private static void assertPacketDataEquals(DatagramPacket p1, DatagramPacket p2) } public void testConnect_echoServer() throws Exception { - final DatagramSocket ds = new DatagramSocket(0); + try (DatagramSocket ds = new DatagramSocket(0); + DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK)) { + server.start(); - final DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK); - server.start(); + ds.connect(Inet6Address.LOOPBACK, server.getPort()); - ds.connect(Inet6Address.LOOPBACK, server.getPort()); + final byte[] sendBytes = { 'T', 'e', 's', 't', 0 }; + final DatagramPacket send = new DatagramPacket(sendBytes, sendBytes.length); + final DatagramPacket receive = new DatagramPacket(new byte[20], 20); - final byte[] sendBytes = { 'T', 'e', 's', 't', 0 }; - final DatagramPacket send = new DatagramPacket(sendBytes, sendBytes.length); - final DatagramPacket receive = new DatagramPacket(new byte[20], 20); + ds.send(send); + ds.setSoTimeout(2000); + ds.receive(receive); - ds.send(send); - ds.setSoTimeout(2000); - ds.receive(receive); - ds.close(); - - assertEquals(sendBytes.length, receive.getLength()); - assertPacketDataEquals(send, receive); - assertEquals(Inet6Address.LOOPBACK, receive.getAddress()); - - server.stopServer(); + assertEquals(sendBytes.length, receive.getLength()); + assertPacketDataEquals(send, receive); + assertEquals(Inet6Address.LOOPBACK, receive.getAddress()); + } } // Validate that once connected we cannot send to another address. public void testConnect_throwsOnAddressMismatch() throws Exception { - final DatagramSocket ds = new DatagramSocket(0); + try (DatagramSocket ds = new DatagramSocket(0); + DatagramServer s1 = new DatagramServer(Inet6Address.LOOPBACK); + DatagramServer s2 = new DatagramServer(Inet6Address.LOOPBACK)) { - DatagramServer s1 = new DatagramServer(Inet6Address.LOOPBACK); - DatagramServer s2 = new DatagramServer(Inet6Address.LOOPBACK); - try { ds.connect(Inet6Address.LOOPBACK, s1.getPort()); - ds.send(new DatagramPacket(new byte[10], 10, Inet6Address.LOOPBACK, s2.getPort())); - fail(); - } catch (IllegalArgumentException expected) { - } finally { - ds.close(); - s1.stopServer(); - s2.stopServer(); + try { + ds.send(new DatagramPacket(new byte[10], 10, Inet6Address.LOOPBACK, s2.getPort())); + fail(); + } catch (IllegalArgumentException expected) { + } } } // Validate that we can connect, then disconnect, then connect then // send/recv. public void testConnect_connectDisconnectConnectThenSendRecv() throws Exception { - final DatagramSocket ds = new DatagramSocket(0); - - final DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK); - final DatagramServer broken = new DatagramServer(Inet6Address.LOOPBACK, false); - server.start(); - broken.start(); + try (DatagramSocket ds = new DatagramSocket(0); + DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK); + DatagramServer broken = new DatagramServer(Inet6Address.LOOPBACK, false)) { + server.start(); + broken.start(); - final int serverPortNumber = server.getPort(); - ds.connect(Inet6Address.LOOPBACK, broken.getPort()); - ds.disconnect(); - ds.connect(Inet6Address.LOOPBACK, serverPortNumber); - - final byte[] sendBytes = { 'T', 'e', 's', 't', 0 }; - final DatagramPacket send = new DatagramPacket(sendBytes, sendBytes.length); - final DatagramPacket receive = new DatagramPacket(new byte[20], 20); - ds.send(send); - ds.setSoTimeout(2000); - ds.receive(receive); - ds.close(); + final int serverPortNumber = server.getPort(); + ds.connect(Inet6Address.LOOPBACK, broken.getPort()); + ds.disconnect(); + ds.connect(Inet6Address.LOOPBACK, serverPortNumber); - assertPacketDataEquals(send, receive); - assertEquals(Inet6Address.LOOPBACK, receive.getAddress()); + final byte[] sendBytes = { 'T', 'e', 's', 't', 0 }; + final DatagramPacket send = new DatagramPacket(sendBytes, sendBytes.length); + final DatagramPacket receive = new DatagramPacket(new byte[20], 20); + ds.send(send); + ds.setSoTimeout(2000); + ds.receive(receive); - server.stopServer(); - broken.stopServer(); + assertPacketDataEquals(send, receive); + assertEquals(Inet6Address.LOOPBACK, receive.getAddress()); + } } // Validate that we can connect/disconnect then send/recv to any address public void testConnect_connectDisconnectThenSendRecv() throws Exception { - final DatagramSocket ds = new DatagramSocket(0); + try (DatagramSocket ds = new DatagramSocket(0); + DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK)) { + server.start(); - final DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK); - server.start(); + final int serverPortNumber = server.getPort(); + ds.connect(Inet6Address.LOOPBACK, serverPortNumber); + ds.disconnect(); - final int serverPortNumber = server.getPort(); - ds.connect(Inet6Address.LOOPBACK, serverPortNumber); - ds.disconnect(); + final byte[] sendBytes = { 'T', 'e', 's', 't', 0 }; + final DatagramPacket send = new DatagramPacket(sendBytes, sendBytes.length, + Inet6Address.LOOPBACK, serverPortNumber); + final DatagramPacket receive = new DatagramPacket(new byte[20], 20); + ds.send(send); + ds.setSoTimeout(2000); + ds.receive(receive); - final byte[] sendBytes = { 'T', 'e', 's', 't', 0 }; - final DatagramPacket send = new DatagramPacket(sendBytes, sendBytes.length, - Inet6Address.LOOPBACK, serverPortNumber); - final DatagramPacket receive = new DatagramPacket(new byte[20], 20); - ds.send(send); - ds.setSoTimeout(2000); - ds.receive(receive); - ds.close(); - - assertPacketDataEquals(send, receive); - assertEquals(Inet6Address.LOOPBACK, receive.getAddress()); - - server.stopServer(); + assertPacketDataEquals(send, receive); + assertEquals(Inet6Address.LOOPBACK, receive.getAddress()); + } } public void testConnect_connectTwice() throws Exception { - final DatagramSocket ds = new DatagramSocket(0); + try (DatagramSocket ds = new DatagramSocket(0); + DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK); + DatagramServer broken = new DatagramServer(Inet6Address.LOOPBACK)) { + server.start(); + broken.start(); - final DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK); - final DatagramServer broken = new DatagramServer(Inet6Address.LOOPBACK); - server.start(); - broken.start(); + final int serverPortNumber = server.getPort(); + ds.connect(Inet6Address.LOOPBACK, broken.getPort()); + ds.connect(Inet6Address.LOOPBACK, serverPortNumber); + ds.disconnect(); - final int serverPortNumber = server.getPort(); - ds.connect(Inet6Address.LOOPBACK, broken.getPort()); - ds.connect(Inet6Address.LOOPBACK, serverPortNumber); - ds.disconnect(); + final byte[] sendBytes = { 'T', 'e', 's', 't', 0 }; + final DatagramPacket send = new DatagramPacket(sendBytes, sendBytes.length, + Inet6Address.LOOPBACK, serverPortNumber); + final DatagramPacket receive = new DatagramPacket(new byte[20], 20); + ds.send(send); + ds.setSoTimeout(2000); + ds.receive(receive); - final byte[] sendBytes = { 'T', 'e', 's', 't', 0 }; - final DatagramPacket send = new DatagramPacket(sendBytes, sendBytes.length, - Inet6Address.LOOPBACK, serverPortNumber); - final DatagramPacket receive = new DatagramPacket(new byte[20], 20); - ds.send(send); - ds.setSoTimeout(2000); - ds.receive(receive); - ds.close(); - - assertPacketDataEquals(send, receive); - assertEquals(Inet6Address.LOOPBACK, receive.getAddress()); - - server.stopServer(); - broken.stopServer(); + assertPacketDataEquals(send, receive); + assertEquals(Inet6Address.LOOPBACK, receive.getAddress()); + } } public void testConnect_zeroAddress() throws Exception { - DatagramSocket ds = new DatagramSocket(); - byte[] addressBytes = { 0, 0, 0, 0 }; - InetAddress inetAddress = InetAddress.getByAddress(addressBytes); - ds.connect(inetAddress, 0); + try (DatagramSocket ds = new DatagramSocket()) { + byte[] addressBytes = { 0, 0, 0, 0 }; + InetAddress inetAddress = InetAddress.getByAddress(addressBytes); + ds.connect(inetAddress, 0); + } - ds = new java.net.DatagramSocket(); - byte[] addressTestBytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 }; - inetAddress = InetAddress.getByAddress(addressTestBytes); - ds.connect(inetAddress, 0); + try (DatagramSocket ds = new DatagramSocket()) { + byte[] addressTestBytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 }; + InetAddress inetAddress = InetAddress.getByAddress(addressTestBytes); + ds.connect(inetAddress, 0); + } } public void test_disconnect() throws Exception { - DatagramSocket ds = new DatagramSocket(); - InetAddress inetAddress = InetAddress.getLocalHost(); - ds.connect(inetAddress, 0); - ds.disconnect(); - assertNull("Incorrect InetAddress", ds.getInetAddress()); - assertEquals("Incorrect Port", -1, ds.getPort()); + try (DatagramSocket ds = new DatagramSocket()) { + InetAddress inetAddress = InetAddress.getLocalHost(); + ds.connect(inetAddress, 0); + ds.disconnect(); + assertNull("Incorrect InetAddress", ds.getInetAddress()); + assertEquals("Incorrect Port", -1, ds.getPort()); + } - ds = new DatagramSocket(); - inetAddress = InetAddress.getByName("FE80:0000:0000:0000:020D:60FF:FE0F:A776%4"); - ds.connect(inetAddress, 0); - ds.disconnect(); - assertNull("Incorrect InetAddress", ds.getInetAddress()); - assertEquals("Incorrect Port", -1, ds.getPort()); + try (DatagramSocket ds = new DatagramSocket()) { + InetAddress inetAddress = + InetAddress.getByName("FE80:0000:0000:0000:020D:60FF:FE0F:A776%4"); + ds.connect(inetAddress, 0); + ds.disconnect(); + assertNull("Incorrect InetAddress", ds.getInetAddress()); + assertEquals("Incorrect Port", -1, ds.getPort()); + } } public void test_getLocalAddress() throws Exception { // Test for method java.net.InetAddress // java.net.DatagramSocket.getLocalAddress() InetAddress local = InetAddress.getLocalHost(); - DatagramSocket ds = new java.net.DatagramSocket(0, local); - assertEquals(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), ds.getLocalAddress()); + try (DatagramSocket ds = new DatagramSocket(0, local)) { + assertEquals(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), + ds.getLocalAddress()); + } // now check behavior when the ANY address is returned - DatagramSocket s = new DatagramSocket(0); - assertTrue("ANY address not IPv6: " + s.getLocalSocketAddress(), s.getLocalAddress() instanceof Inet6Address); - s.close(); + try (DatagramSocket s = new DatagramSocket(0)) { + assertTrue("ANY address not IPv6: " + s.getLocalSocketAddress(), + s.getLocalAddress() instanceof Inet6Address); + } } public void test_getLocalPort() throws SocketException { - DatagramSocket ds = new DatagramSocket(); - assertTrue("Returned incorrect port", ds.getLocalPort() != 0); + try (DatagramSocket ds = new DatagramSocket()) { + assertTrue("Returned incorrect port", ds.getLocalPort() != 0); + } } public void test_getPort() throws IOException { - DatagramSocket theSocket = new DatagramSocket(); - assertEquals("Expected -1 for remote port as not connected", -1, - theSocket.getPort()); + try (DatagramSocket theSocket = new DatagramSocket()) { + assertEquals("Expected -1 for remote port as not connected", -1, + theSocket.getPort()); - // Now connect the socket and validate that we get the right port - int portNumber = 49152; // any valid port, even if it is unreachable - theSocket.connect(InetAddress.getLocalHost(), portNumber); - assertEquals("getPort returned wrong value", portNumber, theSocket - .getPort()); + // Now connect the socket and validate that we get the right port + int portNumber = 49152; // any valid port, even if it is unreachable + theSocket.connect(InetAddress.getLocalHost(), portNumber); + assertEquals("getPort returned wrong value", portNumber, theSocket + .getPort()); + } } public void test_getReceiveBufferSize() throws Exception { @@ -389,12 +406,15 @@ public void test_getSendBufferSize() throws Exception { } public void test_getSoTimeout() throws Exception { - DatagramSocket ds = new DatagramSocket(); - final int timeoutSet = 100; - ds.setSoTimeout(timeoutSet); - int actualTimeout = ds.getSoTimeout(); - // The kernel can round the requested value based on the HZ setting. We allow up to 10ms. - assertTrue("Returned incorrect timeout", Math.abs(actualTimeout - timeoutSet) <= 10); + try (DatagramSocket ds = new DatagramSocket()) { + final int timeoutSet = 100; + ds.setSoTimeout(timeoutSet); + int actualTimeout = ds.getSoTimeout(); + // The kernel can round the requested value based on the HZ setting. We allow up to + // 10ms. + assertTrue("Returned incorrect timeout", + Math.abs(actualTimeout - timeoutSet) <= 10); + } } static final class TestDatagramSocketImpl extends DatagramSocketImpl { @@ -593,23 +613,25 @@ public UnsupportedSocketAddress() { } } - DatagramSocket ds = new DatagramSocket(new InetSocketAddress( - InetAddress.getLocalHost(), 0)); - assertTrue(ds.getBroadcast()); - assertTrue("Created socket with incorrect port", ds.getLocalPort() != 0); - assertEquals("Created socket with incorrect address", InetAddress - .getLocalHost(), ds.getLocalAddress()); + try (DatagramSocket ds = new DatagramSocket( + new InetSocketAddress(InetAddress.getLocalHost(), 0))) { + assertTrue(ds.getBroadcast()); + assertTrue("Created socket with incorrect port", ds.getLocalPort() != 0); + assertEquals("Created socket with incorrect address", InetAddress + .getLocalHost(), ds.getLocalAddress()); + } try { - ds = new java.net.DatagramSocket(new UnsupportedSocketAddress()); + new DatagramSocket(new UnsupportedSocketAddress()); fail("No exception when constructing datagramSocket with unsupported SocketAddress type"); } catch (IllegalArgumentException e) { // Expected } // regression for HARMONY-894 - ds = new DatagramSocket(null); - assertTrue(ds.getBroadcast()); + try (DatagramSocket ds = new DatagramSocket(null)) { + assertTrue(ds.getBroadcast()); + } } @@ -677,50 +699,52 @@ public void test_isBound() throws Exception { } public void test_isConnected() throws Exception { - DatagramServer ds = new DatagramServer(Inet6Address.LOOPBACK); + try (DatagramServer ds = new DatagramServer(Inet6Address.LOOPBACK)) { - // base test - DatagramSocket theSocket = new DatagramSocket(0); - assertFalse(theSocket.isConnected()); - theSocket.connect(new InetSocketAddress(Inet6Address.LOOPBACK, ds.getPort())); - assertTrue(theSocket.isConnected()); + // base test + try (DatagramSocket theSocket = new DatagramSocket(0)) { + assertFalse(theSocket.isConnected()); + theSocket.connect(new InetSocketAddress(Inet6Address.LOOPBACK, ds.getPort())); + assertTrue(theSocket.isConnected()); - // reconnect the socket and make sure we get the right answer - theSocket.connect(new InetSocketAddress(Inet6Address.LOOPBACK, ds.getPort())); - assertTrue(theSocket.isConnected()); + // reconnect the socket and make sure we get the right answer + theSocket.connect(new InetSocketAddress(Inet6Address.LOOPBACK, ds.getPort())); + assertTrue(theSocket.isConnected()); - // now disconnect the socket and make sure we get the right answer - theSocket.disconnect(); - assertFalse(theSocket.isConnected()); - theSocket.close(); + // now disconnect the socket and make sure we get the right answer + theSocket.disconnect(); + assertFalse(theSocket.isConnected()); + } - // now check behavior when socket is closed when connected - theSocket = new DatagramSocket(0); - theSocket.connect(new InetSocketAddress(Inet6Address.LOOPBACK, ds.getPort())); - theSocket.close(); - assertTrue(theSocket.isConnected()); + // now check behavior when socket is closed when connected + DatagramSocket theSocket = new DatagramSocket(0); + theSocket.connect(new InetSocketAddress(Inet6Address.LOOPBACK, ds.getPort())); + theSocket.close(); + assertTrue(theSocket.isConnected()); + } } public void test_getRemoteSocketAddress() throws Exception { - DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK); - DatagramSocket s = new DatagramSocket(0); - s.connect(new InetSocketAddress(Inet6Address.LOOPBACK, server.getPort())); + try (DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK)) { + try (DatagramSocket s = new DatagramSocket(0)) { + s.connect(new InetSocketAddress(Inet6Address.LOOPBACK, server.getPort())); - assertEquals(new InetSocketAddress(Inet6Address.LOOPBACK, server.getPort()), - s.getRemoteSocketAddress()); - s.close(); + assertEquals(new InetSocketAddress(Inet6Address.LOOPBACK, server.getPort()), + s.getRemoteSocketAddress()); + } - // now create one that is not connected and validate that we get the - // right answer - DatagramSocket theSocket = new DatagramSocket(null); - theSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0)); - assertNull(theSocket.getRemoteSocketAddress()); + // now create one that is not connected and validate that we get the + // right answer + try (DatagramSocket theSocket = new DatagramSocket(null)) { + theSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0)); + assertNull(theSocket.getRemoteSocketAddress()); - // now connect and validate we get the right answer - theSocket.connect(new InetSocketAddress(Inet6Address.LOOPBACK, server.getPort())); - assertEquals(new InetSocketAddress(Inet6Address.LOOPBACK, server.getPort()), - theSocket.getRemoteSocketAddress()); - theSocket.close(); + // now connect and validate we get the right answer + theSocket.connect(new InetSocketAddress(Inet6Address.LOOPBACK, server.getPort())); + assertEquals(new InetSocketAddress(Inet6Address.LOOPBACK, server.getPort()), + theSocket.getRemoteSocketAddress()); + } + } } public void test_getLocalSocketAddress_late_bind() throws Exception { @@ -864,34 +888,36 @@ public void test_setBroadcastZ() throws Exception { } public void test_getBroadcast() throws Exception { - DatagramSocket theSocket = new DatagramSocket(); - theSocket.setBroadcast(true); - assertTrue("getBroadcast false when it should be true", theSocket.getBroadcast()); - theSocket.setBroadcast(false); - assertFalse("getBroadcast true when it should be False", theSocket.getBroadcast()); + try (DatagramSocket theSocket = new DatagramSocket()) { + theSocket.setBroadcast(true); + assertTrue("getBroadcast false when it should be true", theSocket.getBroadcast()); + theSocket.setBroadcast(false); + assertFalse("getBroadcast true when it should be False", theSocket.getBroadcast()); + } } public void test_setTrafficClassI() throws Exception { int IPTOS_LOWCOST = 0x2; int IPTOS_THROUGHPUT = 0x8; - DatagramSocket theSocket = new DatagramSocket(0); + try (DatagramSocket theSocket = new DatagramSocket(0)) { - // validate that value set must be between 0 and 255 - try { - theSocket.setTrafficClass(256); - fail("No exception when traffic class set to 256"); - } catch (IllegalArgumentException e) { - } + // validate that value set must be between 0 and 255 + try { + theSocket.setTrafficClass(256); + fail("No exception when traffic class set to 256"); + } catch (IllegalArgumentException e) { + } - try { - theSocket.setTrafficClass(-1); - fail("No exception when traffic class set to -1"); - } catch (IllegalArgumentException e) { - } + try { + theSocket.setTrafficClass(-1); + fail("No exception when traffic class set to -1"); + } catch (IllegalArgumentException e) { + } - // now validate that we can set it to some good values - theSocket.setTrafficClass(IPTOS_LOWCOST); - theSocket.setTrafficClass(IPTOS_THROUGHPUT); + // now validate that we can set it to some good values + theSocket.setTrafficClass(IPTOS_LOWCOST); + theSocket.setTrafficClass(IPTOS_THROUGHPUT); + } } @@ -911,19 +937,20 @@ public void test_isClosed() throws Exception { } public void test_getChannel() throws Exception { - assertNull(new DatagramSocket().getChannel()); + try (DatagramSocket ds = new DatagramSocket()) { + assertNull(ds.getChannel()); + } - DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK); - DatagramSocket ds = new DatagramSocket(0); - assertNull(ds.getChannel()); - ds.disconnect(); - ds.close(); - server.stopServer(); + try (DatagramServer server = new DatagramServer(Inet6Address.LOOPBACK); + DatagramSocket ds = new DatagramSocket(0)) { + assertNull(ds.getChannel()); + ds.disconnect(); + } - DatagramChannel channel = DatagramChannel.open(); - DatagramSocket socket = channel.socket(); - assertEquals(channel, socket.getChannel()); - socket.close(); + try (DatagramChannel channel = DatagramChannel.open(); + DatagramSocket socket = channel.socket()) { + assertEquals(channel, socket.getChannel()); + } } public void testReceiveOversizePacket() throws Exception { @@ -941,4 +968,28 @@ public void testReceiveOversizePacket() throws Exception { ds.close(); assertEquals(new String("01234"), new String(recvBuffer, 0, recvBuffer.length, "UTF-8")); } + + // Receive twice reusing the same DatagramPacket. + // http://b/33957878 + public void testReceiveTwice() throws Exception { + try (DatagramSocket ds = new DatagramSocket(); + DatagramSocket sds = new DatagramSocket()) { + sds.connect(ds.getLocalSocketAddress()); + DatagramPacket p = new DatagramPacket(new byte[16], 16); + + byte[] smallPacketBytes = "01234".getBytes("UTF-8"); + DatagramPacket smallPacket = + new DatagramPacket(smallPacketBytes, smallPacketBytes.length); + sds.send(smallPacket); + ds.receive(p); + assertPacketDataEquals(smallPacket, p); + + byte[] largePacketBytes = "0123456789".getBytes("UTF-8"); + DatagramPacket largerPacket = + new DatagramPacket(largePacketBytes, largePacketBytes.length); + sds.send(largerPacket); + ds.receive(p); + assertPacketDataEquals(largerPacket, p); + } + } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/HttpCookieTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/HttpCookieTest.java index e8d2ba94a..5fedfd787 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/HttpCookieTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/HttpCookieTest.java @@ -5,9 +5,9 @@ * The ASF licenses this file to You 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 @@ -164,6 +164,10 @@ public void test_DomainMatches() { match = HttpCookie.domainMatches(null, "b.a.AJAX.com"); assertFalse(match); + + // JDK-7023713 + match = HttpCookie.domainMatches("hostname.local", "hostname"); + assertTrue(match); } /** @@ -781,12 +785,7 @@ public void test_Parse() { list = HttpCookie .parse("Set-Cookie:name=test;expires=Sun, 29-Feb-1999 19:14:07 GMT"); cookie = list.get(0); - // A value of "0" means the cookie must be discarded immediately. 29-Feb-1999 is an - // invalid date and fails to parse, so it must be discarded immediately. - // - // Android versions earlier than N returned a negative value here, which means the cookie - // is valid for the current session. - assertEquals(0, cookie.getMaxAge()); + assertTrue(cookie.getMaxAge() < 0); assertTrue(cookie.hasExpired()); // Parse multiple cookies @@ -950,6 +949,19 @@ public void test_Parse_versionConflict() { assertEquals(0, cookie.getVersion()); } + // http://b/31039416. Android N+ checks current time in hasExpired. + // Repeated invocations of cookie.hasExpired() may return different results + // due to time passage. + // This was not the case in earlier android versions, where hasExpired + // was testing the value of max-age/expires at the time of cookie creation. + public void test_hasExpired_checksTime() throws Exception { + List list = HttpCookie.parse("Set-Cookie:name=test;Max-Age=1"); + HttpCookie cookie = list.get(0); + assertFalse(cookie.hasExpired()); + Thread.sleep(2000); + assertTrue(cookie.hasExpired()); + } + /** * java.net.HttpCookie#parse(String) on multiple threads * Regression test for HARMONY-6307 diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java index 785a303d2..b32a96515 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java @@ -681,49 +681,6 @@ public void test_isIPv4CompatibleAddress() throws Exception { .isIPv4CompatibleAddress()); } - public void test_getByNameLjava_lang_String() throws Exception { - // ones to add "::255.255.255.255", "::FFFF:0.0.0.0", - // "0.0.0.0.0.0::255.255.255.255", "F:F:F:F:F:F:F:F", - // "[F:F:F:F:F:F:F:F]" - String validIPAddresses[] = { "::1.2.3.4", "::", "::", "1::0", "1::", - "::1", "0", /* jdk1.5 accepts 0 as valid */ - "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", - "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255", - "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0.0.0.0" }; - - String invalidIPAddresses[] = { "FFFF:FFFF" }; - - for (int i = 0; i < validIPAddresses.length; i++) { - - InetAddress.getByName(validIPAddresses[i]); - - //exercise positive cache - InetAddress.getByName(validIPAddresses[i]); - - if (!validIPAddresses[i].equals("0")) { - String tempIPAddress = "[" + validIPAddresses[i] + "]"; - InetAddress.getByName(tempIPAddress); - } - } - - for (int i = 0; i < invalidIPAddresses.length; i++) { - try { - InetAddress.getByName(invalidIPAddresses[i]); - fail("Invalid IP address incorrectly recognized as valid: " - + invalidIPAddresses[i]); - } catch (Exception e) { - } - - //exercise negative cache - try { - InetAddress.getByName(invalidIPAddresses[i]); - fail("Invalid IP address incorrectly recognized as valid: " - + invalidIPAddresses[i]); - } catch (Exception e) { - } - } - } - public void test_getByAddressLString$BI() throws UnknownHostException { try { Inet6Address.getByAddress("123", null, 0); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java index 4e41c2a1a..7e232cc93 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java @@ -111,23 +111,6 @@ public void test_getAllByNameLjava_lang_String() throws Exception { } } - /** - * java.net.InetAddress#getByName(java.lang.String) - */ - public void test_getByNameLjava_lang_String() throws Exception { - // Test for method java.net.InetAddress - // java.net.InetAddress.getByName(java.lang.String) - InetAddress ia2 = InetAddress.getByName("127.0.0.1"); - - // TODO : Test to ensure all the address formats are recognized - InetAddress i = InetAddress.getByName("1.2.3"); - assertEquals("1.2.0.3", i.getHostAddress()); - i = InetAddress.getByName("1.2"); - assertEquals("1.0.0.2", i.getHostAddress()); - i = InetAddress.getByName(String.valueOf(0xffffffffL)); - assertEquals("255.255.255.255", i.getHostAddress()); - } - /** * java.net.InetAddress#getHostAddress() */ @@ -383,12 +366,11 @@ public void test_isReachableLjava_net_NetworkInterfaceII_loopbackInterface() thr NetworkInterface loopbackInterface = null; ArrayList localAddresses = new ArrayList(); - Enumeration networkInterfaces = NetworkInterface - .getNetworkInterfaces(); + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + assertNotNull(networkInterfaces); while (networkInterfaces.hasMoreElements()) { NetworkInterface networkInterface = networkInterfaces.nextElement(); - Enumeration addresses = networkInterface - .getInetAddresses(); + Enumeration addresses = networkInterface.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); if (address.isLoopbackAddress()) { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java index 264e004e8..a24a67b87 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java @@ -32,8 +32,14 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.List; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public class MulticastSocketTest extends junit.framework.TestCase { +public class MulticastSocketTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); private static InetAddress lookup(String s) { try { @@ -71,7 +77,7 @@ protected void setUp() throws Exception { // Determine if the device is marked to support multicast or not. If this propery is not // set we assume the device has an interface capable of supporting multicast. - supportsMulticast = Boolean.valueOf( + supportsMulticast = Boolean.parseBoolean( System.getProperty("android.cts.device.multicast", "true")); if (!supportsMulticast) { return; @@ -573,32 +579,33 @@ private void test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface( SocketAddress groupSockAddr = null; SocketAddress groupSockAddr2 = null; - MulticastSocket mss = new MulticastSocket(0); - groupSockAddr = new InetSocketAddress(group, mss.getLocalPort()); - mss.joinGroup(groupSockAddr, null); - mss.leaveGroup(groupSockAddr, null); - try { + try (MulticastSocket mss = new MulticastSocket(0)) { + groupSockAddr = new InetSocketAddress(group, mss.getLocalPort()); + mss.joinGroup(groupSockAddr, null); mss.leaveGroup(groupSockAddr, null); - fail("Did not get exception when trying to leave group that was already left"); - } catch (IOException expected) { - } + try { + mss.leaveGroup(groupSockAddr, null); + fail("Did not get exception when trying to leave group that was already left"); + } catch (IOException expected) { + } - groupSockAddr2 = new InetSocketAddress(group2, mss.getLocalPort()); - mss.joinGroup(groupSockAddr, networkInterface); - try { - mss.leaveGroup(groupSockAddr2, networkInterface); - fail("Did not get exception when trying to leave group that was never joined"); - } catch (IOException expected) { - } + groupSockAddr2 = new InetSocketAddress(group2, mss.getLocalPort()); + mss.joinGroup(groupSockAddr, networkInterface); + try { + mss.leaveGroup(groupSockAddr2, networkInterface); + fail("Did not get exception when trying to leave group that was never joined"); + } catch (IOException expected) { + } - mss.leaveGroup(groupSockAddr, networkInterface); + mss.leaveGroup(groupSockAddr, networkInterface); - mss.joinGroup(groupSockAddr, networkInterface); - try { - mss.leaveGroup(groupSockAddr, loopbackInterface); - fail("Did not get exception when trying to leave group on wrong interface " + - "joined on [" + networkInterface + "] left on [" + loopbackInterface + "]"); - } catch (IOException expected) { + mss.joinGroup(groupSockAddr, networkInterface); + try { + mss.leaveGroup(groupSockAddr, loopbackInterface); + fail("Did not get exception when trying to leave group on wrong interface " + + "joined on [" + networkInterface + "] left on [" + loopbackInterface + "]"); + } catch (IOException expected) { + } } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java index 527946495..9d6c95023 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java @@ -17,6 +17,12 @@ package org.apache.harmony.tests.java.net; +import libcore.io.Libcore; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + import tests.support.Support_Configuration; import java.io.IOException; import java.io.InputStream; @@ -38,7 +44,12 @@ import java.util.Locale; import java.util.Properties; -public class ServerSocketTest extends junit.framework.TestCase { +import static android.system.OsConstants.F_GETFL; +import static android.system.OsConstants.O_NONBLOCK; + +public class ServerSocketTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); boolean interrupted; @@ -168,12 +179,16 @@ public void test_ConstructorIILjava_net_InetAddress() /** * java.net.ServerSocket#accept() */ - public void test_accept() throws IOException { + public void test_accept() throws Exception { s = new ServerSocket(0); try { s.setSoTimeout(5000); startClient(s.getLocalPort()); sconn = s.accept(); + + // The new socket should not be blocking. + assertEquals(0, Libcore.os.fcntlVoid(sconn.getFileDescriptor$(), F_GETFL) & O_NONBLOCK); + int localPort1 = s.getLocalPort(); int localPort2 = sconn.getLocalPort(); sconn.close(); @@ -691,19 +706,25 @@ public void test_defaultValueReuseAddress() throws Exception { String platform = System.getProperty("os.name").toLowerCase(Locale.US); if (!platform.startsWith("windows")) { // on Unix - assertTrue(new ServerSocket().getReuseAddress()); - assertTrue(new ServerSocket(0).getReuseAddress()); - assertTrue(new ServerSocket(0, 50).getReuseAddress()); - assertTrue(new ServerSocket(0, 50, InetAddress.getLocalHost()).getReuseAddress()); + assertReuseAddressAndCloseSocket(new ServerSocket()); + assertReuseAddressAndCloseSocket(new ServerSocket(0)); + assertReuseAddressAndCloseSocket(new ServerSocket(0, 50)); + assertReuseAddressAndCloseSocket(new ServerSocket(0, 50, InetAddress.getLocalHost())); } else { // on Windows - assertFalse(new ServerSocket().getReuseAddress()); - assertFalse(new ServerSocket(0).getReuseAddress()); - assertFalse(new ServerSocket(0, 50).getReuseAddress()); - assertFalse(new ServerSocket(0, 50, InetAddress.getLocalHost()).getReuseAddress()); + assertReuseAddressAndCloseSocket(new ServerSocket()); + assertReuseAddressAndCloseSocket(new ServerSocket(0)); + assertReuseAddressAndCloseSocket(new ServerSocket(0, 50)); + assertReuseAddressAndCloseSocket(new ServerSocket(0, 50, InetAddress.getLocalHost())); } } + private void assertReuseAddressAndCloseSocket(ServerSocket socket) throws IOException { + boolean reuseAddress = socket.getReuseAddress(); + socket.close(); + assertTrue(reuseAddress); + } + public void test_setReuseAddressZ() throws Exception { // set up server and connect InetSocketAddress anyAddress = new InetSocketAddress(InetAddress.getLocalHost(), 0); @@ -719,16 +740,15 @@ public void test_setReuseAddressZ() throws Exception { serverSocket.close(); // now try to rebind the server which should fail with - // setReuseAddress to false. On windows platforms the bind is - // allowed even then reUseAddress is false so our test uses - // the platform to determine what the expected result is. - String platform = System.getProperty("os.name"); - try { - serverSocket = new ServerSocket(); - serverSocket.setReuseAddress(false); - serverSocket.bind(theAddress); - fail("No exception when setReuseAddress is false and we bind:" + theAddress.toString()); - } catch (IOException expected) { + // setReuseAddress to false. + try (ServerSocket failingServerSocket = new ServerSocket()) { + failingServerSocket.setReuseAddress(false); + try { + failingServerSocket.bind(theAddress); + fail("No exception when setReuseAddress is false and we bind:" + theAddress + .toString()); + } catch (IOException expected) { + } } stillActiveSocket.close(); theSocket.close(); @@ -748,13 +768,14 @@ public void test_setReuseAddressZ() throws Exception { // now try to rebind the server which should pass with // setReuseAddress to true - try { - serverSocket = new ServerSocket(); - serverSocket.setReuseAddress(true); - serverSocket.bind(theAddress); - } catch (IOException ex) { - fail("Unexpected exception when setReuseAddress is true and we bind:" - + theAddress.toString() + ":" + ex.toString()); + try (ServerSocket rebindServerSocket = new ServerSocket()) { + rebindServerSocket.setReuseAddress(true); + try { + rebindServerSocket.bind(theAddress); + } catch (IOException ex) { + fail("Unexpected exception when setReuseAddress is true and we bind:" + + theAddress.toString() + ":" + ex.toString()); + } } stillActiveSocket.close(); theSocket.close(); @@ -773,23 +794,26 @@ public void test_setReuseAddressZ() throws Exception { serverSocket.close(); // now try to rebind the server which should pass - try { - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - } catch (IOException ex) { - fail("Unexpected exception when setReuseAddress is the default case and we bind:" - + theAddress.toString() + ":" + ex.toString()); + try (ServerSocket rebindServerSocket = new ServerSocket()) { + try { + rebindServerSocket.bind(theAddress); + } catch (IOException ex) { + fail("Unexpected exception when setReuseAddress is the default case and we bind:" + + theAddress.toString() + ":" + ex.toString()); + } } stillActiveSocket.close(); theSocket.close(); } public void test_getReuseAddress() throws Exception { - ServerSocket theSocket = new ServerSocket(); - theSocket.setReuseAddress(true); - assertTrue("getReuseAddress false when it should be true", theSocket.getReuseAddress()); - theSocket.setReuseAddress(false); - assertFalse("getReuseAddress true when it should be False", theSocket.getReuseAddress()); + try (ServerSocket theSocket = new ServerSocket()) { + theSocket.setReuseAddress(true); + assertTrue("getReuseAddress false when it should be true", theSocket.getReuseAddress()); + theSocket.setReuseAddress(false); + assertFalse("getReuseAddress true when it should be false", + theSocket.getReuseAddress()); + } } public void test_setReceiveBufferSizeI() throws Exception { @@ -819,13 +843,15 @@ public void test_setReceiveBufferSizeI() throws Exception { } public void test_getReceiveBufferSize() throws Exception { - ServerSocket theSocket = new ServerSocket(); + try (ServerSocket theSocket = new ServerSocket()) { - // since the value returned is not necessary what we set we are - // limited in what we can test - // just validate that it is not 0 or negative - assertFalse("get Buffer size returns 0:", 0 == theSocket.getReceiveBufferSize()); - assertFalse("get Buffer size returns a negative value:", 0 > theSocket.getReceiveBufferSize()); + // since the value returned is not necessary what we set we are + // limited in what we can test + // just validate that it is not 0 or negative + assertFalse("get Buffer size returns 0:", 0 == theSocket.getReceiveBufferSize()); + assertFalse("get Buffer size returns a negative value:", + 0 > theSocket.getReceiveBufferSize()); + } } public void test_getChannel() throws Exception { @@ -880,11 +906,13 @@ protected void startClient(int port) { */ public void test_implAcceptLjava_net_Socket() throws Exception { // regression test for Harmony-1235 - try { - new MockServerSocket().mockImplAccept(new MockSocket( - new MockSocketImpl())); - } catch (SocketException e) { - // expected + try (MockServerSocket mockServerSocket = new MockServerSocket()) { + try { + mockServerSocket.mockImplAccept(new MockSocket(new MockSocketImpl())); + fail("Expected SocketException"); + } catch (SocketException e) { + // expected + } } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketImplTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketImplTest.java index f6122e5ac..b64a2cedd 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketImplTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketImplTest.java @@ -25,8 +25,14 @@ import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketImpl; - -public class SocketImplTest extends junit.framework.TestCase { +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public class SocketImplTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); /** * java.net.SocketImpl#SocketImpl() diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java index 0915a2763..68122b289 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java @@ -21,8 +21,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.ConnectException; -import java.net.Inet4Address; -import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Proxy; @@ -31,16 +29,20 @@ import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketImpl; -import java.net.SocketImplFactory; import java.net.SocketTimeoutException; import java.net.UnknownHostException; -import java.security.Permission; import java.util.Arrays; import java.util.Locale; - +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; import tests.support.Support_Configuration; -public class SocketTest extends junit.framework.TestCase { +public class SocketTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); + private class ClientThread implements Runnable { public void run() { @@ -509,12 +511,10 @@ public void test_Constructor() { * java.net.Socket#Socket(java.lang.String, int) */ public void test_ConstructorLjava_lang_StringI() throws IOException { - ServerSocket server = new ServerSocket(0); - Socket client = new Socket(InetAddress.getLocalHost(), server - .getLocalPort()); - - assertEquals("Failed to create socket", server.getLocalPort(), client - .getPort()); + try (ServerSocket server = new ServerSocket(0); + Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) { + assertEquals("Failed to create socket", server.getLocalPort(), client.getPort()); + } // Regression for HARMONY-946 ServerSocket ss = new ServerSocket(0); @@ -601,10 +601,11 @@ public void test_ConstructorLjava_net_InetAddressI() throws IOException { */ public void test_ConstructorLjava_net_InetAddressILjava_net_InetAddressI() throws IOException { - ServerSocket server = new ServerSocket(0); - Socket client = new Socket(InetAddress.getLocalHost(), server - .getLocalPort(), InetAddress.getLocalHost(), 0); - assertNotSame("Failed to create socket", 0, client.getLocalPort()); + try (ServerSocket server = new ServerSocket(0); + Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort(), + InetAddress.getLocalHost(), 0)) { + assertNotSame("Failed to create socket", 0, client.getLocalPort()); + } } /** @@ -612,14 +613,17 @@ public void test_ConstructorLjava_net_InetAddressILjava_net_InetAddressI() */ @SuppressWarnings("deprecation") public void test_ConstructorLjava_net_InetAddressIZ() throws IOException { - ServerSocket server = new ServerSocket(0); - int serverPort = server.getLocalPort(); + try (ServerSocket server = new ServerSocket(0)) { + int serverPort = server.getLocalPort(); - Socket client = new Socket(InetAddress.getLocalHost(), serverPort, true); - assertEquals("Failed to create socket", serverPort, client.getPort()); + try (Socket client = new Socket(InetAddress.getLocalHost(), serverPort, true)) { + assertEquals("Failed to create socket", serverPort, client.getPort()); + } - client = new Socket(InetAddress.getLocalHost(), serverPort, false); - client.close(); + try (Socket client = new Socket(InetAddress.getLocalHost(), serverPort, false)) { + assertEquals("Failed to create socket", serverPort, client.getPort()); + } + } } /** @@ -694,43 +698,40 @@ private boolean isUnix() { } public void test_getKeepAlive() throws Exception { - ServerSocket server = new ServerSocket(0); - Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort(), null, 0); + try (ServerSocket server = new ServerSocket(0); + Socket client = new Socket(InetAddress.getLocalHost(), + server.getLocalPort(), null, 0)) { - client.setKeepAlive(true); - assertTrue("getKeepAlive false when it should be true", client.getKeepAlive()); + client.setKeepAlive(true); + assertTrue("getKeepAlive false when it should be true", client.getKeepAlive()); - client.setKeepAlive(false); - assertFalse("getKeepAlive true when it should be False", client.getKeepAlive()); + client.setKeepAlive(false); + assertFalse("getKeepAlive true when it should be False", client.getKeepAlive()); + } } public void test_getLocalAddress() throws IOException { - ServerSocket server = new ServerSocket(0); - Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort()); - - assertTrue("Returned incorrect InetAddress", client.getLocalAddress() - .equals(InetAddress.getLocalHost())); - - client = new Socket(); - client.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0)); - assertTrue(client.getLocalAddress().isAnyLocalAddress()); + try (ServerSocket server = new ServerSocket(0)) { + try (Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) { + assertTrue("Returned incorrect InetAddress", client.getLocalAddress() + .equals(InetAddress.getLocalHost())); + } - client.close(); - server.close(); + try (Socket client = new Socket()) { + client.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0)); + assertTrue(client.getLocalAddress().isAnyLocalAddress()); + } + } } /** * java.net.Socket#getLocalPort() */ public void test_getLocalPort() throws IOException { - ServerSocket server = new ServerSocket(0); - Socket client = new Socket(InetAddress.getLocalHost(), server - .getLocalPort()); - - assertNotSame("Returned incorrect port", 0, client.getLocalPort()); - - client.close(); - server.close(); + try (ServerSocket server = new ServerSocket(0); + Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) { + assertNotSame("Returned incorrect port", 0, client.getLocalPort()); + } } public void test_getLocalSocketAddress() throws IOException { @@ -776,16 +777,16 @@ public void test_getLocalSocketAddress() throws IOException { } public void test_getOOBInline() throws Exception { - Socket theSocket = new Socket(); - - theSocket.setOOBInline(true); - assertTrue("expected OOBIline to be true", theSocket.getOOBInline()); + try (Socket theSocket = new Socket()) { + theSocket.setOOBInline(true); + assertTrue("expected OOBIline to be true", theSocket.getOOBInline()); - theSocket.setOOBInline(false); - assertFalse("expected OOBIline to be false", theSocket.getOOBInline()); + theSocket.setOOBInline(false); + assertFalse("expected OOBIline to be false", theSocket.getOOBInline()); - theSocket.setOOBInline(false); - assertFalse("expected OOBIline to be false", theSocket.getOOBInline()); + theSocket.setOOBInline(false); + assertFalse("expected OOBIline to be false", theSocket.getOOBInline()); + } } /** @@ -870,15 +871,16 @@ public void run() { sinkServer.close(); // Regression test for HARMONY-873 - ServerSocket ss2 = new ServerSocket(0); - Socket s = new Socket("127.0.0.1", ss2.getLocalPort()); - ss2.accept(); - s.shutdownOutput(); - try { - s.getOutputStream(); - fail("should throw SocketException"); - } catch (SocketException e) { - // expected + try (ServerSocket ss2 = new ServerSocket(0); + Socket s = new Socket("127.0.0.1", ss2.getLocalPort())) { + ss2.accept(); + s.shutdownOutput(); + try { + s.getOutputStream(); + fail("should throw SocketException"); + } catch (SocketException e) { + // expected + } } } @@ -938,54 +940,52 @@ public void test_getRemoteSocketAddress() throws IOException { } public void test_getReuseAddress() throws Exception { - Socket theSocket = new Socket(); - theSocket.setReuseAddress(true); - assertTrue("getReuseAddress false when it should be true", theSocket.getReuseAddress()); - theSocket.setReuseAddress(false); - assertFalse("getReuseAddress true when it should be False", theSocket.getReuseAddress()); + try (Socket theSocket = new Socket()) { + theSocket.setReuseAddress(true); + assertTrue("getReuseAddress false when it should be true", theSocket.getReuseAddress()); + theSocket.setReuseAddress(false); + assertFalse("getReuseAddress true when it should be False", + theSocket.getReuseAddress()); + } } public void test_getSendBufferSize() throws Exception { - ServerSocket server = new ServerSocket(0); - Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort()); - client.setSendBufferSize(134); - assertTrue("Incorrect buffer size", client.getSendBufferSize() >= 134); - client.close(); - server.close(); + try (ServerSocket server = new ServerSocket(0); + Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) { + client.setSendBufferSize(134); + assertTrue("Incorrect buffer size", client.getSendBufferSize() >= 134); + } } public void test_getSoLinger() throws Exception { - ServerSocket server = new ServerSocket(0); - Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort()); - client.setSoLinger(true, 200); - assertEquals("Returned incorrect linger", 200, client.getSoLinger()); - client.setSoLinger(false, 0); - client.close(); - server.close(); + try (ServerSocket server = new ServerSocket(0); + Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) { + client.setSoLinger(true, 200); + assertEquals("Returned incorrect linger", 200, client.getSoLinger()); + client.setSoLinger(false, 0); + } } public void test_getSoTimeout() throws Exception { - ServerSocket server = new ServerSocket(0); - Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort()); - final int timeoutSet = 100; - client.setSoTimeout(timeoutSet); - int actualTimeout = client.getSoTimeout(); - // The kernel can round the requested value based on the HZ setting. We allow up to 10ms. - assertTrue("Returned incorrect sotimeout", Math.abs(timeoutSet - actualTimeout) <= 10); - client.close(); - server.close(); + try (ServerSocket server = new ServerSocket(0); + Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) { + final int timeoutSet = 100; + client.setSoTimeout(timeoutSet); + int actualTimeout = client.getSoTimeout(); + // The kernel can round the requested value based on the HZ setting. We allow up to 10ms. + assertTrue("Returned incorrect sotimeout", + Math.abs(timeoutSet - actualTimeout) <= 10); + } } public void test_getTcpNoDelay() throws Exception { - ServerSocket server = new ServerSocket(0); - Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort()); - - boolean bool = !client.getTcpNoDelay(); - client.setTcpNoDelay(bool); - assertTrue("Failed to get no delay setting: " + client.getTcpNoDelay(), client.getTcpNoDelay() == bool); - - client.close(); - server.close(); + try (ServerSocket server = new ServerSocket(0); + Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) { + boolean bool = !client.getTcpNoDelay(); + client.setTcpNoDelay(bool); + assertTrue("Failed to get no delay setting: " + client.getTcpNoDelay(), + client.getTcpNoDelay() == bool); + } } public void test_getTrafficClass() throws Exception { @@ -994,9 +994,11 @@ public void test_getTrafficClass() throws Exception { * does not support the option then it may come back unset even * though we set it so just get the value to make sure we can get it */ - int trafficClass = new Socket().getTrafficClass(); - assertTrue(0 <= trafficClass); - assertTrue(trafficClass <= 255); + try (Socket socket = new Socket()) { + int trafficClass = socket.getTrafficClass(); + assertTrue(0 <= trafficClass); + assertTrue(trafficClass <= 255); + } } /** @@ -1422,18 +1424,22 @@ public TestSocket(SocketImpl impl) throws SocketException { server.close(); // Regression test for HARMONY-1136 - new TestSocket(null).setKeepAlive(true); + try (TestSocket socket = new TestSocket(null)) { + socket.setKeepAlive(true); + } } public void test_setOOBInlineZ() throws Exception { - Socket theSocket = new Socket(); - theSocket.setOOBInline(true); - assertTrue("expected OOBIline to be true", theSocket.getOOBInline()); + try (Socket theSocket = new Socket()) { + theSocket.setOOBInline(true); + assertTrue("expected OOBIline to be true", theSocket.getOOBInline()); + } } public void test_setPerformancePreference_Int_Int_Int() throws IOException { - Socket theSocket = new Socket(); - theSocket.setPerformancePreferences(1, 1, 1); + try (Socket theSocket = new Socket()) { + theSocket.setPerformancePreferences(1, 1, 1); + } } public void test_setReceiveBufferSizeI() throws Exception { @@ -1522,26 +1528,27 @@ public void test_setTrafficClassI() throws Exception { int IPTOS_THROUGHPUT = 0x8; int IPTOS_LOWDELAY = 0x10; - Socket theSocket = new Socket(); + try (Socket theSocket = new Socket()) { - // validate that value set must be between 0 and 255 - try { - theSocket.setTrafficClass(256); - fail("No exception was thrown when traffic class set to 256"); - } catch (IllegalArgumentException expected) { - } + // validate that value set must be between 0 and 255 + try { + theSocket.setTrafficClass(256); + fail("No exception was thrown when traffic class set to 256"); + } catch (IllegalArgumentException expected) { + } - try { - theSocket.setTrafficClass(-1); - fail("No exception was thrown when traffic class set to -1"); - } catch (IllegalArgumentException expected) { - } + try { + theSocket.setTrafficClass(-1); + fail("No exception was thrown when traffic class set to -1"); + } catch (IllegalArgumentException expected) { + } - // now validate that we can set it to some good values - theSocket.setTrafficClass(IPTOS_LOWCOST); - theSocket.setTrafficClass(IPTOS_RELIABILTY); - theSocket.setTrafficClass(IPTOS_THROUGHPUT); - theSocket.setTrafficClass(IPTOS_LOWDELAY); + // now validate that we can set it to some good values + theSocket.setTrafficClass(IPTOS_LOWCOST); + theSocket.setTrafficClass(IPTOS_RELIABILTY); + theSocket.setTrafficClass(IPTOS_THROUGHPUT); + theSocket.setTrafficClass(IPTOS_LOWDELAY); + } } @SuppressWarnings("deprecation") diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ByteBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ByteBufferTest.java index b25c4dec4..db09af8cc 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ByteBufferTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ByteBufferTest.java @@ -17,6 +17,7 @@ package org.apache.harmony.tests.java.nio; +import java.io.RandomAccessFile; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; @@ -29,6 +30,9 @@ import java.nio.LongBuffer; import java.nio.ReadOnlyBufferException; import java.nio.ShortBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; /** @@ -2033,6 +2037,28 @@ public void testWrappedByteBuffer_null_array() { } } + // http://b/34045479 + public void testMappedByteBuffer_Put_ReadOnlyHeapByteBuffer() throws Exception { + // Create a temp file + byte[] data = new byte[] {1, 2, 3, 4}; + Path tempFile = Files.createTempFile("mmap", "test"); + Files.write(tempFile, data); + + // Create a read-only heap buffer + ByteBuffer readOnlySource = ByteBuffer.allocate(4).asReadOnlyBuffer(); + try (RandomAccessFile tempRAF = new RandomAccessFile(tempFile.toFile(), "rw")) { + FileChannel tempFileChannel = tempRAF.getChannel(); + ByteBuffer mappedByteBuffer = + tempFileChannel.map(FileChannel.MapMode.READ_WRITE, 0, tempFileChannel.size()); + + // Try to put a non-empty, read-only heap byte buffer into a mapped byte buffer. + mappedByteBuffer.put(readOnlySource); + tempFileChannel.close(); + } finally { + Files.delete(tempFile); + } + } + private void loadTestData1(byte array[], int offset, int length) { for (int i = 0; i < length; i++) { array[offset + i] = (byte) i; diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java index 8ff795699..e567504db 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java @@ -140,8 +140,8 @@ public void testPutCharBuffer() { } try { buf.put(buf); - fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$ - } catch (ReadOnlyBufferException e) { + fail("Should throw IllegalArgumentException"); //$NON-NLS-1$ + } catch (IllegalArgumentException e) { // expected } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java index f2f1ea41d..1673c164a 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java @@ -137,8 +137,8 @@ public void testPutDoubleBuffer() { } try { buf.put(buf); - fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$ - } catch (ReadOnlyBufferException e) { + fail("Should throw IllegalArgumentException"); //$NON-NLS-1$ + } catch (IllegalArgumentException e) { // expected } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java index 56a14baba..3aec858d3 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java @@ -138,8 +138,8 @@ public void testPutFloatBuffer() { } try { buf.put(buf); - fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$ - } catch (ReadOnlyBufferException e) { + fail("Should throw IllegalArgumentException"); //$NON-NLS-1$ + } catch (IllegalArgumentException e) { // expected } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java index e6187835f..f0dcad01d 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java @@ -138,8 +138,8 @@ public void testPutIntBuffer() { } try { buf.put(buf); - fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$ - } catch (ReadOnlyBufferException e) { + fail("Should throw IllegalArgumentException"); //$NON-NLS-1$ + } catch (IllegalArgumentException e) { // expected } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java index fd6438eb2..283f4f11d 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java @@ -138,8 +138,8 @@ public void testPutLongBuffer() { } try { buf.put(buf); - fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$ - } catch (ReadOnlyBufferException e) { + fail("Should throw IllegalArgumentException"); //$NON-NLS-1$ + } catch (IllegalArgumentException e) { // expected } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java index aab913e92..88858060f 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java @@ -138,8 +138,8 @@ public void testPutShortBuffer() { } try { buf.put(buf); - fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$ - } catch (ReadOnlyBufferException e) { + fail("Should throw IllegalArgumentException"); //$NON-NLS-1$ + } catch (IllegalArgumentException e) { // expected } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ChannelsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ChannelsTest.java index bd7a1ad59..274628a9d 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ChannelsTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ChannelsTest.java @@ -507,6 +507,7 @@ public void testNewWriterWritableByteChannelString_internalBufZero() // null channel try { Writer testWriter = Channels.newWriter(null, Charset.forName(CODE_SET).newEncoder(), -1); + fail(); } catch (NullPointerException expected) { } @@ -514,6 +515,7 @@ public void testNewWriterWritableByteChannelString_internalBufZero() this.fouts = null; try { WritableByteChannel wbChannel = Channels.newChannel(this.fouts); + fail(); } catch (NullPointerException expected) { } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java index d6dacb40d..03c472cc4 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java @@ -1367,6 +1367,7 @@ public void test_readLByteBufferJ_Position_As_Long() throws Exception { ByteBuffer readBuffer = ByteBuffer.allocate(CAPACITY); try { readOnlyFileChannel.read(readBuffer, Long.MAX_VALUE); + fail(); } catch (IOException expected) { } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockDatagramChannel.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockDatagramChannel.java index 8b0f5b3ae..8978714bd 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockDatagramChannel.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockDatagramChannel.java @@ -25,6 +25,7 @@ import java.net.SocketOption; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; +import java.nio.channels.MembershipKey; import java.nio.channels.ServerSocketChannel; import java.nio.channels.spi.SelectorProvider; import java.util.Set; @@ -111,13 +112,23 @@ public T getOption(SocketOption name) throws IOException { return null; } + public DatagramChannel bind(SocketAddress local) throws IOException { + return null; + } + @Override - public Set> supportedOptions() { + public MembershipKey join(InetAddress group, NetworkInterface interf) { return null; } @Override - public DatagramChannel bind(SocketAddress local) throws IOException { + public MembershipKey join(InetAddress group, NetworkInterface interf, InetAddress source) + throws IOException { + return null; + } + + @Override + public Set> supportedOptions() { return null; } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetEncoderTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetEncoderTest.java index 5226ed688..c1c1e93a0 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetEncoderTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetEncoderTest.java @@ -178,7 +178,7 @@ public void testCharsetEncoderCharsetfloatfloatbyteArray() { assertSame(ec.charset(), cs); assertEquals(1.0, ec.averageBytesPerChar(), 0.0); assertTrue(ec.maxBytesPerChar() == MAX_BYTES); - assertSame(ba, ec.replacement()); + assertTrue(Arrays.equals(ba, ec.replacement())); /* * ------------------------ Exceptional cases ------------------------- @@ -996,7 +996,7 @@ public void testReplacement() { byte[] nr = getLegalByteArray(); assertSame(encoder, encoder.replaceWith(nr)); - assertSame(nr, encoder.replacement()); + assertTrue(Arrays.equals(nr, encoder.replacement())); nr = getIllegalByteArray(); try { @@ -1099,7 +1099,7 @@ protected CoderResult implFlush(ByteBuffer out) { } protected void implReplaceWith(byte[] ba) { - assertSame(ba, replacement()); + assertTrue(Arrays.equals(ba, replacement())); } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java index d52e58621..d2f4ca3c4 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java @@ -21,6 +21,7 @@ import java.text.FieldPosition; import java.text.MessageFormat; import java.text.ParsePosition; +import java.util.Arrays; import java.util.Locale; import junit.framework.TestCase; @@ -292,31 +293,6 @@ public void test_formatJLjava_lang_StringBufferLjava_text_FieldPosition() { assertEquals("Wrong choice for 2.5", "Greater than two", r); } - /** - * @tests java.text.ChoiceFormat#getFormats() - */ - public void test_getFormats() { - // Test for method java.lang.Object [] - // java.text.ChoiceFormat.getFormats() - String[] orgFormats = (String[]) formats.clone(); - String[] f = (String[]) f1.getFormats(); - assertTrue("Wrong formats", f.equals(formats)); - f[0] = "Modified"; - assertTrue("Formats copied", !f.equals(orgFormats)); - } - - /** - * @tests java.text.ChoiceFormat#getLimits() - */ - public void test_getLimits() { - // Test for method double [] java.text.ChoiceFormat.getLimits() - double[] orgLimits = (double[]) limits.clone(); - double[] l = f1.getLimits(); - assertTrue("Wrong limits", l.equals(limits)); - l[0] = 3.14527; - assertTrue("Limits copied", !l.equals(orgLimits)); - } - /** * @tests java.text.ChoiceFormat#hashCode() */ @@ -389,20 +365,6 @@ public void test_previousDoubleD() { .previousDouble(Double.NaN))); } - /** - * @tests java.text.ChoiceFormat#setChoices(double[], java.lang.String[]) - */ - public void test_setChoices$D$Ljava_lang_String() { - // Test for method void java.text.ChoiceFormat.setChoices(double [], - // java.lang.String []) - ChoiceFormat f = (ChoiceFormat) f1.clone(); - double[] l = new double[] { 0, 1 }; - String[] fs = new String[] { "0", "1" }; - f.setChoices(l, fs); - assertTrue("Limits copied", f.getLimits() == l); - assertTrue("Formats copied", f.getFormats() == fs); - } - /** * @tests java.text.ChoiceFormat#toPattern() */ diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java index 3d710c595..29470d112 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java @@ -1151,12 +1151,12 @@ public void test_formatDouble_scientificNotation() { // Scientific notation => use significant digit logic // '@' not present: Significant digits: Min: 1, // Max: "min integer digits" (1) + "max fractional digits (0) == 1 - formatTester.format(df, "0E0", 0.0); - formatTester.format(df, "1E0", 1.0); - formatTester.format(df, "1E1", 12.0); - formatTester.format(df, "1E2", 123.0); - formatTester.format(df, "1E3", 1234.0); - formatTester.format(df, "1E4", 9999.0); + formatTester.format(df, "0.E0", 0.0); + formatTester.format(df, "1.E0", 1.0); + formatTester.format(df, "1.E1", 12.0); + formatTester.format(df, "1.E2", 123.0); + formatTester.format(df, "1.E3", 1234.0); + formatTester.format(df, "1.E4", 9999.0); df = new DecimalFormat("##0.00#E0", dfs); // ["##0.00#E0",isDecimalSeparatorAlwaysShown=false,groupingSize=0,multiplier=1, @@ -1699,7 +1699,7 @@ public void test_formatDouble_bug17656132() { // double 9999999999.999998 is decimal 9999999999.9999980926513671875 assertEquals("9999999999.999998", df.format(9999999999.999998)); // double 1E23 is decimal 99999999999999991611392 - assertEquals("9999999999999999", df.format(1E23)); + assertEquals("99999999999999990000000", df.format(1E23)); } public void test_getDecimalFormatSymbols() { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ParseExceptionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ParseExceptionTest.java index c73d8e3c5..307cfb684 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ParseExceptionTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ParseExceptionTest.java @@ -16,6 +16,8 @@ */ package org.apache.harmony.tests.java.text; +import java.io.InputStream; +import java.io.ObjectInputStream; import java.text.DateFormat; import java.text.ParseException; @@ -46,4 +48,17 @@ public void test_getErrorOffset() { assertEquals("getErrorOffsetFailed.", 4, e.getErrorOffset()); } } + + public void test_serialize() throws Exception { + try (InputStream inputStream = getClass().getResourceAsStream( + "/serialization/org/apache/harmony/tests/java/text/ParseException.ser"); + ObjectInputStream ois = new ObjectInputStream(inputStream)) { + + Object object = ois.readObject(); + assertTrue("Not a ParseException", object instanceof ParseException); + ParseException parseException = (ParseException) object; + assertEquals("fred", parseException.getMessage()); + assertEquals(4, parseException.getErrorOffset()); + } + } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/SimpleDateFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/SimpleDateFormatTest.java index 9237fac3b..16217b2f0 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/SimpleDateFormatTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/SimpleDateFormatTest.java @@ -641,11 +641,13 @@ public void test_parse_h_z_2DigitOffsetFromGMT_doesNotParse() throws Exception { SimpleDateFormat pFormat = new SimpleDateFormat("h z", Locale.ENGLISH); try { pFormat.parse("14 GMT-23"); + fail(); } catch (ParseException expected) { } try { pFormat.parse("14 GMT+23"); + fail(); } catch (ParseException expected) { } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayDequeTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayDequeTest.java index b0fc89673..0b832239a 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayDequeTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayDequeTest.java @@ -928,6 +928,7 @@ public void test_spliterator() throws Exception { SpliteratorTester.runOrderedTests(adq); SpliteratorTester.runSizedTests(adq, 16 /* expected size */); SpliteratorTester.runSubSizedTests(adq, 16 /* expected size */); + SpliteratorTester.assertSupportsTrySplit(adq); } public void test_spliterator_CME() throws Exception { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java index cb22613a8..7b3d7d04e 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java @@ -344,7 +344,7 @@ public void test_addAllILjava_util_Collection_3() { } } -// BEGIN android-removed +// BEGIN Android-removed // The spec does not mandate that IndexOutOfBoundsException be thrown in // preference to NullPointerException when the caller desserves both. // @@ -360,7 +360,7 @@ public void test_addAllILjava_util_Collection_3() { // } catch (IndexOutOfBoundsException e) { // } // } -// END android-removed +// END Android-removed /** * java.util.ArrayList#addAll(java.util.Collection) @@ -1129,6 +1129,7 @@ public void test_spliterator() throws Exception { SpliteratorTester.runOrderedTests(list); SpliteratorTester.runSizedTests(list, 16 /* expected size */); SpliteratorTester.runSubSizedTests(list, 16 /* expected size */); + SpliteratorTester.assertSupportsTrySplit(list); } public void test_spliterator_CME() throws Exception { @@ -1178,6 +1179,7 @@ public void test_sublist_spliterator() { SpliteratorTester.runOrderedTests(list); SpliteratorTester.runSizedTests(list, 8 /* expected size */); SpliteratorTester.runSubSizedTests(list, 8 /* expected size */); + SpliteratorTester.assertSupportsTrySplit(list); } /** diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java index 7b0bed1fe..ea6d02e15 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java @@ -4214,6 +4214,7 @@ public void test_asList_spliterator() { assertTrue(list.spliterator().hasCharacteristics(Spliterator.ORDERED)); SpliteratorTester.runOrderedTests(list); + SpliteratorTester.assertSupportsTrySplit(list); } public void test_spliterator_ref() { @@ -4225,6 +4226,7 @@ public void test_spliterator_ref() { SpliteratorTester.runBasicIterationTests(Arrays.spliterator(elements), expected); SpliteratorTester.testSpliteratorNPE(Arrays.spliterator(elements)); + assertNotNull(Arrays.spliterator(elements).trySplit()); Spliterator sp = Arrays.spliterator(elements); assertTrue(sp.hasCharacteristics(Spliterator.ORDERED)); @@ -4249,6 +4251,7 @@ public void test_spliterator_ref_bounds() { SpliteratorTester.runBasicIterationTests(Arrays.spliterator(elements, 2, 16), expected); SpliteratorTester.testSpliteratorNPE(Arrays.spliterator(elements, 2, 16)); + assertNotNull(Arrays.spliterator(elements, 2, 16).trySplit()); Spliterator sp = Arrays.spliterator(elements, 2, 16); assertTrue(sp.hasCharacteristics(Spliterator.ORDERED)); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CalendarTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CalendarTest.java index 06a37c816..6284f0f46 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CalendarTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CalendarTest.java @@ -968,7 +968,8 @@ public void test_getDisplayNamesIILjava_util_Locale() { .getAmPmStrings() : symbols.getEras(); assertDisplayNameMap(values, shortResult, 0); assertDisplayNameMap(values, longResult, 0); - assertDisplayNameMap(values, allResult, 0); + assertTrue(allResult.size() >= shortResult.size()); + assertTrue(allResult.size() >= longResult.size()); break; case Calendar.MONTH: values = symbols.getShortMonths(); @@ -977,8 +978,6 @@ public void test_getDisplayNamesIILjava_util_Locale() { assertDisplayNameMap(values, longResult, 0); assertTrue(allResult.size() >= shortResult.size()); assertTrue(allResult.size() >= longResult.size()); - assertTrue(allResult.size() <= shortResult.size() - + longResult.size()); break; case Calendar.DAY_OF_WEEK: values = symbols.getShortWeekdays(); @@ -987,8 +986,6 @@ public void test_getDisplayNamesIILjava_util_Locale() { assertDisplayNameMap(values, longResult, 1); assertTrue(allResult.size() >= shortResult.size()); assertTrue(allResult.size() >= longResult.size()); - assertTrue(allResult.size() <= shortResult.size() - + longResult.size()); break; default: assertNull(shortResult); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java index 8b45079da..b71fade8d 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java @@ -28,6 +28,7 @@ import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.AbstractList; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -750,10 +751,11 @@ public void test_sortLjava_util_ListLjava_util_Comparator() { //expected } - Mock_ArrayList mal = new Mock_ArrayList(); - - mal.add(new MyInt(1)); - mal.add(new MyInt(2)); + List mal = new AbstractList() { + private final List delegate = Arrays.asList(new MyInt(1), new MyInt(2)); + @Override public Object get(int index) { return delegate.get(index); } + @Override public int size() { return delegate.size(); } + }; try { Collections.sort(mal, comp); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java index a32845c95..ec065a96c 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java @@ -131,6 +131,18 @@ public void test_getInstanceLjava_util_Locale() { fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { } + + try { + Currency.getInstance((Locale) null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + + try { + Currency.getInstance((String) null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } } /** @@ -142,31 +154,31 @@ public void test_getSymbol() { Currency currUS = Currency.getInstance("USD"); Locale.setDefault(Locale.US); - // BEGIN android-changed + // BEGIN Android-changed // KRW currency symbol is \u20a9 since CLDR1.7 release. assertEquals("currK.getSymbol()", "\u20a9", currK.getSymbol()); // IEP currency symbol is IEP since CLDR2.0 release. assertEquals("currI.getSymbol()", "IEP", currI.getSymbol()); - // END android-changed + // END Android-changed assertEquals("currUS.getSymbol()", "$", currUS.getSymbol()); Locale.setDefault(new Locale("en", "IE")); - // BEGIN android-changed + // BEGIN Android-changed assertEquals("currK.getSymbol()", "\u20a9", currK.getSymbol()); assertEquals("currI.getSymbol()", "IEP", currI.getSymbol()); assertEquals("currUS.getSymbol()", "US$", currUS.getSymbol()); - // END android-changed + // END Android-changed // Test what happens if the default is an invalid locale, one with the country Korea (KR) // but a currently unsupported language. "kr" == Kanuri (Korean is actually "ko"). // All these values are those defined in the "root" locale or the currency code if one isn't // defined. Locale.setDefault(new Locale("kr", "KR")); - // BEGIN android-changed + // BEGIN Android-changed assertEquals("currK.getSymbol()", "\u20a9", currK.getSymbol()); assertEquals("currI.getSymbol()", "IEP", currI.getSymbol()); assertEquals("currUS.getSymbol()", "US$", currUS.getSymbol()); - // END android-changed + // END Android-changed } /** @@ -221,10 +233,10 @@ public void test_getSymbolLjava_util_Locale() { // But the RI returns the \uffe5 and Android returns those with \u00a5 String[] yen = new String[] {"JPY", "\u00a5", "\u00a5JP", "JP\u00a5", "\uffe5", "\uffe5JP", "JP\uffe5"}; String[] dollar = new String[] {"USD", "$", "US$", "$US", "$ US"}; - // BEGIN android-changed + // BEGIN Android-changed // Starting CLDR 1.7 release, currency symbol for CAD changed to CA$ in some locales such as ja. String[] cDollar = new String[] {"CA$", "CAD", "$", "Can$", "$CA"}; - // END android-changed + // END Android-changed Currency currE = Currency.getInstance("EUR"); Currency currJ = Currency.getInstance("JPY"); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/EnumSetTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/EnumSetTest.java index 612c9f1b4..9213d7f38 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/EnumSetTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/EnumSetTest.java @@ -1211,11 +1211,15 @@ public void test_retainAll_LCollection() { } set.clear(); - boolean result = set.retainAll(null); - assertFalse("Should return false", result); + try { + set.retainAll(null); + fail("Should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } Collection rawCollection = new ArrayList(); - result = set.retainAll(rawCollection); + boolean result = set.retainAll(rawCollection); assertFalse("Should return false", result); rawCollection.add(EnumFoo.a); @@ -1305,8 +1309,12 @@ public void test_retainAll_LCollection() { } hugeSet.clear(); - result = hugeSet.retainAll(null); - assertFalse(result); + try { + hugeSet.retainAll(null); + fail("Should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } rawCollection = new ArrayList(); result = hugeSet.retainAll(rawCollection); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/GregorianCalendarTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/GregorianCalendarTest.java index bbd8c50ae..1483222c5 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/GregorianCalendarTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/GregorianCalendarTest.java @@ -580,7 +580,9 @@ public void test_rollIZ() { .get(Calendar.YEAR)); assertEquals("Wrong month: " + cal.getTime(), Calendar.JANUARY, cal .get(Calendar.MONTH)); - assertEquals("Wrong date: " + cal.getTime(), 9, cal.get(Calendar.DATE)); + // Android-changed: Bugfix for https://bugs.openjdk.java.net/browse/JDK-6902861. This + // returned 9 before Android O. + assertEquals("Wrong date: " + cal.getTime(), 2, cal.get(Calendar.DATE)); // Regression for HARMONY-4372 cal.set(1994, 11, 30, 5, 0, 0); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java index adf662065..5c7812609 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java @@ -848,6 +848,7 @@ public void test_spliterator_keySet() { SpliteratorTester.runSizedTests(keys.spliterator(), 16); SpliteratorTester.runDistinctTests(keys); + SpliteratorTester.assertSupportsTrySplit(keys); } public void test_spliterator_valueSet() { @@ -879,6 +880,7 @@ public void test_spliterator_valueSet() { assertTrue(values.spliterator().hasCharacteristics(Spliterator.SIZED)); SpliteratorTester.runSizedTests(values.spliterator(), 16); + SpliteratorTester.assertSupportsTrySplit(values); } public void test_spliterator_entrySet() { @@ -914,6 +916,7 @@ public void test_spliterator_entrySet() { SpliteratorTester.runSizedTests(values.spliterator(), 16); SpliteratorTester.runDistinctTests(values); + SpliteratorTester.assertSupportsTrySplit(values); } /** diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java index 95aeb6edc..a42b42fc3 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java @@ -275,6 +275,7 @@ public void test_spliterator() throws Exception { assertTrue(hashSet.spliterator().hasCharacteristics(Spliterator.DISTINCT)); SpliteratorTester.runDistinctTests(keys); + SpliteratorTester.assertSupportsTrySplit(hashSet); } /** diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashtableTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashtableTest.java index 165ff2322..6500ae7c4 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashtableTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashtableTest.java @@ -249,7 +249,7 @@ public void test_elements() { } } -// BEGIN android-removed +// BEGIN Android-removed // implementation dependent // /** // * java.util.Hashtable#elements() @@ -285,7 +285,7 @@ public void test_elements() { // } // assertTrue("unexpected NoSuchElementException", !exception); // } -// END android-removed +// END Android-removed /** * java.util.Hashtable#entrySet() @@ -301,11 +301,11 @@ public void test_entrySet() { while (e.hasMoreElements()) assertTrue("Returned incorrect entry set", s2.contains(e .nextElement())); -// BEGIN android-removed +// BEGIN Android-removed // implementation dependent // assertEquals("Not synchronized", // "java.util.Collections$SynchronizedSet", s.getClass().getName()); -// END android-removed +// END Android-removed boolean exception = false; try { @@ -338,7 +338,7 @@ public void test_getLjava_lang_Object() { assertEquals("Could not retrieve element", "FVal 2", ((String) h.get("FKey 2")) ); -// BEGIN android-removed +// BEGIN Android-removed // implementation dependent // // Regression for HARMONY-262 // ReusableKey k = new ReusableKey(); @@ -358,7 +358,7 @@ public void test_getLjava_lang_Object() { // } catch (NullPointerException e) { // //expected // } -// END android-removed +// END Android-removed } /** @@ -469,11 +469,11 @@ public void test_keySet() { assertTrue("Returned incorrect key set", s .contains(e.nextElement())); -// BEGIN android-removed +// BEGIN Android-removed // implementation dependent // assertEquals("Not synchronized", // "java.util.Collections$SynchronizedSet", s.getClass().getName()); -// END android-removed +// END Android-removed Map map = new Hashtable(101); map.put(new Integer(1), "1"); @@ -548,7 +548,7 @@ public void run() { } } -// BEGIN android-removed +// BEGIN Android-removed // implementation dependent // /** // * java.util.Hashtable#keySet() @@ -592,7 +592,7 @@ public void run() { // } // assertTrue("unexpected NoSuchElementException", !exception); // } -// END android-removed +// END Android-removed /** * java.util.Hashtable#put(java.lang.Object, java.lang.Object) @@ -751,11 +751,11 @@ public void test_values() { while (e.hasMoreElements()) assertTrue("Returned incorrect values", c.contains(e.nextElement())); -// BEGIN android-removed +// BEGIN Android-removed // implementation dependent // assertEquals("Not synchronized", // "java.util.Collections$SynchronizedCollection", c.getClass().getName()); -// END android-removed +// END Android-removed Hashtable myHashtable = new Hashtable(); for (int i = 0; i < 100; i++) diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java index ee1a3721c..bc84a9a00 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java @@ -448,12 +448,12 @@ public void test_equalsLjava_lang_Object() { public void test_Serialization() throws Exception { IdentityHashMap map = new IdentityHashMap(); map.put(ID, "world"); - // BEGIN android-added + // BEGIN Android-added // Regression test for null key in serialized IdentityHashMap (1178549) // Together with this change the IdentityHashMap.golden.ser resource // was replaced by a version that contains a map with a null key. map.put(null, "null"); - // END android-added + // END Android-added SerializationTest.verifySelf(map, comparator); SerializationTest.verifyGolden(this, map, comparator); } @@ -467,7 +467,7 @@ protected void setUp() { objArray2 = new Object[hmSize]; for (int i = 0; i < objArray.length; i++) { objArray[i] = new Integer(i); - // android-changed: the containsKey test requires unique strings. + // Android-changed: the containsKey test requires unique strings. objArray2[i] = new String(objArray[i].toString()); } @@ -996,6 +996,7 @@ public void test_spliterator_keySet() { SpliteratorTester.runBasicIterationTests(keys.spliterator(), expectedKeys); SpliteratorTester.runBasicSplitTests(keys, expectedKeys); SpliteratorTester.testSpliteratorNPE(keys.spliterator()); + SpliteratorTester.assertSupportsTrySplit(keys); } public void test_spliterator_valueSet() { @@ -1023,6 +1024,7 @@ public void test_spliterator_valueSet() { SpliteratorTester.runBasicIterationTests(values.spliterator(), expectedValues); SpliteratorTester.runBasicSplitTests(values, expectedValues); SpliteratorTester.testSpliteratorNPE(values.spliterator()); + SpliteratorTester.assertSupportsTrySplit(values); } public void test_spliterator_entrySet() { @@ -1053,6 +1055,7 @@ public void test_spliterator_entrySet() { SpliteratorTester.runBasicIterationTests(values.spliterator(), expectedValues); SpliteratorTester.runBasicSplitTests(values, expectedValues, comparator); SpliteratorTester.testSpliteratorNPE(values.spliterator()); + SpliteratorTester.assertSupportsTrySplit(values); } public void test_replaceAll() { @@ -1080,6 +1083,7 @@ public String apply(String s, String s2) { return ""; } }); + fail(); } catch (ConcurrentModificationException expected) {} } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/InvalidPropertiesFormatExceptionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/InvalidPropertiesFormatExceptionTest.java deleted file mode 100644 index 10bb50e4d..000000000 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/InvalidPropertiesFormatExceptionTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ -package org.apache.harmony.tests.java.util; - -import java.io.NotSerializableException; -import java.util.InvalidPropertiesFormatException; - -import org.apache.harmony.testframework.serialization.SerializationTest; - -public class InvalidPropertiesFormatExceptionTest extends - junit.framework.TestCase { - - /** - * java.util.InvalidPropertiesFormatException#SerializationTest() - */ - public void test_Serialization() throws Exception { - InvalidPropertiesFormatException ipfe = new InvalidPropertiesFormatException( - "Hey, this is InvalidPropertiesFormatException"); - try { - SerializationTest.verifySelf(ipfe); - } catch (NotSerializableException e) { - // expected - } - } - - /** - * {@link java.util.InvalidPropertiesFormatException#InvalidPropertiesFormatException(Throwable)} - */ - public void test_Constructor_Ljava_lang_Throwable() { - Throwable throwable = new Throwable(); - InvalidPropertiesFormatException exception = new InvalidPropertiesFormatException( - throwable); - assertEquals("the casue did not equals argument passed in constructor", - throwable, exception.getCause()); - } - -} diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashSetTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashSetTest.java index f3340d140..d403b2ac1 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashSetTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashSetTest.java @@ -348,6 +348,7 @@ public void test_spliterator() throws Exception { assertTrue(hashSet.spliterator().hasCharacteristics(Spliterator.DISTINCT)); SpliteratorTester.runDistinctTests(keys); + SpliteratorTester.assertSupportsTrySplit(hashSet); } /** diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java index da01e342d..ea829c9a9 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java @@ -963,6 +963,7 @@ public void test_spliterator() throws Exception { SpliteratorTester.runOrderedTests(list); SpliteratorTester.runSizedTests(list, 16 /* expected size */); SpliteratorTester.runSubSizedTests(list, 16 /* expected size */); + SpliteratorTester.assertSupportsTrySplit(list); } public void test_spliterator_CME() throws Exception { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LocaleTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LocaleTest.java index dfb2d9605..b4cf99bf4 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LocaleTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LocaleTest.java @@ -124,7 +124,7 @@ public void test_equalsLjava_lang_Object() { * java.util.Locale#getAvailableLocales() */ public void test_getAvailableLocales() { -// BEGIN android-changed +// BEGIN Android-changed // Test for method java.util.Locale [] // java.util.Locale.getAvailableLocales() // Assumes there will generally be about 10+ available locales... @@ -139,7 +139,7 @@ public void test_getAvailableLocales() { } catch (Exception e) { fail("Exception during test : " + e.getMessage()); } -// END android-changed +// END Android-changed } /** @@ -172,11 +172,6 @@ public void test_getDisplayCountry() { assertTrue("Returned incorrect country: " + testLocale.getDisplayCountry(), testLocale .getDisplayCountry().equals("Canada")); - - // Regression for Harmony-1146 - Locale l_countryCD = new Locale("", "CD"); - assertEquals("Congo (DRC)", - l_countryCD.getDisplayCountry()); } public void test_getDisplayCountryLjava_util_Locale() { @@ -308,14 +303,14 @@ public void test_getISOLanguages() { String[] isoLang = Locale.getISOLanguages(); int length = isoLang.length; - // BEGIN android-changed + // BEGIN Android-changed // Language codes are 2- and 3-letter, with preference given // to 2-letter codes where possible. 3-letter codes are used // when lack a 2-letter equivalent. assertTrue("Random element in wrong format.", (isoLang[length / 2].length() == 2 || isoLang[length / 2].length() == 3) && isoLang[length / 2].toLowerCase().equals(isoLang[length / 2])); - // END android-changed + // END Android-changed assertTrue("Wrong number of ISOLanguages.", length > 130); } @@ -410,7 +405,7 @@ public void test_constantROOT() { assertEquals("", root.getVariant()); } -// BEGIN android-removed +// BEGIN Android-removed // These locales are not part of the android reference impl // // Regression Test for HARMONY-2953 // public void test_getISO() { @@ -426,7 +421,7 @@ public void test_constantROOT() { // List countries = Arrays.asList(Locale.getISOCountries()); // assertTrue(countries.contains("CS")); // } -// END android-removed +// END Android-removed /** * Sets up the fixture, for example, open a network connection. This method diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PriorityQueueTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PriorityQueueTest.java index d4061dc39..b6d95d47c 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PriorityQueueTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PriorityQueueTest.java @@ -800,6 +800,7 @@ public void test_spliterator() throws Exception { SpliteratorTester.runSizedTests(list, 16 /* expected size */); SpliteratorTester.runSubSizedTests(list, 16 /* expected size */); + SpliteratorTester.assertSupportsTrySplit(list); } public void test_spliterator_CME() throws Exception { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PropertiesTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PropertiesTest.java index 27cae4e36..038cc7bf9 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PropertiesTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/PropertiesTest.java @@ -20,15 +20,18 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.CharArrayReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.PrintWriter; +import java.io.Reader; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.InvalidPropertiesFormatException; @@ -389,21 +392,21 @@ public void test_loadLjava_io_Reader() throws IOException { prop = new Properties(); Properties expected = new Properties(); - expected.put("a", "\u0000"); + expected.put("a", ""); prop.load(new ByteArrayInputStream("a=\\".getBytes())); - assertEquals("Failed to read trailing slash value", expected, prop); + assertEquals("Failed to trim trailing slash value", expected, prop); prop = new Properties(); expected = new Properties(); - expected.put("a", "\u1234\u0000"); + expected.put("a", "\u1234"); prop.load(new ByteArrayInputStream("a=\\u1234\\".getBytes())); - assertEquals("Failed to read trailing slash value #2", expected, prop); + assertEquals("Failed to trim trailing slash value #2", expected, prop); prop = new Properties(); expected = new Properties(); expected.put("a", "q"); prop.load(new ByteArrayInputStream("a=\\q".getBytes())); - assertEquals("Failed to read slash value #3", expected, prop); + assertEquals("Failed to skip slash value #3", expected, prop); } /** @@ -1086,6 +1089,47 @@ public void testLoadReader() throws IOException { inputStream.close(); } + /** + * Checks the example given in the documentation of a single property split over + * multiple lines separated by a backslash and newline character. + */ + public void testSingleProperty_multipleLinesJoinedByBackslash() throws Exception { + String propertyString = "fruits apple, banana, pear, \\\n" + + " cantaloupe, watermelon, \\\n" + + " kiwi, mango"; + checkSingleProperty("fruits", "apple, banana, pear, cantaloupe, watermelon, kiwi, mango", + propertyString); + } + + /** + * Checks that a trailing backslash at the end of the single line of input is ignored. + * This is similar to a check in {@link #test_loadLjava_io_Reader()} that uses an + * InputStream and {@link Properties#equals(Object)} . + */ + public void testSingleProperty_oneLineWithTrailingBackslash() throws Exception { + checkSingleProperty("key", "value", "key=value\\"); + } + + /** + * Checks that a trailing backslash at the end of the single line of input is ignored, + * even when that line has a newline. + */ + public void testSingleProperty_oneLineWithTrailingBackslash_newline() throws Exception { + checkSingleProperty("key", "value", "key=value\\\r"); + checkSingleProperty("key", "value", "key=value\\\n"); + checkSingleProperty("key", "value", "key=value\\\r\n"); + } + + private static void checkSingleProperty(String key, String value, String serialized) + throws IOException { + Properties properties = new Properties(); + try (Reader reader = new CharArrayReader(serialized.toCharArray())) { + properties.load(reader); + assertEquals(Collections.singleton(key), properties.keySet()); + assertEquals(value, properties.getProperty(key)); + } + } + /** * Sets up the fixture, for example, open a network connection. This method * is called before a test is executed. diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ResourceBundleTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ResourceBundleTest.java index db2ee7ab9..5d35e4cbf 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ResourceBundleTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ResourceBundleTest.java @@ -39,6 +39,17 @@ public void test_getCandidateLocales() throws Exception { assertEquals("[de_CH, de, ]", c.getCandidateLocales("base", new Locale("de", "CH")).toString()); } + public void test_getBaseName() { + String name = "tests.support.Support_TestResource"; + ResourceBundle bundle = ResourceBundle.getBundle(name); + assertEquals(name, bundle.getBaseBundleName()); + + bundle = ResourceBundle.getBundle(name, Locale.getDefault()); + assertEquals(name, bundle.getBaseBundleName()); + + assertNull(new Mock_ResourceBundle().getBaseBundleName()); + } + /** * java.util.ResourceBundle#getBundle(java.lang.String, * java.util.Locale) diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerTest.java index d67b1c906..909dd27d4 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerTest.java @@ -46,6 +46,10 @@ import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.InputMismatchException; @@ -118,6 +122,38 @@ public void test_ConstructorLjava_io_File() throws IOException { // TODO: test if the default charset is used. } + + /** + * @tests java.util.Scanner#Scanner(Path) + */ + public void test_ConstructorLjava_nio_file_Path() throws IOException { + Path tmpFilePath = Files.createTempFile("TestFileForScanner", ".tmp"); + String testString = "test"; + try (OutputStream os = Files.newOutputStream(tmpFilePath)) { + os.write(testString.getBytes()); + } + try (Scanner s = new Scanner(tmpFilePath)){ + assertEquals(testString, s.next()); + assertFalse(s.hasNext()); + } + } + + /** + * @tests java.util.Scanner#Scanner(Path) + */ + public void test_ConstructorLjava_nio_file_Path_Exception() throws IOException { + Path nonExistentFilePath = Paths.get("testPath"); + try (Scanner s = new Scanner(nonExistentFilePath)) { + fail(); + } catch (NoSuchFileException expected) { + } + + try (Scanner s = new Scanner((Path) null)) { + fail(); + } catch (NullPointerException expected) { + } + } + /** * @tests java.util.Scanner#Scanner(File, String) */ @@ -185,6 +221,83 @@ public void test_ConstructorLjava_io_FileLjava_lang_String() // TODO: test if the specified charset is used. } + /** + * @tests java.util.Scanner#Scanner(Path, String) + */ + public void test_ConstructorLjava_nio_file_PathLjava_lang_String() + throws IOException { + Path tmpFilePath = Files.createTempFile("TestFileForScanner", ".tmp"); + String testString = "परीक्षण"; + try (OutputStream os = Files.newOutputStream(tmpFilePath)) { + os.write(testString.getBytes()); + } + // With correct charset. + try (Scanner s = new Scanner(tmpFilePath, Charset.defaultCharset().name())){ + assertEquals(testString, s.next()); + assertFalse(s.hasNext()); + } + // With incorrect charset. + try (Scanner s = new Scanner(tmpFilePath, "US-ASCII")){ + if (s.next().equals(testString)) { + fail("Should not be able to read with incorrect charset."); + } + } + } + + /** + * @tests java.util.Scanner#Scanner(Path, String) + */ + public void test_ConstructorLjava_nio_file_PathLjava_lang_String_Exception() + throws IOException { + Path nonExistentFilePath = Paths.get("nonExistentFile"); + Path existentFilePath = Files.createTempFile("TestFileForScanner", ".tmp"); + + // File doesn't exist. + try (Scanner s = new Scanner(nonExistentFilePath, Charset.defaultCharset().name())) { + fail(); + } catch (NoSuchFileException expected) { + } + + // Exception order test. + try { + s = new Scanner(nonExistentFilePath, null); + fail(); + } catch (NullPointerException expected) { + } + + // Invalid charset. + try { + s = new Scanner(existentFilePath, "invalid charset"); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Scanner(Path = null, Charset = null) + try (Scanner s = new Scanner((Path) null, null)) { + fail(); + } catch (NullPointerException expected) { + } + + // Scanner(Path = null, Charset = UTF-8) + try (Scanner s = new Scanner((Path) null, "UTF-8")) { + fail(); + } catch (NullPointerException expected) { + } + + // Scanner(Path = null, Charset = invalid) + try (Scanner s = new Scanner((Path) null, "invalid")) { + fail(); + } catch (NullPointerException expected) { + } + + // Scanner(Path, Charset = null) + try (Scanner s = new Scanner(existentFilePath, null)) { + fail(); + } catch (NullPointerException expected) { + } + } + + /** * @tests java.util.Scanner#Scanner(InputStream) */ diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeSetTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeSetTest.java index 8d1c3f841..008701383 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeSetTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeSetTest.java @@ -344,6 +344,7 @@ public void test_spliterator() throws Exception { assertTrue(treeSet.spliterator().hasCharacteristics(Spliterator.DISTINCT)); SpliteratorTester.runDistinctTests(keys); + SpliteratorTester.assertSupportsTrySplit(treeSet); } /** diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java index a9f64a2ee..f889c8e7e 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java @@ -30,6 +30,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.Spliterator; import java.util.Vector; @@ -850,6 +851,15 @@ public void test_lastIndexOfLjava_lang_ObjectI() { } } + // http://b/30974375 + public void test_listIterator_addAndPrevious() { + ListIterator it = new Vector().listIterator(); + assertFalse(it.hasNext()); + it.add("value"); + assertEquals("value", it.previous()); + assertTrue(it.hasNext()); + } + /** * java.util.Vector#remove(int) */ @@ -1447,6 +1457,7 @@ public void test_spliterator() throws Exception { SpliteratorTester.runOrderedTests(list); SpliteratorTester.runSizedTests(list, 16 /* expected size */); SpliteratorTester.runSubSizedTests(list, 16 /* expected size */); + SpliteratorTester.assertSupportsTrySplit(list); } public void test_spliterator_CME() throws Exception { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java index 302b50583..312542d1d 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java @@ -310,14 +310,20 @@ public void test_keySet() { long startTime = System.currentTimeMillis(); // We use a busy wait loop here since we cannot know when the ReferenceQueue // daemon will enqueue the cleared references on their internal reference - // queues. The current timeout is 5 seconds. + // queues. + // The timeout after which the reference should be cleared. This test used to + // be flaky when it was set to 5 seconds. Daemons.MAX_FINALIZE_NANOS is + // currently 10 seconds so that seems like the correct value. + // We allow an extra 500msec buffer to minimize races between finalizer, + // keySet.size() evaluation and time check. + long timeout = 10000 + 500; do { try { Thread.sleep(100); } catch (InterruptedException e) { } } while (keySet.size() != 99 && - System.currentTimeMillis() - startTime < 5000); + System.currentTimeMillis() - startTime < timeout); assertEquals("Incorrect number of keys returned after gc,", 99, keySet.size()); } @@ -539,6 +545,7 @@ public void test_spliterator_keySet() { assertTrue(keys.spliterator().hasCharacteristics(Spliterator.DISTINCT)); SpliteratorTester.runDistinctTests(keys); + SpliteratorTester.assertSupportsTrySplit(keys); } public void test_spliterator_valueSet() { @@ -602,6 +609,7 @@ public void test_spliterator_entrySet() { assertTrue(values.spliterator().hasCharacteristics(Spliterator.DISTINCT)); SpliteratorTester.runDistinctTests(values); + SpliteratorTester.assertSupportsTrySplit(values); } /** diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java index 958d9bcf3..12890c875 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java @@ -36,6 +36,7 @@ import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Vector; @@ -59,7 +60,7 @@ public class JarFileTest extends TestCase { - // BEGIN android-added + // BEGIN Android-added public byte[] getAllBytesFromStream(InputStream is) throws IOException { ByteArrayOutputStream bs = new ByteArrayOutputStream(); byte[] buf = new byte[666]; @@ -72,7 +73,7 @@ public byte[] getAllBytesFromStream(InputStream is) throws IOException { return bs.toByteArray(); } - // END android-added + // END Android-added private final String jarName = "hyts_patch.jar"; // a 'normal' jar file @@ -571,9 +572,9 @@ public void test_getInputStreamLjava_util_jar_JarEntry_subtest0() throws Excepti JarFile jar = new JarFile(signedFile); JarEntry entry = new JarEntry(entryName3); InputStream in = jar.getInputStream(entry); - // BEGIN android-added + // BEGIN Android-added byte[] dummy = getAllBytesFromStream(in); - // END android-added + // END Android-added assertNull("found certificates", entry.getCertificates()); } catch (Exception e) { fail("Exception during test 4: " + e); @@ -584,9 +585,9 @@ public void test_getInputStreamLjava_util_jar_JarEntry_subtest0() throws Excepti JarEntry entry = jar.getJarEntry(entryName3); entry.setSize(1076); InputStream in = jar.getInputStream(entry); - // BEGIN android-added + // BEGIN Android-added byte[] dummy = getAllBytesFromStream(in); - // END android-added + // END Android-added fail("SecurityException should be thrown."); } catch (SecurityException e) { // expected @@ -977,9 +978,9 @@ public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException { try { JarFile jf = new JarFile(localFile); java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName)); - // BEGIN android-removed + // BEGIN Android-removed // jf.close(); - // END android-removed + // END Android-removed assertTrue("Returned invalid stream", is.available() > 0); int r = is.read(b, 0, 1024); is.close(); @@ -989,9 +990,9 @@ public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException { } String contents = sb.toString(); assertTrue("Incorrect stream read", contents.indexOf("bar") > 0); - // BEGIN android-added + // BEGIN Android-added jf.close(); - // END android-added + // END Android-added } catch (Exception e) { fail("Exception during test: " + e.toString()); } @@ -1124,4 +1125,45 @@ protected Object engineGetParameter(String param) throws InvalidParameterExcepti } } } + + /** + * java.util.jar.JarFile#stream() + */ + public void test_stream() throws Exception { + /* + * Note only (and all of) the following should be contained in the file + * META-INF/ META-INF/MANIFEST.MF Blah.txt foo/ foo/bar/ foo/bar/A.class + */ + Support_Resources.copyFile(resources, null, jarName); + JarFile jarFile = new JarFile(new File(resources, jarName)); + + final List names = new ArrayList<>(); + jarFile.stream().forEach((ZipEntry entry) -> names.add(entry.getName())); + assertEquals(Arrays.asList("META-INF/", "META-INF/MANIFEST.MF", "Blah.txt", "foo/", "foo/bar/", + "foo/bar/A.class"), names); + jarFile.close(); + } + + + /** + * hyts_metainf.jar contains an additional entry in META-INF (META-INF/bad_checksum.txt), + * that has been altered since jar signing - we expect to detect a mismatching digest. + */ + public void test_metainf_verification() throws Exception { + String jarFilename = "hyts_metainf.jar"; + Support_Resources.copyFile(resources, null, jarFilename); + try (JarFile jarFile = new JarFile(new File(resources, jarFilename))) { + + JarEntry jre = new JarEntry("META-INF/bad_checksum.txt"); + InputStream in = jarFile.getInputStream(jre); + + byte[] buffer = new byte[1024]; + try { + while (in.available() > 0) { + in.read(buffer); + } + fail("SecurityException expected"); + } catch (SecurityException expected) {} + } + } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarInputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarInputStreamTest.java index 9d4224ae5..bd66bbe4f 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarInputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarInputStreamTest.java @@ -403,4 +403,35 @@ public void test_getNextEntry() throws Exception { // expected } } + + /** + * hyts_metainf.jar contains an additional entry in META-INF (META-INF/bad_checksum.txt), + * that has been altered since jar signing - we expect to detect a mismatching digest. + */ + public void test_metainf_verification() throws Exception { + String jarFilename = "hyts_metainf.jar"; + File resources = Support_Resources.createTempFolder(); + Support_Resources.copyFile(resources, null, jarFilename); + InputStream is = Support_Resources.getStream(jarFilename); + + try (JarInputStream jis = new JarInputStream(is, true)) { + JarEntry je = jis.getNextJarEntry(); + je = jis.getNextJarEntry(); + je = jis.getNextJarEntry(); + je = jis.getNextJarEntry(); + + if (!je.getName().equals("META-INF/bad_checksum.txt")) { + fail("Expected META-INF/bad_checksum.txt as a 4th entry, got:" + je.getName()); + } + byte[] buffer = new byte[1024]; + int length = 0; + try { + while (length >= 0) { + length = jis.read(buffer); + } + fail("SecurityException expected"); + } catch (SecurityException expected) {} + } + } + } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/Adler32Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/Adler32Test.java index 755589609..c7e18c072 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/Adler32Test.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/Adler32Test.java @@ -1,13 +1,13 @@ -/* +/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. @@ -16,6 +16,7 @@ */ package org.apache.harmony.tests.java.util.zip; +import java.nio.ByteBuffer; import java.util.zip.Adler32; public class Adler32Test extends junit.framework.TestCase { @@ -158,6 +159,38 @@ public void test_updateI() { } + private void assertChecksumFromByteBuffer(long expectedChecksum, ByteBuffer byteBuffer) { + Adler32 checksum = new Adler32(); + checksum.update(byteBuffer); + assertEquals("update(ByteBuffer) failed to update the checksum to the correct value ", + expectedChecksum, checksum.getValue()); + assertEquals(0, byteBuffer.remaining()); + } + + /** + * java.util.zip.Adler32#update(ByteBuffer) + */ + public void test_update$ByteBuffer() { + // test methods of java.util.zip.update(ByteBuffer) + // Heap ByteBuffer + ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[] {1,2,3,4}); + byteBuffer.position(2); + assertChecksumFromByteBuffer(0xc0008, byteBuffer); + + // Direct ByteBuffer + byteBuffer.flip(); + byteBuffer = ByteBuffer.allocateDirect(4).put(byteBuffer); + byteBuffer.flip(); + byteBuffer.position(2); + assertChecksumFromByteBuffer(0xc0008, byteBuffer); + + Adler32 checksum = new Adler32(); + try { + checksum.update((ByteBuffer)null); + fail(); + } catch (NullPointerException expected) {} + } + @Override protected void setUp() { } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CRC32Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CRC32Test.java index 30bdf9f08..3d840cea7 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CRC32Test.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CRC32Test.java @@ -1,13 +1,13 @@ -/* +/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. @@ -16,6 +16,7 @@ */ package org.apache.harmony.tests.java.util.zip; +import java.nio.ByteBuffer; import java.util.zip.CRC32; public class CRC32Test extends junit.framework.TestCase { @@ -174,6 +175,39 @@ public void test_updateI() { 2, r); } + + private void assertChecksumFromByteBuffer(long expectedChecksum, ByteBuffer byteBuffer) { + CRC32 checksum = new CRC32(); + checksum.update(byteBuffer); + assertEquals("update(ByteBuffer) failed to update the checksum to the correct value ", + expectedChecksum, checksum.getValue()); + assertEquals(0, byteBuffer.remaining()); + } + + /** + * java.util.zip.CRC32#update(ByteBuffer) + */ + public void test_update$ByteBuffer() { + // test methods of java.util.zip.update(ByteBuffer) + // Heap ByteBuffer + ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[] {1,2,3,4}); + byteBuffer.position(2); + assertChecksumFromByteBuffer(0x6d998525, byteBuffer); + + // Direct ByteBuffer + byteBuffer.flip(); + byteBuffer = ByteBuffer.allocateDirect(4).put(byteBuffer); + byteBuffer.flip(); + byteBuffer.position(2); + assertChecksumFromByteBuffer(0x6d998525, byteBuffer); + + CRC32 checksum = new CRC32(); + try { + checksum.update((ByteBuffer)null); + fail(); + } catch (NullPointerException expected) {} + } + @Override protected void setUp() { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterInputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterInputStreamTest.java index 7ed59169d..435ffd8c6 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterInputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterInputStreamTest.java @@ -24,11 +24,16 @@ import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.DeflaterInputStream; - -import junit.framework.TestCase; import libcore.io.Streams; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.DisableResourceLeakageDetection; +import org.junit.Rule; +import org.junit.rules.TestRule; -public class DeflaterInputStreamTest extends TestCase { +public class DeflaterInputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); private static final String TEST_STR = "Hi,this is a test"; @@ -142,21 +147,24 @@ public void testRead() throws IOException { } public void testRead_golden() throws Exception { - DeflaterInputStream dis = new DeflaterInputStream(is); - byte[] contents = Streams.readFully(dis); - assertTrue(Arrays.equals(TEST_STRING_DEFLATED_BYTES, contents)); + try (DeflaterInputStream dis = new DeflaterInputStream(is)) { + byte[] contents = Streams.readFully(dis); + assertTrue(Arrays.equals(TEST_STRING_DEFLATED_BYTES, contents)); + } - byte[] result = new byte[32]; - dis = new DeflaterInputStream(new ByteArrayInputStream(TEST_STR.getBytes("UTF-8"))); - int count = 0; - int bytesRead = 0; - while ((bytesRead = dis.read(result, count, 4)) != -1) { - count += bytesRead; + try (DeflaterInputStream dis = new DeflaterInputStream( + new ByteArrayInputStream(TEST_STR.getBytes("UTF-8")))) { + byte[] result = new byte[32]; + int count = 0; + int bytesRead; + while ((bytesRead = dis.read(result, count, 4)) != -1) { + count += bytesRead; + } + assertEquals(23, count); + byte[] splicedResult = new byte[23]; + System.arraycopy(result, 0, splicedResult, 0, 23); + assertTrue(Arrays.equals(TEST_STRING_DEFLATED_BYTES, splicedResult)); } - assertEquals(23, count); - byte[] splicedResult = new byte[23]; - System.arraycopy(result, 0, splicedResult, 0, 23); - assertTrue(Arrays.equals(TEST_STRING_DEFLATED_BYTES, splicedResult)); } public void testRead_leavesBufUnmodified() throws Exception { @@ -181,12 +189,16 @@ public void testRead_leavesBufUnmodified() throws Exception { public void testReadByteArrayIntInt() throws IOException { byte[] buf1 = new byte[256]; byte[] buf2 = new byte[256]; - DeflaterInputStream dis = new DeflaterInputStream(is); - assertEquals(23, dis.read(buf1, 0, 256)); - dis = new DeflaterInputStream(is); - assertEquals(8, dis.read(buf2, 0, 256)); + try (DeflaterInputStream dis = new DeflaterInputStream(is)) { + assertEquals(23, dis.read(buf1, 0, 256)); + } + + try (DeflaterInputStream dis = new DeflaterInputStream(is)) { + assertEquals(8, dis.read(buf2, 0, 256)); + } + is = new ByteArrayInputStream(TEST_STR.getBytes("UTF-8")); - dis = new DeflaterInputStream(is); + DeflaterInputStream dis = new DeflaterInputStream(is); assertEquals(1, dis.available()); assertEquals(120, dis.read()); assertEquals(1, dis.available()); @@ -300,15 +312,23 @@ public void testReset() throws IOException { */ public void testSkip() throws IOException { byte[] buf = new byte[1024]; - DeflaterInputStream dis = new DeflaterInputStream(is); - assertEquals(1, dis.available()); - dis.skip(1); - assertEquals(1, dis.available()); - assertEquals(22, dis.read(buf, 0, 1024)); - assertEquals(0, dis.available()); - assertEquals(0, dis.available()); + try (DeflaterInputStream dis = new DeflaterInputStream(is)) { + assertEquals(1, dis.available()); + dis.skip(1); + assertEquals(1, dis.available()); + assertEquals(22, dis.read(buf, 0, 1024)); + assertEquals(0, dis.available()); + assertEquals(0, dis.available()); + is = new ByteArrayInputStream(TEST_STR.getBytes("UTF-8")); + } + is = new ByteArrayInputStream(TEST_STR.getBytes("UTF-8")); - dis = new DeflaterInputStream(is); + try (DeflaterInputStream dis = new DeflaterInputStream(is)) { + assertEquals(23, dis.skip(Long.MAX_VALUE)); + assertEquals(0, dis.available()); + } + + DeflaterInputStream dis = new DeflaterInputStream(is); assertEquals(1, dis.available()); dis.skip(56); assertEquals(0, dis.available()); @@ -324,19 +344,20 @@ public void testSkip() throws IOException { } catch (IOException e) { // expected } - - is = new ByteArrayInputStream(TEST_STR.getBytes("UTF-8")); - dis = new DeflaterInputStream(is); - assertEquals(23, dis.skip(Long.MAX_VALUE)); - assertEquals(0, dis.available()); } /** * DeflaterInputStream#DeflaterInputStream(InputStream) */ - public void testDeflaterInputStreamInputStream() { + @DisableResourceLeakageDetection( + why = "DeflaterInputStream does not clean up the default Deflater created in the" + + " constructor if the constructor fails; i.e. constructor calls" + + " this(..., new Deflater(), ...) and that constructor fails but does not know" + + " that it needs to call Deflater.end() as the caller has no access to it", + bug = "31798154") + public void testDeflaterInputStreamInputStream() throws IOException { // ok - new DeflaterInputStream(is); + new DeflaterInputStream(is).close(); // fail try { new DeflaterInputStream(null); @@ -356,21 +377,26 @@ public void testDataFormatException() { /** * DeflaterInputStream#DeflaterInputStream(InputStream, Deflater) */ - public void testDeflaterInputStreamInputStreamDeflater() { + public void testDeflaterInputStreamInputStreamDeflater() throws IOException { // ok - new DeflaterInputStream(is, new Deflater()); - // fail - try { - new DeflaterInputStream(is, null); - fail("should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } + Deflater deflater = new Deflater(); try { - new DeflaterInputStream(null, new Deflater()); - fail("should throw NullPointerException"); - } catch (NullPointerException e) { - // expected + new DeflaterInputStream(is, deflater).close(); + // fail + try { + new DeflaterInputStream(is, null); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + try { + new DeflaterInputStream(null, deflater); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + } finally { + deflater.end(); } } @@ -379,37 +405,42 @@ public void testDeflaterInputStreamInputStreamDeflater() { */ public void testDeflaterInputStreamInputStreamDeflaterInt() { // ok - new DeflaterInputStream(is, new Deflater(), 1024); - // fail + Deflater deflater = new Deflater(); try { - new DeflaterInputStream(is, null, 1024); - fail("should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } - try { - new DeflaterInputStream(null, new Deflater(), 1024); - fail("should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } - try { - new DeflaterInputStream(is, new Deflater(), -1); - fail("should throw IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } - try { - new DeflaterInputStream(null, new Deflater(), -1); - fail("should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } - try { - new DeflaterInputStream(is, null, -1); - fail("should throw NullPointerException"); - } catch (NullPointerException e) { - // expected + new DeflaterInputStream(is, deflater, 1024); + // fail + try { + new DeflaterInputStream(is, null, 1024); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + try { + new DeflaterInputStream(null, deflater, 1024); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + try { + new DeflaterInputStream(is, deflater, -1); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + try { + new DeflaterInputStream(null, deflater, -1); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + try { + new DeflaterInputStream(is, null, -1); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + } finally { + deflater.end(); } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterOutputStreamTest.java index e4be19824..30defe239 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterOutputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterOutputStreamTest.java @@ -26,10 +26,15 @@ import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.DisableResourceLeakageDetection; +import org.junit.Rule; +import org.junit.rules.TestRule; -import junit.framework.TestCase; - -public class DeflaterOutputStreamTest extends TestCase { +public class DeflaterOutputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); private class MyDeflaterOutputStream extends DeflaterOutputStream { boolean deflateFlag = false; @@ -105,6 +110,7 @@ public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_Deflater() throw dos.write(byteArray); dos.close(); f1.delete(); + defl.end(); } /** @@ -169,19 +175,27 @@ public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_DeflaterI() dos.write(byteArray); dos.close(); f1.delete(); + defl.end(); } /** * java.util.zip.DeflaterOutputStream#close() */ + @DisableResourceLeakageDetection( + why = "DeflaterOutputStream.close() does not work properly if finish() throws an" + + " exception; DeflaterOutputStream.finish() throws an exception if the" + + " underlying OutputStream has been closed and the Deflater still has data to" + + " write.", + bug = "31797037") public void test_close() throws Exception { File f1 = File.createTempFile("close", ".tst"); - InflaterInputStream iis = new InflaterInputStream(new FileInputStream(f1)); - try { - iis.read(); - fail("EOFException Not Thrown"); - } catch (EOFException e) { + try (InflaterInputStream iis = new InflaterInputStream(new FileInputStream(f1))) { + try { + iis.read(); + fail("EOFException Not Thrown"); + } catch (EOFException e) { + } } FileOutputStream fos = new FileOutputStream(f1); @@ -190,16 +204,15 @@ public void test_close() throws Exception { dos.write(byteArray); dos.close(); - iis = new InflaterInputStream(new FileInputStream(f1)); - - // Test to see if the finish method wrote the bytes to the file. - assertEquals("Incorrect Byte Returned.", 1, iis.read()); - assertEquals("Incorrect Byte Returned.", 3, iis.read()); - assertEquals("Incorrect Byte Returned.", 4, iis.read()); - assertEquals("Incorrect Byte Returned.", 6, iis.read()); - assertEquals("Incorrect Byte Returned.", -1, iis.read()); - assertEquals("Incorrect Byte Returned.", -1, iis.read()); - iis.close(); + try (InflaterInputStream iis = new InflaterInputStream(new FileInputStream(f1))) { + // Test to see if the finish method wrote the bytes to the file. + assertEquals("Incorrect Byte Returned.", 1, iis.read()); + assertEquals("Incorrect Byte Returned.", 3, iis.read()); + assertEquals("Incorrect Byte Returned.", 4, iis.read()); + assertEquals("Incorrect Byte Returned.", 6, iis.read()); + assertEquals("Incorrect Byte Returned.", -1, iis.read()); + assertEquals("Incorrect Byte Returned.", -1, iis.read()); + } // Not sure if this test will stay. FileOutputStream fos2 = new FileOutputStream(f1); @@ -255,8 +268,8 @@ public void test_finish() throws Exception { // Test for writing with a new FileOutputStream using the same // DeflaterOutputStream. FileOutputStream fos2 = new FileOutputStream(f1); - dos = new DeflaterOutputStream(fos2); - dos.write(1); + DeflaterOutputStream dos4 = new DeflaterOutputStream(fos2); + dos4.write(1); // Test for writing to FileOutputStream fos1, which should be open. fos1.write(("testing").getBytes()); @@ -273,17 +286,22 @@ public void test_finish() throws Exception { fail("IOException not thrown"); } catch (IOException e) { } + dos3.close(); - // dos.close() won't close fos1 because it has been re-assigned to - // fos2 - fos1.close(); dos.close(); + dos4.close(); f1.delete(); } /** * java.util.zip.DeflaterOutputStream#write(int) */ + @DisableResourceLeakageDetection( + why = "DeflaterOutputStream.close() does not work properly if finish() throws an" + + " exception; DeflaterOutputStream.finish() throws an exception if the" + + " underlying OutputStream has been closed and the Deflater still has data to" + + " write.", + bug = "31797037") public void test_writeI() throws Exception { File f1 = File.createTempFile("writeIL", ".tst"); FileOutputStream fos = new FileOutputStream(f1); @@ -313,6 +331,12 @@ public void test_writeI() throws Exception { fail("IOException not thrown"); } catch (IOException e) { } + // Close to try and free up the resources. + try { + dos2.close(); + fail("IOException not thrown"); + } catch (IOException e) { + } f1.delete(); } @@ -320,6 +344,12 @@ public void test_writeI() throws Exception { /** * java.util.zip.DeflaterOutputStream#write(byte[], int, int) */ + @DisableResourceLeakageDetection( + why = "DeflaterOutputStream.close() does not work properly if finish() throws an" + + " exception; DeflaterOutputStream.finish() throws an exception if the" + + " underlying OutputStream has been closed and the Deflater still has data to" + + " write.", + bug = "31797037") public void test_write$BII() throws Exception { byte byteArray[] = { 1, 3, 4, 7, 8, 3, 6 }; @@ -384,6 +414,12 @@ public void test_writeI() throws Exception { fail("IOException not thrown"); } catch (IOException e) { } + // Close to try and free up the resources. + try { + dos3.close(); + fail("IOException not thrown"); + } catch (IOException e) { + } f2.delete(); } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterTest.java index 75e4a643b..1ba418759 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterTest.java @@ -23,11 +23,15 @@ import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; - -import junit.framework.TestCase; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; import tests.support.resource.Support_Resources; -public class DeflaterTest extends TestCase { +public class DeflaterTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); class MyDeflater extends Deflater { MyDeflater() { @@ -339,6 +343,7 @@ public void test_getTotalOut() { x += defl.deflate(outPutBuf); } assertEquals(x, defl.getTotalOut()); + defl.end(); } /** @@ -438,6 +443,7 @@ public void test_reset() { } assertEquals(0, outPutInf[curArray.length]); } + defl.end(); } /** @@ -539,6 +545,7 @@ public void test_reset() { } catch (ArrayIndexOutOfBoundsException e) { } } + defl.end(); } /** @@ -629,6 +636,7 @@ public void test_reset() { } catch (ArrayIndexOutOfBoundsException e) { } } + defl.end(); } /** @@ -673,8 +681,8 @@ public void test_setLevelI() throws Exception { } // testing boundaries + Deflater boundDefl = new Deflater(); try { - Deflater boundDefl = new Deflater(); // Level must be between 0-9 boundDefl.setLevel(-2); fail( @@ -682,12 +690,12 @@ public void test_setLevelI() throws Exception { } catch (IllegalArgumentException e) { } try { - Deflater boundDefl = new Deflater(); boundDefl.setLevel(10); fail( "IllegalArgumentException not thrown when setting level to a number > 9."); } catch (IllegalArgumentException e) { } + boundDefl.end(); } /** @@ -742,13 +750,13 @@ public void test_setStrategyI() throws Exception { } // Attempting to setStrategy to an invalid value + Deflater defl = new Deflater(); try { - Deflater defl = new Deflater(); defl.setStrategy(-412); - fail( - "IllegalArgumentException not thrown when setting strategy to an invalid value."); + fail("IllegalArgumentException not thrown when setting strategy to an invalid value."); } catch (IllegalArgumentException e) { } + defl.end(); } /** @@ -775,6 +783,8 @@ public void test_Constructor() throws Exception { // creating a Deflater using the DEFAULT_COMPRESSION as the int MyDeflater mdefl = new MyDeflater(); + mdefl.end(); + mdefl = new MyDeflater(mdefl.getDefCompression()); outPutBuf = new byte[500]; mdefl.setInput(byteArray); @@ -866,31 +876,31 @@ public void test_ConstructorIZ() throws Exception { } catch (DataFormatException e) { r = 1; } + infl.end(); assertEquals("header option did not correspond", 1, r); // testing boundaries + Deflater boundDefl = new Deflater(); try { - Deflater boundDefl = new Deflater(); // Level must be between 0-9 boundDefl.setLevel(-2); fail("IllegalArgumentException not thrown when setting level to a number < 0."); } catch (IllegalArgumentException e) { } try { - Deflater boundDefl = new Deflater(); boundDefl.setLevel(10); fail("IllegalArgumentException not thrown when setting level to a number > 9."); } catch (IllegalArgumentException e) { } - + boundDefl.end(); try { - Deflater boundDefl = new Deflater(-2, true); + new Deflater(-2, true).end(); fail("IllegalArgumentException not thrown when passing level to a number < 0."); } catch (IllegalArgumentException e) { } try { - Deflater boundDefl = new Deflater(10, true); + new Deflater(10, true).end(); fail("IllegalArgumentException not thrown when passing level to a number > 9."); } catch (IllegalArgumentException e) { } @@ -935,19 +945,19 @@ public void test_ConstructorI() throws Exception { defl.end(); // testing boundaries + Deflater boundDefl = new Deflater(); try { - Deflater boundDefl = new Deflater(); // Level must be between 0-9 boundDefl.setLevel(-2); fail("IllegalArgumentException not thrown when setting level to a number < 0."); } catch (IllegalArgumentException e) { } try { - Deflater boundDefl = new Deflater(); boundDefl.setLevel(10); fail("IllegalArgumentException not thrown when setting level to a number > 9."); } catch (IllegalArgumentException e) { } + boundDefl.end(); } private void helper_end_test(Deflater defl, String desc) { @@ -1061,6 +1071,7 @@ public void test_needsDictionary() { assertEquals(0, inf.getTotalOut()); assertEquals(0, inf.getBytesRead()); assertEquals(0, inf.getBytesWritten()); + inf.end(); } /** @@ -1087,6 +1098,7 @@ public void test_getBytesRead() throws DataFormatException, assertEquals(14, def.getTotalIn()); assertEquals(compressedDataLength, def.getTotalOut()); assertEquals(14, def.getBytesRead()); + def.end(); } /** @@ -1113,6 +1125,7 @@ public void test_getBytesWritten() throws DataFormatException, assertEquals(14, def.getTotalIn()); assertEquals(compressedDataLength, def.getTotalOut()); assertEquals(compressedDataLength, def.getBytesWritten()); + def.end(); } //Regression Test for HARMONY-2481 @@ -1125,5 +1138,6 @@ public void test_deflate_beforeSetInput() throws Exception { for (int i = 0; i < expectedBytes.length; i++) { assertEquals(expectedBytes[i], buffer[i]); } + deflater.end(); } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPInputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPInputStreamTest.java index 567189df0..4f44ec077 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPInputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPInputStreamTest.java @@ -27,10 +27,17 @@ import java.util.zip.Checksum; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; - +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.DisableResourceLeakageDetection; +import org.junit.Rule; +import org.junit.rules.TestRule; import tests.support.resource.Support_Resources; -public class GZIPInputStreamTest extends junit.framework.TestCase { +public class GZIPInputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); + File resources; class TestGZIPInputStream extends GZIPInputStream { @@ -76,6 +83,12 @@ public void test_ConstructorLjava_io_InputStream() { * @tests java.util.zip.GZIPInputStream#GZIPInputStream(java.io.InputStream, *int) */ + @DisableResourceLeakageDetection( + why = "InflaterInputStream does not clean up the default Inflater created in the" + + " constructor if the constructor fails; i.e. constructor calls" + + " this(..., new Inflater(), ...) and that constructor fails but does not know" + + " that it needs to call Inflater.end() as the caller has no access to it", + bug = "31798154") public void test_ConstructorLjava_io_InputStreamI() { // test method java.util.zip.GZIPInputStream.constructorI try { @@ -162,68 +175,71 @@ public void test_ConstructorLjava_io_InputStreamI() { out.write(test); out.close(); byte[] comp = bout.toByteArray(); - GZIPInputStream gin2 = new GZIPInputStream(new ByteArrayInputStream( - comp), 512); - int total = 0; - while ((result = gin2.read(test)) != -1) { - total += result; + int total; + try (GZIPInputStream gin2 = new GZIPInputStream(new ByteArrayInputStream(comp), 512)) { + total = 0; + while ((result = gin2.read(test)) != -1) { + total += result; + } + assertEquals("Should return -1", -1, gin2.read()); } - assertEquals("Should return -1", -1, gin2.read()); - gin2.close(); assertEquals("Incorrectly decompressed", test.length, total); - gin2 = new GZIPInputStream(new ByteArrayInputStream(comp), 512); - total = 0; - while ((result = gin2.read(new byte[200])) != -1) { - total += result; + try (GZIPInputStream gin2 = new GZIPInputStream(new ByteArrayInputStream(comp), 512)) { + total = 0; + while ((result = gin2.read(new byte[200])) != -1) { + total += result; + } + assertEquals("Should return -1", -1, gin2.read()); } - assertEquals("Should return -1", -1, gin2.read()); - gin2.close(); assertEquals("Incorrectly decompressed", test.length, total); - gin2 = new GZIPInputStream(new ByteArrayInputStream(comp), 516); - total = 0; - while ((result = gin2.read(new byte[200])) != -1) { - total += result; + try (GZIPInputStream gin2 = new GZIPInputStream(new ByteArrayInputStream(comp), 516)) { + total = 0; + while ((result = gin2.read(new byte[200])) != -1) { + total += result; + } + assertEquals("Should return -1", -1, gin2.read()); } - assertEquals("Should return -1", -1, gin2.read()); - gin2.close(); assertEquals("Incorrectly decompressed", test.length, total); comp[40] = 0; - gin2 = new GZIPInputStream(new ByteArrayInputStream(comp), 512); - boolean exception = false; - try { - while (gin2.read(test) != -1) { - ; + try (GZIPInputStream gin2 = new GZIPInputStream(new ByteArrayInputStream(comp), 512)) { + boolean exception = false; + try { + while (gin2.read(test) != -1) { + ; + } + } catch (IOException e) { + exception = true; } - } catch (IOException e) { - exception = true; + assertTrue("Exception expected", exception); } - assertTrue("Exception expected", exception); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - GZIPOutputStream zipout = new GZIPOutputStream(baos); - zipout.write(test); - zipout.close(); - outBuf = new byte[530]; - GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(baos.toByteArray())); - try { - in.read(outBuf, 530, 1); - fail("Test failed IOOBE was not thrown"); - } catch (IndexOutOfBoundsException e) { + try (GZIPOutputStream zipout = new GZIPOutputStream(baos)) { + zipout.write(test); } - while (true) { - result = in.read(outBuf, 0, 5); - if (result == -1) { - //"EOF was reached"; - break; + outBuf = new byte[530]; + try (GZIPInputStream in = new GZIPInputStream( + new ByteArrayInputStream(baos.toByteArray()))) { + try { + in.read(outBuf, 530, 1); + fail("Test failed IOOBE was not thrown"); + } catch (IndexOutOfBoundsException e) { + } + while (true) { + result = in.read(outBuf, 0, 5); + if (result == -1) { + //"EOF was reached"; + break; + } } + result = -10; + result = in.read(null, 100, 1); + result = in.read(outBuf, -100, 1); + result = in.read(outBuf, -1, 1);// 100, 1); } - result = -10; - result = in.read(null, 100, 1); - result = in.read(outBuf, -100, 1); - result = in.read(outBuf, -1, 1);// 100, 1); } /** @@ -264,7 +280,6 @@ public void test_close() { * @tests java.util.zip.GZIPInputStream#read() */ public void test_read() throws IOException { - GZIPInputStream gis = null; int result = 0; byte[] buffer = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; File f = new File(resources.getAbsolutePath() + "test.gz"); @@ -278,8 +293,9 @@ public void test_read() throws IOException { gout.finish(); out.write(1); out.close(); + gout.close(); - gis = new GZIPInputStream(new FileInputStream(f)); + GZIPInputStream gis = new GZIPInputStream(new FileInputStream(f)); buffer = new byte[100]; gis.read(buffer); result = gis.read(); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPOutputStreamTest.java index 30a94f06d..d6c87a92f 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPOutputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPOutputStreamTest.java @@ -25,8 +25,14 @@ import java.util.zip.Checksum; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public class GZIPOutputStreamTest extends junit.framework.TestCase { +public class GZIPOutputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); class TestGZIPOutputStream extends GZIPOutputStream { TestGZIPOutputStream(OutputStream out) throws IOException { @@ -169,9 +175,12 @@ public void test_close() { public void testSyncFlush() throws IOException { PipedOutputStream pout = new PipedOutputStream(); PipedInputStream pin = new PipedInputStream(pout); + // Must create in this order so that GZIPOutputStream writes the header before + // GZIPInputStream tries to read it otherwise it will deadlock with GZIPInputStream waiting + // for the header to be written but it cannot be written until after GZIPInputStream has + // read it. GZIPOutputStream out = new GZIPOutputStream(pout, true /* syncFlush */); GZIPInputStream in = new GZIPInputStream(pin); - out.write(1); out.write(2); out.write(3); @@ -183,5 +192,7 @@ public void testSyncFlush() throws IOException { assertEquals(1, in.read()); assertEquals(2, in.read()); assertEquals(3, in.read()); + out.close(); + in.close(); } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterInputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterInputStreamTest.java index 6930c59f4..6109762c8 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterInputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterInputStreamTest.java @@ -18,19 +18,22 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.zip.DeflaterOutputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; - -import junit.framework.TestCase; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; import tests.support.resource.Support_Resources; -public class InflaterInputStreamTest extends TestCase { +public class InflaterInputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); // files hyts_construO,hyts_construOD,hyts_construODI needs to be // included as resources @@ -116,51 +119,54 @@ public void test_ConstructorLjava_io_InputStreamLjava_util_zip_InflaterI() throw *java.util.zip.Inflater, int) */ public void test_ConstructorLjava_io_InputStreamLjava_util_zip_InflaterI_1() throws IOException { - InputStream infile = Support_Resources.getStream("hyts_construODI.bin"); - Inflater inflate = new Inflater(); - InflaterInputStream inflatIP = null; - try { - inflatIP = new InflaterInputStream(infile, null, 1); - fail("NullPointerException expected"); - } catch (NullPointerException NPE) { - //expected - } + try (InputStream infile = Support_Resources.getStream("hyts_construODI.bin")) { + Inflater inflate = new Inflater(); + try { + new InflaterInputStream(infile, null, 1); + fail("NullPointerException expected"); + } catch (NullPointerException NPE) { + //expected + } - try { - inflatIP = new InflaterInputStream(null, inflate, 1); - fail("NullPointerException expected"); - } catch (NullPointerException NPE) { - //expected - } + try { + new InflaterInputStream(null, inflate, 1); + fail("NullPointerException expected"); + } catch (NullPointerException NPE) { + //expected + } - try { - inflatIP = new InflaterInputStream(infile, inflate, -1); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException iae) { - //expected + try { + new InflaterInputStream(infile, inflate, -1); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException iae) { + //expected + } + inflate.end(); } } /** * java.util.zip.InflaterInputStream#mark(int) */ - public void test_markI() { + public void test_markI() throws IOException { InputStream is = new ByteArrayInputStream(new byte[10]); - InflaterInputStream iis = new InflaterInputStream(is); - // mark do nothing, do no check - iis.mark(0); - iis.mark(-1); - iis.mark(10000000); + try (InflaterInputStream iis = new InflaterInputStream(is)) { + // mark do nothing, do no check + iis.mark(0); + iis.mark(-1); + iis.mark(10000000); + } } /** * java.util.zip.InflaterInputStream#markSupported() */ - public void test_markSupported() { + public void test_markSupported() throws IOException { InputStream is = new ByteArrayInputStream(new byte[10]); - InflaterInputStream iis = new InflaterInputStream(is); - assertFalse(iis.markSupported()); - assertTrue(is.markSupported()); + try (InflaterInputStream iis = new InflaterInputStream(is)) { + assertFalse(iis.markSupported()); + assertTrue(is.markSupported()); + } } /** @@ -228,37 +234,40 @@ public void test_read_LBII() throws IOException { public void testAvailableNonEmptySource() throws Exception { // this byte[] is a deflation of these bytes: { 1, 3, 4, 6 } byte[] deflated = { 72, -119, 99, 100, 102, 97, 3, 0, 0, 31, 0, 15, 0 }; - InputStream in = new InflaterInputStream(new ByteArrayInputStream(deflated)); - // InflaterInputStream.available() returns either 1 or 0, even though - // that contradicts the behavior defined in InputStream.available() - assertEquals(1, in.read()); - assertEquals(1, in.available()); - assertEquals(3, in.read()); - assertEquals(1, in.available()); - assertEquals(4, in.read()); - assertEquals(1, in.available()); - assertEquals(6, in.read()); - assertEquals(0, in.available()); - assertEquals(-1, in.read()); - assertEquals(-1, in.read()); + try (InputStream in = new InflaterInputStream(new ByteArrayInputStream(deflated))) { + // InflaterInputStream.available() returns either 1 or 0, even though + // that contradicts the behavior defined in InputStream.available() + assertEquals(1, in.read()); + assertEquals(1, in.available()); + assertEquals(3, in.read()); + assertEquals(1, in.available()); + assertEquals(4, in.read()); + assertEquals(1, in.available()); + assertEquals(6, in.read()); + assertEquals(0, in.available()); + assertEquals(-1, in.read()); + assertEquals(-1, in.read()); + } } public void testAvailableSkip() throws Exception { // this byte[] is a deflation of these bytes: { 1, 3, 4, 6 } byte[] deflated = { 72, -119, 99, 100, 102, 97, 3, 0, 0, 31, 0, 15, 0 }; - InputStream in = new InflaterInputStream(new ByteArrayInputStream(deflated)); - assertEquals(1, in.available()); - assertEquals(4, in.skip(4)); - assertEquals(0, in.available()); + try (InputStream in = new InflaterInputStream(new ByteArrayInputStream(deflated))) { + assertEquals(1, in.available()); + assertEquals(4, in.skip(4)); + assertEquals(0, in.available()); + } } public void testAvailableEmptySource() throws Exception { // this byte[] is a deflation of the empty file byte[] deflated = { 120, -100, 3, 0, 0, 0, 0, 1 }; - InputStream in = new InflaterInputStream(new ByteArrayInputStream(deflated)); - assertEquals(-1, in.read()); - assertEquals(-1, in.read()); - assertEquals(0, in.available()); + try (InputStream in = new InflaterInputStream(new ByteArrayInputStream(deflated))) { + assertEquals(-1, in.read()); + assertEquals(-1, in.read()); + assertEquals(0, in.available()); + } } /** @@ -273,25 +282,26 @@ public void testAvailableEmptySource() throws Exception { test[i] = (byte) (256 - i); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DeflaterOutputStream dos = new DeflaterOutputStream(baos); - dos.write(test); - dos.close(); - InputStream is = new ByteArrayInputStream(baos.toByteArray()); - InflaterInputStream iis = new InflaterInputStream(is); - byte[] outBuf = new byte[530]; - int result = 0; - while (true) { - result = iis.read(outBuf, 0, 5); - if (result == -1) { - //"EOF was reached"; - break; - } + try (DeflaterOutputStream dos = new DeflaterOutputStream(baos)) { + dos.write(test); } - try { - iis.read(outBuf, -1, 10); - fail("should throw IOOBE."); - } catch (IndexOutOfBoundsException e) { - // expected; + try (InflaterInputStream iis = new InflaterInputStream( + new ByteArrayInputStream(baos.toByteArray()))) { + byte[] outBuf = new byte[530]; + int result = 0; + while (true) { + result = iis.read(outBuf, 0, 5); + if (result == -1) { + //"EOF was reached"; + break; + } + } + try { + iis.read(outBuf, -1, 10); + fail("should throw IOOBE."); + } catch (IndexOutOfBoundsException e) { + // expected; + } } } @@ -317,28 +327,28 @@ public void testAvailableEmptySource() throws Exception { Support_Resources.copyFile(resources, null, "Broken_manifest.jar"); FileInputStream fis = new FileInputStream(new File(resources, "Broken_manifest.jar")); - InflaterInputStream iis = new InflaterInputStream(fis); - byte[] outBuf = new byte[530]; - - try { - iis.read(); - fail("IOException expected."); - } catch (IOException ee) { - // expected + try (InflaterInputStream iis = new InflaterInputStream(fis)) { + try { + iis.read(); + fail("IOException expected."); + } catch (IOException ee) { + // expected + } } } /** * java.util.zip.InflaterInputStream#reset() */ - public void test_reset() { + public void test_reset() throws IOException { InputStream is = new ByteArrayInputStream(new byte[10]); - InflaterInputStream iis = new InflaterInputStream(is); - try { - iis.reset(); - fail("Should throw IOException"); - } catch (IOException e) { - // correct + try (InflaterInputStream iis = new InflaterInputStream(is)) { + try { + iis.reset(); + fail("Should throw IOException"); + } catch (IOException e) { + // correct + } } } @@ -390,11 +400,9 @@ public void test_skipJ2() throws IOException { byte orgBuffer[] = { 1, 3, 4, 7, 8 }; // testing for negative input to skip - InputStream infile = Support_Resources - .getStream("hyts_construOD.bin"); + InputStream infile = Support_Resources.getStream("hyts_construOD.bin"); Inflater inflate = new Inflater(); - InflaterInputStream inflatIP = new InflaterInputStream(infile, - inflate, 10); + InflaterInputStream inflatIP = new InflaterInputStream(infile, inflate, 10); long skip; try { skip = inflatIP.skip(Integer.MIN_VALUE); @@ -405,32 +413,33 @@ public void test_skipJ2() throws IOException { inflatIP.close(); // testing for number of bytes greater than input. - InputStream infile2 = Support_Resources - .getStream("hyts_construOD.bin"); - InflaterInputStream inflatIP2 = new InflaterInputStream(infile2); + InputStream infile2 = Support_Resources.getStream("hyts_construOD.bin"); + try (InflaterInputStream inflatIP2 = new InflaterInputStream(infile2)) { - // looked at how many bytes the skip skipped. It is - // 5 and its supposed to be the entire input stream. + // looked at how many bytes the skip skipped. It is + // 5 and its supposed to be the entire input stream. - skip = inflatIP2.skip(Integer.MAX_VALUE); - // System.out.println(skip); - assertEquals("method skip() returned wrong number of bytes skipped", - 5, skip); + skip = inflatIP2.skip(Integer.MAX_VALUE); + // System.out.println(skip); + assertEquals("method skip() returned wrong number of bytes skipped", + 5, skip); + inflatIP2.close(); + } // test for skipping of 2 bytes - InputStream infile3 = Support_Resources - .getStream("hyts_construOD.bin"); - InflaterInputStream inflatIP3 = new InflaterInputStream(infile3); - skip = inflatIP3.skip(2); - assertEquals("the number of bytes returned by skip did not correspond with its input parameters", - 2, skip); - int i = 0; - result = 0; - while ((result = inflatIP3.read()) != -1) { - buffer[i] = result; - i++; + InputStream infile3 = Support_Resources.getStream("hyts_construOD.bin"); + try (InflaterInputStream inflatIP3 = new InflaterInputStream(infile3)) { + skip = inflatIP3.skip(2); + assertEquals( + "the number of bytes returned by skip did not correspond with its input parameters", + 2, skip); + int i = 0; + result = 0; + while ((result = inflatIP3.read()) != -1) { + buffer[i] = result; + i++; + } } - inflatIP2.close(); for (int j = 2; j < orgBuffer.length; j++) { assertEquals( diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterOutputStreamTest.java index ab856b11a..dcde2428a 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterOutputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterOutputStreamTest.java @@ -23,10 +23,15 @@ import java.util.zip.Inflater; import java.util.zip.InflaterOutputStream; import java.util.zip.ZipException; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.DisableResourceLeakageDetection; +import org.junit.Rule; +import org.junit.rules.TestRule; -import junit.framework.TestCase; - -public class InflaterOutputStreamTest extends TestCase { +public class InflaterOutputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); private ByteArrayOutputStream os = new ByteArrayOutputStream(); @@ -37,8 +42,14 @@ public class InflaterOutputStreamTest extends TestCase { /** * java.util.zip.InflaterOutputStream#InflaterOutputStream(java.io.OutputStream) */ + @DisableResourceLeakageDetection( + why = "InflaterOutputStream does not clean up the default Inflater created in the" + + " constructor if the constructor fails; i.e. constructor calls" + + " this(..., new Inflater(), ...) and that constructor fails but does not know" + + " that it needs to call Inflater.end() as the caller has no access to it", + bug = "31798154") public void test_ConstructorLjava_io_OutputStream() throws IOException { - new InflaterOutputStream(os); + new InflaterOutputStream(os).close(); try { new InflaterOutputStream(null); @@ -51,11 +62,12 @@ public void test_ConstructorLjava_io_OutputStream() throws IOException { /** * java.util.zip.InflaterOutputStream#InflaterOutputStream(java.io.OutputStream, Inflater) */ - public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_Inflater() { - new InflaterOutputStream(os, new Inflater()); + public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_Inflater() throws IOException { + Inflater inflater = new Inflater(); + new InflaterOutputStream(os, inflater).close(); try { - new InflaterOutputStream(null, new Inflater()); + new InflaterOutputStream(null, inflater); fail("Should throw NullPointerException"); } catch (NullPointerException e) { // expected @@ -67,13 +79,16 @@ public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_Inflater() { } catch (NullPointerException e) { // expected } + + inflater.end(); } /** * java.util.zip.InflaterOutputStream#InflaterOutputStream(java.io.OutputStream, Inflater, int) */ - public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_InflaterI() { - new InflaterOutputStream(os, new Inflater(), 20); + public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_InflaterI() throws IOException { + Inflater inflater = new Inflater(); + new InflaterOutputStream(os, inflater, 20).close(); try { new InflaterOutputStream(null, null, 10); @@ -83,7 +98,7 @@ public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_InflaterI() { } try { - new InflaterOutputStream(null, new Inflater(), -1); + new InflaterOutputStream(null, inflater, -1); fail("Should throw NullPointerException"); } catch (NullPointerException e) { // expected @@ -104,18 +119,20 @@ public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_InflaterI() { } try { - new InflaterOutputStream(os, new Inflater(), 0); + new InflaterOutputStream(os, inflater, 0); fail("Should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { // expected } try { - new InflaterOutputStream(os, new Inflater(), -10000); + new InflaterOutputStream(os, inflater, -10000); fail("Should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { // expected } + + inflater.end(); } /** @@ -144,6 +161,7 @@ public void test_flush() throws IOException { ios = new InflaterOutputStream(os); ios.flush(); ios.flush(); + ios.close(); } /** @@ -165,18 +183,21 @@ public void test_finish() throws IOException { ios.flush(); ios.flush(); ios.finish(); + ios.close(); byte[] bytes1 = { 10, 20, 30, 40, 50 }; Deflater defaultDeflater = new Deflater(Deflater.BEST_SPEED); defaultDeflater.setInput(bytes1); defaultDeflater.finish(); int length1 = defaultDeflater.deflate(compressedBytes); + defaultDeflater.end(); byte[] bytes2 = { 100, 90, 80, 70, 60 }; Deflater bestDeflater = new Deflater(Deflater.BEST_COMPRESSION); bestDeflater.setInput(bytes2); bestDeflater.finish(); int length2 = bestDeflater.deflate(compressedBytes, length1, compressedBytes.length - length1); + bestDeflater.end(); ios = new InflaterOutputStream(os); for (int i = 0; i < length1; i++) { @@ -211,13 +232,14 @@ public void test_write_I() throws IOException { int length = compressToBytes(testString); // uncompress the data stored in the compressedBytes - InflaterOutputStream ios = new InflaterOutputStream(os); - for (int i = 0; i < length; i++) { - ios.write(compressedBytes[i]); - } + try (InflaterOutputStream ios = new InflaterOutputStream(os)) { + for (int i = 0; i < length; i++) { + ios.write(compressedBytes[i]); + } - String result = new String(os.toByteArray()); - assertEquals(testString, result); + String result = new String(os.toByteArray()); + assertEquals(testString, result); + } } /** @@ -243,20 +265,25 @@ public void test_write_I_Illegal() throws IOException { int length = compressToBytes(testString); // uncompress the data stored in the compressedBytes - InflaterOutputStream ios = new InflaterOutputStream(os); - ios.write(compressedBytes, 0, length); + try (InflaterOutputStream ios = new InflaterOutputStream(os)) { + ios.write(compressedBytes, 0, length); - String result = new String(os.toByteArray()); - assertEquals(testString, result); + String result = new String(os.toByteArray()); + assertEquals(testString, result); + } } /** * java.util.zip.InflaterOutputStream#write(byte[], int, int) */ + @DisableResourceLeakageDetection( + why = "InflaterOutputStream.close() does not work properly if finish() throws an" + + " exception; finish() throws an exception if the output is invalid.", + bug = "31797037") public void test_write_$BII_Illegal() throws IOException { // write error compression (ZIP) format - InflaterOutputStream ios = new InflaterOutputStream(os); byte[] bytes = { 0, 1, 2, 3 }; + InflaterOutputStream ios = new InflaterOutputStream(os); try { ios.write(bytes, 0, 4); fail("Should throw ZipException"); @@ -304,70 +331,72 @@ public void test_write_I_Illegal() throws IOException { // expected } - ios = new InflaterOutputStream(os); - try { - ios.write(null, 0, 4); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } - try { - ios.write(null, -1, 4); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } - try { - ios.write(null, 0, -4); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } - try { - ios.write(null, 0, 1000); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - // expected - } - try { - ios.write(bytes, -1, 4); - fail("Should throw IndexOutOfBoundsException"); - } catch (IndexOutOfBoundsException e) { - // expected - } - try { - ios.write(bytes, 0, -4); - fail("Should throw IndexOutOfBoundsException"); - } catch (IndexOutOfBoundsException e) { - // expected - } - try { - ios.write(bytes, 0, 100); - fail("Should throw IndexOutOfBoundsException"); - } catch (IndexOutOfBoundsException e) { - // expected - } - try { - ios.write(bytes, -100, 100); - fail("Should throw IndexOutOfBoundsException"); - } catch (IndexOutOfBoundsException e) { - // expected - } - - ios = new InflaterOutputStream(os); - ios.finish(); - - try { - ios.write(bytes, -1, -100); - fail("Should throw IndexOutOfBoundsException"); - } catch (IndexOutOfBoundsException e) { - // expected - } - try { - ios.write(null, -1, -100); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - // expected + try (InflaterOutputStream ios2 = new InflaterOutputStream(os)) { + try { + ios2.write(null, 0, 4); + fail("Should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + try { + ios2.write(null, -1, 4); + fail("Should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + try { + ios2.write(null, 0, -4); + fail("Should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + try { + ios2.write(null, 0, 1000); + fail("Should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + try { + ios2.write(bytes, -1, 4); + fail("Should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + try { + ios2.write(bytes, 0, -4); + fail("Should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + try { + ios2.write(bytes, 0, 100); + fail("Should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + try { + ios2.write(bytes, -100, 100); + fail("Should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + try (InflaterOutputStream ios2 = new InflaterOutputStream(os)) { + ios2.finish(); + + try { + ios2.write(bytes, -1, -100); + fail("Should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + try { + ios2.write(null, -1, -100); + fail("Should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } } ios = new InflaterOutputStream(os); @@ -384,9 +413,12 @@ public void test_write_I_Illegal() throws IOException { private int compressToBytes(String string) { byte[] input = string.getBytes(); Deflater deflater = new Deflater(); - deflater.setInput(input); - deflater.finish(); - return deflater.deflate(compressedBytes); + try { + deflater.setInput(input); + deflater.finish(); + return deflater.deflate(compressedBytes); + } finally { + deflater.end(); + } } - } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterTest.java index a16fab7e8..a15a5cf30 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/InflaterTest.java @@ -27,9 +27,16 @@ import java.util.zip.DeflaterOutputStream; import java.util.zip.Inflater; import java.util.zip.ZipException; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; import tests.support.resource.Support_Resources; -public class InflaterTest extends junit.framework.TestCase { +public class InflaterTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); + byte outPutBuff1[] = new byte[500]; byte outPutDiction[] = new byte[500]; @@ -81,6 +88,7 @@ public void test_finished() { } catch (DataFormatException e) { fail("Invalid input to be decompressed"); } + inflate.end(); for (int i = 0; i < byteArray.length; i++) { assertEquals( "Final decompressed data does not equal the original data", @@ -108,6 +116,7 @@ public void test_getAdler() { "the checksum value returned by getAdler() is not the same as the checksum returned by creating the adler32 instance", inflateDiction.getAdler(), checkSumR); } + inflateDiction.end(); } /** @@ -123,6 +132,7 @@ public void test_getRemaining() { assertTrue( "getRemaining returned zero when there is input in the input buffer", inflate.getRemaining() != 0); + inflate.end(); } /** @@ -161,6 +171,8 @@ public void test_getTotalIn() { assertEquals( "the total byte in outPutBuf did not equal the byte returned in getTotalIn", deflate.getTotalOut(), inflate.getTotalIn()); + deflate.end(); + inflate.end(); Inflater inflate2 = new Inflater(); int offSet = 0;// seems only can start as 0 @@ -180,6 +192,7 @@ public void test_getTotalIn() { assertEquals( "total byte dictated by length did not equal byte returned in getTotalIn", length, inflate2.getTotalIn()); + inflate2.end(); } /** @@ -246,6 +259,9 @@ public void test_getTotalOut() { assertEquals( "the total number of bytes to be compressed does not equal the total bytes decompressed", deflate.getTotalIn(), inflate.getTotalOut()); + + deflate.end(); + inflate.end(); } /** @@ -256,14 +272,15 @@ public void test_getTotalOut() { byte byteArray[] = { 1, 3, 4, 7, 8, 'e', 'r', 't', 'y', '5' }; byte outPutInf[] = new byte[500]; - Inflater inflate = new Inflater(); try { + Inflater inflate = new Inflater(); while (!(inflate.finished())) { if (inflate.needsInput()) { inflate.setInput(outPutBuff1); } inflate.inflate(outPutInf); } + inflate.end(); } catch (DataFormatException e) { fail("Invalid input to be decompressed"); } @@ -293,14 +310,16 @@ public void test_getTotalOut() { assertEquals( "the number of input byte from the array did not correspond with getTotalIn - inflate(byte)", emptyArray.length, defEmpty.getTotalIn()); - Inflater infEmpty = new Inflater(); + defEmpty.end(); try { + Inflater infEmpty = new Inflater(); while (!(infEmpty.finished())) { if (infEmpty.needsInput()) { infEmpty.setInput(outPutBuf); } infEmpty.inflate(outPutInf); } + infEmpty.end(); } catch (DataFormatException e) { fail("Invalid input to be decompressed"); } @@ -476,8 +495,8 @@ public void testInflateZero() throws Exception { public void test_Constructor() { // test method of java.util.zip.inflater.Inflater() Inflater inflate = new Inflater(); - assertNotNull("failed to create the instance of inflater", - inflate); + assertNotNull("failed to create the instance of inflater", inflate); + inflate.end(); } /** @@ -501,15 +520,15 @@ public void test_ConstructorZ() { inflate.inflate(outPutInf); } for (int i = 0; i < byteArray.length; i++) { - assertEquals("the output array from inflate should contain 0 because the header of inflate and deflate did not match, but this failed", + assertEquals("the output array from inflate should contain 0 because the" + + " header of inflate and deflate did not match, but this failed", 0, outPutBuff1[i]); } } catch (DataFormatException e) { r = 1; } - assertEquals("Error: exception should be thrown because of header inconsistency", - 1, r); - + assertEquals("Error: exception should be thrown because of header inconsistency", 1, r); + inflate.end(); } /** @@ -534,15 +553,17 @@ public void test_needsDictionary() { assertTrue( "method needsDictionary returned false when dictionary was used in deflater", inflateDiction.needsDictionary()); + inflateDiction.end(); // testing without dictionary - Inflater inflate = new Inflater(); try { + Inflater inflate = new Inflater(); inflate.setInput(outPutBuff1); inflate.inflate(outPutInf); assertFalse( "method needsDictionary returned true when dictionary was not used in deflater", inflate.needsDictionary()); + inflate.end(); } catch (DataFormatException e) { fail( "Input to inflate is invalid or corrupted - needsDictionary"); @@ -556,6 +577,7 @@ public void test_needsDictionary() { assertEquals(0, inf.getBytesRead()); assertEquals(0, inf.getBytesWritten()); assertEquals(1, inf.getAdler()); + inf.end(); } /** @@ -580,6 +602,7 @@ public void test_needsInput() { assertTrue( "needsInput give wrong boolean value as a result of an empty input buffer", inflate.needsInput()); + inflate.end(); } /** @@ -623,6 +646,7 @@ public void test_reset() { } catch (DataFormatException e) { fail("Invalid input to be decompressed"); } + inflate.end(); for (int i = 0; i < byteArray.length; i++) { assertEquals( "Final decompressed data does not equal the original data", @@ -698,6 +722,7 @@ public void test_reset() { inflate.setInput(byteArray); assertTrue("setInputB did not deliver any byte to the input buffer", inflate.getRemaining() != 0); + inflate.end(); } /** @@ -721,6 +746,7 @@ public void test_reset() { } catch (ArrayIndexOutOfBoundsException e) { r = 1; } + inflate.end(); assertEquals("boundary check is not present for setInput", 1, r); } @@ -779,6 +805,8 @@ public void test_getBytesRead() throws DataFormatException, assertEquals(16, inf.getTotalIn()); assertEquals(compressedDataLength, inf.getTotalOut()); assertEquals(16, inf.getBytesRead()); + def.end(); + inf.end(); } /** @@ -805,6 +833,8 @@ public void test_getBytesWritten() throws DataFormatException, UnsupportedEncodi assertEquals(16, inf.getTotalIn()); assertEquals(compressedDataLength, inf.getTotalOut()); assertEquals(14, inf.getBytesWritten()); + def.end(); + inf.end(); } /** @@ -814,6 +844,7 @@ public void testInflate() throws Exception { // Regression for HARMONY-81 Inflater inf = new Inflater(); int res = inf.inflate(new byte[0], 0, 0); + inf.end(); assertEquals(0, res); @@ -837,6 +868,7 @@ public void testInflate() throws Exception { } catch (DataFormatException e) { // expected } + inflater.end(); inflater = new Inflater(); inflater.setInput(new byte[] { -1, -1, -1 }); @@ -845,6 +877,7 @@ public void testInflate() throws Exception { } catch (DataFormatException e) { // expected } + inflater.end(); } public void testSetDictionary$B() throws Exception { @@ -875,6 +908,10 @@ public void testInflate() throws Exception { int dataLen1 = defDict1.deflate(output1); int dataLen2 = defDict2.deflate(output2); + defDictNo.end(); + defDict1.end(); + defDict2.end(); + boolean passNo1 = false; boolean passNo2 = false; boolean pass12 = false; @@ -1001,6 +1038,10 @@ public void testInflate() throws Exception { int dataLen2 = defDict2.deflate(output2); int dataLen3 = defDict3.deflate(output3); + defDict1.end(); + defDict2.end(); + defDict3.end(); + boolean pass12 = false; boolean pass23 = false; boolean pass13 = true; @@ -1083,6 +1124,7 @@ public void testInflate() throws Exception { } catch (ArrayIndexOutOfBoundsException aiob) { //expected } + infl4.end(); } public void testExceptions() throws Exception { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipEntryTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipEntryTest.java index f034639bd..360264bcd 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipEntryTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipEntryTest.java @@ -1,13 +1,13 @@ -/* +/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. @@ -20,13 +20,21 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.attribute.FileTime; import java.util.TimeZone; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import libcore.io.Streams; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; import tests.support.resource.Support_Resources; -public class ZipEntryTest extends junit.framework.TestCase { +public class ZipEntryTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); + // zip file hyts_ZipFile.zip must be included as a resource private ZipEntry zentry; private ZipFile zfile; @@ -154,6 +162,27 @@ public void test_getTime() { assertEquals("Failed to get time", orgTime, zentry.getTime()); } + /** + * java.util.zip.ZipEntry#getCreationTime() + */ + public void test_getCreationTime() { + assertNull(zentry.getCreationTime()); + } + + /** + * java.util.zip.ZipEntry#getLastAccessTime() + */ + public void test_getLastAccessTime() { + assertNull(zentry.getLastAccessTime()); + } + + /** + * java.util.zip.ZipEntry#getLastModifiedTime() + */ + public void test_getLastModifiedTime() { + assertEquals(orgTime, zentry.getLastModifiedTime().toMillis()); + } + /** * java.util.zip.ZipEntry#isDirectory() */ @@ -341,29 +370,70 @@ public void test_setTimeJ() { zentry.getTime()); TimeZone zone = TimeZone.getDefault(); try { + // These cases are supported since Android O thanks to + // Info-ZIP Extended Timestamp. Before Android O/openJdk8 + // these cases would behave differently. TimeZone.setDefault(TimeZone.getTimeZone("EST")); zentry.setTime(0); assertEquals("Test 3: Failed to set time: " + zentry.getTime(), - 315550800000L, zentry.getTime()); + 0L, zentry.getTime()); TimeZone.setDefault(TimeZone.getTimeZone("GMT")); assertEquals("Test 3a: Failed to set time: " + zentry.getTime(), - 315532800000L, zentry.getTime()); + 0L, zentry.getTime()); zentry.setTime(0); TimeZone.setDefault(TimeZone.getTimeZone("EST")); assertEquals("Test 3b: Failed to set time: " + zentry.getTime(), - 315550800000L, zentry.getTime()); + 0L, zentry.getTime()); zentry.setTime(-25); assertEquals("Test 4: Failed to set time: " + zentry.getTime(), - 315550800000L, zentry.getTime()); + -25L, zentry.getTime()); zentry.setTime(4354837200000L); assertEquals("Test 5: Failed to set time: " + zentry.getTime(), - 315550800000L, zentry.getTime()); + 4354837200000L, zentry.getTime()); } finally { TimeZone.setDefault(zone); } } + /** + * java.util.zip.ZipEntry#setLastModifiedTime(FileTime) + */ + public void test_setLastModifiedTime() { + zentry.setLastModifiedTime(FileTime.fromMillis(0)); + assertEquals(0, zentry.getLastModifiedTime().toMillis()); + assertEquals(0, zentry.getTime()); + + final long someTimestampValue = 1478624967000L; + zentry.setLastModifiedTime(FileTime.fromMillis(someTimestampValue)); + assertEquals(someTimestampValue, zentry.getLastModifiedTime().toMillis()); + assertEquals(someTimestampValue, zentry.getTime()); + } + + /** + * java.util.zip.ZipEntry#setCreationTime(FileTime) + */ + public void test_setCreationTime() { + zentry.setCreationTime(FileTime.fromMillis(0)); + assertEquals(0, zentry.getCreationTime().toMillis()); + + final long someTimestampValue = 1478624967000L; + zentry.setCreationTime(FileTime.fromMillis(someTimestampValue)); + assertEquals(someTimestampValue, zentry.getCreationTime().toMillis()); + } + + /** + * java.util.zip.ZipEntry#setLastAccessTime(FileTime) + */ + public void test_setLastAccessTime() { + zentry.setLastAccessTime(FileTime.fromMillis(0)); + assertEquals(0, zentry.getLastAccessTime().toMillis()); + + final long someTimestampValue = 1478624967000L; + zentry.setLastAccessTime(FileTime.fromMillis(someTimestampValue)); + assertEquals(someTimestampValue, zentry.getLastAccessTime().toMillis()); + } + /** * java.util.zip.ZipEntry#toString() */ @@ -450,4 +520,3 @@ protected void tearDown() { } } } - diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipFileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipFileTest.java index 5b966333e..c5a41029f 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipFileTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipFileTest.java @@ -23,9 +23,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; +import java.util.HashSet; import libcore.io.Streams; import libcore.java.lang.ref.FinalizationTester; import tests.support.resource.Support_Resources; @@ -396,6 +401,48 @@ public void test_reset_subtest0() throws IOException { is.close(); } + /** + * java.util.zip.ZipFile#stream() + */ + public void test_stream() { + assertEquals(6, zfile.stream().count()); + final List names = new ArrayList<>(); + zfile.stream().forEach((ZipEntry entry) -> names.add(entry.getName())); + assertEquals(Arrays.asList("File1.txt","File2.txt","File3.txt", + "testdir1/","testdir1/File1.txt", + "testdir1/testdir1"), names); + } + + public void test_sameNamesDifferentCase() throws Exception { + // Create a + final File tempFile = File.createTempFile("smdc", "zip"); + try { + // Create a zip file with multiple entries with same text and different + // capitalization + FileOutputStream tempFileStream = new FileOutputStream(tempFile); + ZipOutputStream zipOutputStream = new ZipOutputStream(tempFileStream); + zipOutputStream.putNextEntry(new ZipEntry("test.txt")); + zipOutputStream.write(new byte[2]); + zipOutputStream.closeEntry(); + zipOutputStream.putNextEntry(new ZipEntry("Test.txt")); + zipOutputStream.write(new byte[2]); + zipOutputStream.closeEntry(); + zipOutputStream.putNextEntry(new ZipEntry("TEST.TXT")); + zipOutputStream.write(new byte[2]); + zipOutputStream.closeEntry(); + zipOutputStream.close(); + tempFileStream.close(); + + ZipFile zipFile = new ZipFile(tempFile); + final List names = new ArrayList<>(); + zipFile.stream().forEach((ZipEntry entry) -> names.add(entry.getName())); + assertEquals(Arrays.asList("test.txt", "Test.txt", "TEST.TXT"), names); + } finally { + tempFile.delete(); + } + + } + @Override protected void setUp() throws IOException { // Create a local copy of the file since some tests want to alter information. diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipInputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipInputStreamTest.java index adfe7e1e3..49a15bb87 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipInputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipInputStreamTest.java @@ -27,11 +27,16 @@ import java.util.zip.ZipException; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; - -import junit.framework.TestCase; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; import tests.support.resource.Support_Resources; -public class ZipInputStreamTest extends TestCase { +public class ZipInputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); + // the file hyts_zipFile.zip used in setup needs to included as a resource private ZipEntry zentry; @@ -171,11 +176,11 @@ public int read(byte[] buffer) throws IOException { } }; - zis = new ZipInputStream(in); - while ((zentry = zis.getNextEntry()) != null) { - zentry.getName(); + try (ZipInputStream zis = new ZipInputStream(in)) { + while ((zentry = zis.getNextEntry()) != null) { + zentry.getName(); + } } - zis.close(); } /** @@ -193,18 +198,19 @@ public void test_skipJ() throws Exception { long s = zis.skip(1025); assertEquals("invalid skip: " + s, 1025, s); - ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes)); - zis.getNextEntry(); - long skipLen = dataBytes.length / 2; - assertEquals("Assert 0: failed valid skip", skipLen, zis.skip(skipLen)); - zis.skip(dataBytes.length); - assertEquals("Assert 1: performed invalid skip", 0, zis.skip(1)); - assertEquals("Assert 2: failed zero len skip", 0, zis.skip(0)); - try { - zis.skip(-1); - fail("Assert 3: Expected Illegal argument exception"); - } catch (IllegalArgumentException e) { - // Expected + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) { + zis.getNextEntry(); + long skipLen = dataBytes.length / 2; + assertEquals("Assert 0: failed valid skip", skipLen, zis.skip(skipLen)); + zis.skip(dataBytes.length); + assertEquals("Assert 1: performed invalid skip", 0, zis.skip(1)); + assertEquals("Assert 2: failed zero len skip", 0, zis.skip(0)); + try { + zis.skip(-1); + fail("Assert 3: Expected Illegal argument exception"); + } catch (IllegalArgumentException e) { + // Expected + } } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java index 7f42fa84d..09e4f6601 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java @@ -1,13 +1,13 @@ -/* +/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. @@ -21,13 +21,24 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.attribute.FileTime; +import java.util.ArrayList; +import java.util.List; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector.DisableResourceLeakageDetection; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public class ZipOutputStreamTest extends junit.framework.TestCase { +public class ZipOutputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); ZipOutputStream zos; @@ -41,7 +52,6 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { * java.util.zip.ZipOutputStream#close() */ public void test_close() throws Exception { - zos = new ZipOutputStream(bos); zos.putNextEntry(new ZipEntry("XX")); zos.closeEntry(); zos.close(); @@ -115,6 +125,11 @@ public void test_putNextEntryLjava_util_zip_ZipEntry() throws IOException { /** * java.util.zip.ZipOutputStream#setComment(java.lang.String) */ + @DisableResourceLeakageDetection( + why = "InflaterOutputStream.close() does not work properly if finish() throws an" + + " exception; finish() throws an exception if the output is invalid; this is" + + " an issue with the ZipOutputStream created in setUp()", + bug = "31797037") public void test_setCommentLjava_lang_String() { // There is no way to get the comment back, so no way to determine if // the comment is set correct @@ -168,6 +183,10 @@ public void test_setMethodI() throws IOException { /** * java.util.zip.ZipOutputStream#write(byte[], int, int) */ + @DisableResourceLeakageDetection( + why = "InflaterOutputStream.close() does not work properly if finish() throws an" + + " exception; finish() throws an exception if the output is invalid.", + bug = "31797037") public void test_write$BII() throws IOException { ZipEntry ze = new ZipEntry("test"); zos.putNextEntry(ze); @@ -241,6 +260,11 @@ public void test_setMethodI() throws IOException { /** * java.util.zip.ZipOutputStream#write(byte[], int, int) */ + @DisableResourceLeakageDetection( + why = "InflaterOutputStream.close() does not work properly if finish() throws an" + + " exception; finish() throws an exception if the output is invalid; this is" + + " an issue with the ZipOutputStream created in setUp()", + bug = "31797037") public void test_write$BII_2() throws IOException { // Regression for HARMONY-577 File f1 = File.createTempFile("testZip1", "tst"); @@ -269,6 +293,116 @@ public void test_setMethodI() throws IOException { zip1.close(); } + /** + * Test standard and info-zip-extended timestamp rounding + */ + public void test_timeSerializationRounding() throws Exception { + List entries = new ArrayList<>(); + ZipEntry zipEntry; + + entries.add(zipEntry = new ZipEntry("test1")); + final long someTimestamp = 1479139143200L; + zipEntry.setTime(someTimestamp); + + entries.add(zipEntry = new ZipEntry("test2")); + zipEntry.setLastModifiedTime(FileTime.fromMillis(someTimestamp)); + + for (ZipEntry entry : entries) { + zos.putNextEntry(entry); + zos.write(data.getBytes()); + zos.closeEntry(); + } + zos.close(); + + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bos.toByteArray()))) { + // getTime should be rounded down to a multiple of 2s + ZipEntry readEntry = zis.getNextEntry(); + assertEquals((someTimestamp / 2000) * 2000, + readEntry.getTime()); + + // With extended timestamp getTime&getLastModifiedTime should berounded down to a + // multiple of 1s + readEntry = zis.getNextEntry(); + assertEquals((someTimestamp / 1000) * 1000, + readEntry.getLastModifiedTime().toMillis()); + assertEquals((someTimestamp / 1000) * 1000, + readEntry.getTime()); + } + } + + /** + * Test info-zip extended timestamp support + */ + public void test_exttSupport() throws Exception { + List entries = new ArrayList<>(); + + ZipEntry zipEntry; + + // There's no sane way to access ONLY mtime + Field mtimeField = ZipEntry.class.getDeclaredField("mtime"); + mtimeField.setAccessible(true); + + // Serialized DOS timestamp resolution is 2s. Serialized extended + // timestamp resolution is 1s. If we won't use rounded values then + // asserting time equality would be more complicated (resolution of + // getTime depends weather we use extended timestamp). + // + // We have to call setTime on all entries. If it's not set then + // ZipOutputStream will call setTime(System.currentTimeMillis()) on it. + // I will use this as a excuse to test whether setting particular time + // values (~< 1980 ~> 2099) triggers use of the extended last-modified + // timestamp. + final long timestampWithinDostimeBound = ZipEntry.UPPER_DOSTIME_BOUND; + assertEquals(0, timestampWithinDostimeBound % 1000); + final long timestampBeyondDostimeBound = ZipEntry.UPPER_DOSTIME_BOUND + 2000; + assertEquals(0, timestampBeyondDostimeBound % 1000); + + // This will set both dos timestamp and last-modified timestamp (because < 1980) + entries.add(zipEntry = new ZipEntry("test_setTime")); + zipEntry.setTime(0); + assertNotNull(mtimeField.get(zipEntry)); + + // Explicitly set info-zip last-modified extended timestamp + entries.add(zipEntry = new ZipEntry("test_setLastModifiedTime")); + zipEntry.setLastModifiedTime(FileTime.fromMillis(1000)); + + // Set creation time and (since we have to call setTime on ZipEntry, otherwise + // ZipOutputStream will call setTime(System.currentTimeMillis()) and the getTime() + // assert will fail due to low serialization resolution) test that calling + // setTime with value <= ZipEntry.UPPER_DOSTIME_BOUND won't set the info-zip + // last-modified extended timestamp. + entries.add(zipEntry = new ZipEntry("test_setCreationTime")); + zipEntry.setCreationTime(FileTime.fromMillis(1000)); + zipEntry.setTime(timestampWithinDostimeBound); + assertNull(mtimeField.get(zipEntry)); + + // Set last access time and test that calling setTime with value > + // ZipEntry.UPPER_DOSTIME_BOUND will set the info-zip last-modified extended + // timestamp + entries.add(zipEntry = new ZipEntry("test_setLastAccessTime")); + zipEntry.setLastAccessTime(FileTime.fromMillis(3000)); + zipEntry.setTime(timestampBeyondDostimeBound); + assertNotNull(mtimeField.get(zipEntry)); + + for (ZipEntry entry : entries) { + zos.putNextEntry(entry); + zos.write(data.getBytes()); + zos.closeEntry(); + } + zos.close(); + + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bos.toByteArray()))) { + for (ZipEntry entry : entries) { + ZipEntry readEntry = zis.getNextEntry(); + assertEquals(entry.getName(), readEntry.getName()); + assertEquals(entry.getName(), entry.getTime(), readEntry.getTime()); + assertEquals(entry.getName(), entry.getLastModifiedTime(), readEntry.getLastModifiedTime()); + assertEquals(entry.getLastAccessTime(), readEntry.getLastAccessTime()); + assertEquals(entry.getCreationTime(), readEntry.getCreationTime()); + } + } + } + @Override protected void setUp() throws Exception { @@ -279,12 +413,14 @@ protected void setUp() throws Exception { @Override protected void tearDown() throws Exception { try { - if (zos != null) { - zos.close(); - } + // Close the ZipInputStream first as that does not fail. if (zis != null) { zis.close(); } + if (zos != null) { + // This will throw a ZipException if nothing is written to the ZipOutputStream. + zos.close(); + } } catch (Exception e) { } super.tearDown(); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java index 649be0950..52d914232 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java @@ -126,30 +126,12 @@ public final void test_createSocket_InetAddressI() throws Exception { public final void test_createSocket_InetAddressIInetAddressI() throws Exception { SocketFactory sf = SocketFactory.getDefault(); int sport = new ServerSocket(0).getLocalPort(); - int[] invalidPorts = {Integer.MIN_VALUE, -1, 65536, Integer.MAX_VALUE}; Socket s = sf.createSocket(InetAddress.getLocalHost(), sport, - InetAddress.getLocalHost(), 0); + InetAddress.getLocalHost(), 0); assertNotNull(s); assertTrue("1: Failed to create socket", s.getPort() == sport); int portNumber = s.getLocalPort(); - - for (int i = 0; i < invalidPorts.length; i++) { - try { - sf.createSocket(InetAddress.getLocalHost(), invalidPorts[i], - InetAddress.getLocalHost(), portNumber); - fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]); - } catch (IllegalArgumentException expected) { - } - - try { - sf.createSocket(InetAddress.getLocalHost(), sport, - InetAddress.getLocalHost(), invalidPorts[i]); - fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]); - } catch (IllegalArgumentException expected) { - } - } - try { sf.createSocket(InetAddress.getLocalHost(), sport, InetAddress.getLocalHost(), portNumber); @@ -165,6 +147,64 @@ public final void test_createSocket_InetAddressIInetAddressI() throws Exception } } + // Checks the behavior of createSocket(InetAddress, int, InetAddress, int) when the + // ports are invalid. + public void test_createSocket_InetAddressIInetAddressI_IllegalArgumentException() + throws Exception { + SocketFactory sf = SocketFactory.getDefault(); + int validPort = new ServerSocket(0).getLocalPort(); + int[] invalidPorts = {Integer.MIN_VALUE, -1, 65536, Integer.MAX_VALUE}; + + for (int i = 0; i < invalidPorts.length; i++) { + // Check invalid server port. + try (Socket s = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */, + invalidPorts[i] /* ServerPort */, + InetAddress.getLocalHost() /* ClientAddress */, + validPort /* ClientPort */)) { + fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]); + } catch (IllegalArgumentException expected) { + } + + // Check invalid client port. + try (Socket s = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */, + validPort /* ServerPort */, + InetAddress.getLocalHost() /* ClientAddress */, + invalidPorts[i]) /* ClientPort */){ + fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]); + } catch (IllegalArgumentException expected) { + } + } + } + + // b/31019685 + // Checks the ordering of port number validation (IllegalArgumentException) and binding error. + public void test_createSocket_InetAddressIInetAddressI_ExceptionOrder() throws IOException { + int invalidPort = Integer.MAX_VALUE; + SocketFactory sf = SocketFactory.getDefault(); + int validServerPortNumber = new ServerSocket(0).getLocalPort(); + + // Create a socket with localhost as the client address so that another attempt to bind + // would fail. + Socket s = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */, + validServerPortNumber /* ServerPortNumber */, + InetAddress.getLocalHost() /* ClientAddress */, + 0 /* ClientPortNumber */); + + int assignedLocalPortNumber = s.getLocalPort(); + + // Create a socket with an invalid port and localhost as the client address. Both + // BindException and IllegalArgumentException are expected in this case as the address is + // already bound to the socket above and port is invalid, however, to preserve the + // precedence order, IllegalArgumentException should be thrown. + try (Socket s1 = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */, + invalidPort /* ServerPortNumber */, + InetAddress.getLocalHost() /* ClientAddress */, + assignedLocalPortNumber /* ClientPortNumber */)) { + fail("IllegalArgumentException wasn't thrown for " + invalidPort); + } catch (IllegalArgumentException expected) { + } + } + /** * javax.net.SocketFactory#createSocket(String host, int port, * InetAddress localHost, int localPort) diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java index bb2265fb7..74c3a7faf 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java @@ -25,6 +25,7 @@ import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.util.Base64; import javax.net.ssl.HandshakeCompletedEvent; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.KeyManager; @@ -39,7 +40,6 @@ import javax.net.ssl.X509TrustManager; import javax.security.cert.X509Certificate; import junit.framework.TestCase; -import libcore.io.Base64; import org.apache.harmony.xnet.tests.support.mySSLSession; /** @@ -535,7 +535,7 @@ public X509Certificate[] getChain() { * for the result. */ private KeyManager[] getKeyManagers(String keys) throws Exception { - byte[] bytes = Base64.decode(keys.getBytes()); + byte[] bytes = Base64.getDecoder().decode(keys.getBytes()); InputStream inputStream = new ByteArrayInputStream(bytes); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java index 013c49bf6..b21b9acc1 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java @@ -71,22 +71,15 @@ public void testVerify() throws Exception { in = new ByteArrayInputStream(X509_FOO_BAR_HANAKO); x509 = (X509Certificate) cf.generateCertificate(in); session = new mySSLSession(new X509Certificate[] {x509}); - assertTrue(verifier.verify("foo.com", session)); - assertFalse(verifier.verify("a.foo.com", session)); - // these checks test alternative subjects. The test data contains an - // alternative subject starting with a japanese kanji character. This is - // not supported by Android because the underlying implementation from - // harmony follows the definition from rfc 1034 page 10 for alternative - // subject names. This causes the code to drop all alternative subjects. - // assertTrue(verifier.verify("bar.com", session)); - // assertFalse(verifier.verify("a.bar.com", session)); - // assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); - - in = new ByteArrayInputStream(X509_NO_CNS_FOO); - x509 = (X509Certificate) cf.generateCertificate(in); - session = new mySSLSession(new X509Certificate[] {x509}); - assertTrue(verifier.verify("foo.com", session)); + assertFalse(verifier.verify("foo.com", session)); assertFalse(verifier.verify("a.foo.com", session)); + assertTrue(verifier.verify("bar.com", session)); + assertFalse(verifier.verify("a.bar.com", session)); + // The certificate has this name in the altnames section, but Conscrypt drops + // any altnames that are improperly encoded according to RFC 5280, which requires + // non-ASCII characters to be encoded in ASCII via Punycode. + assertFalse(verifier.verify("\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); in = new ByteArrayInputStream(X509_NO_CNS_FOO); x509 = (X509Certificate) cf.generateCertificate(in); @@ -123,18 +116,18 @@ public void testVerify() throws Exception { session = new mySSLSession(new X509Certificate[] {x509}); // try the foo.com variations assertFalse(verifier.verify("foo.com", session)); - assertTrue(verifier.verify("www.foo.com", session)); - assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session)); + assertFalse(verifier.verify("www.foo.com", session)); + assertFalse(verifier.verify("\u82b1\u5b50.foo.com", session)); assertFalse(verifier.verify("a.b.foo.com", session)); - // these checks test alternative subjects. The test data contains an - // alternative subject starting with a japanese kanji character. This is - // not supported by Android because the underlying implementation from - // harmony follows the definition from rfc 1034 page 10 for alternative - // subject names. This causes the code to drop all alternative subjects. - // assertFalse(verifier.verify("bar.com", session)); - // assertTrue(verifier.verify("www.bar.com", session)); - // assertTrue(verifier.verify("\u82b1\u5b50.bar.com", session)); - // assertTrue(verifier.verify("a.b.bar.com", session)); + assertFalse(verifier.verify("bar.com", session)); + assertTrue(verifier.verify("www.bar.com", session)); + assertTrue(verifier.verify("\u82b1\u5b50.bar.com", session)); + assertFalse(verifier.verify("a.b.bar.com", session)); + // The certificate has this name in the altnames section, but Conscrypt drops + // any altnames that are improperly encoded according to RFC 5280, which requires + // non-ASCII characters to be encoded in ASCII via Punycode. + assertFalse(verifier.verify("\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); } public void testSubjectAlt() throws Exception { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HttpsURLConnectionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HttpsURLConnectionTest.java index 2b4b91685..feac5cb25 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HttpsURLConnectionTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HttpsURLConnectionTest.java @@ -43,6 +43,13 @@ */ public class HttpsURLConnectionTest extends TestCase { + @Override + public void setUp() throws Exception { + // Set the default SSL Socket factory to avoid an unmatched SSLSocketFactory + HttpsURLConnection.setDefaultSSLSocketFactory( + (SSLSocketFactory)SSLSocketFactory.getDefault()); + } + /** * javax.net.ssl.HttpsURLConnection#HttpsURLConnection(java_net_URL) */ diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java index 9360c00e0..f8d2847cf 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java @@ -310,7 +310,7 @@ public void test_unwrap_01() throws Exception { doHandshake(); ByteBuffer bbs = ByteBuffer.wrap(new byte[] {1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,31,2,3,1,2,3,1,2,3,1,2,3}); - ByteBuffer bbd = ByteBuffer.allocate(100); + ByteBuffer bbd = ByteBuffer.allocate(clientEngine.engine.getSession().getApplicationBufferSize()); try { clientEngine.engine.unwrap(bbs, new ByteBuffer[] { bbd }, 0, 1); fail("SSLException wasn't thrown"); @@ -895,6 +895,7 @@ public void test_wrap_ByteBuffer_ByteBuffer_04() throws Exception { try { SSLEngineResult result = sse.wrap(bbs, bbd); + fail(); } catch (IllegalStateException expected) { } } @@ -992,8 +993,11 @@ public void test_wrap_ByteBuffer_ByteBuffer_05() throws Exception { ByteBuffer[] bbA = { ByteBuffer.allocate(5), ByteBuffer.allocate(10), ByteBuffer.allocate(5) }; SSLEngine sse = getEngine(host, port); - SSLEngineResult result = sse.wrap(bbA, bb); - assertEquals(Status.BUFFER_OVERFLOW, result.getStatus()); + try { + SSLEngineResult result = sse.wrap(bbA, bb); + fail(); + } catch (IllegalStateException expected) { + } } /** @@ -1009,7 +1013,11 @@ public void test_wrap_ByteBuffer_ByteBuffer_05() throws Exception { SSLEngineResult res = sse.wrap(bbA, bb); assertEquals(0, res.bytesConsumed()); - assertEquals(0, res.bytesProduced()); + if (res.bytesProduced() == 0) { + assertEquals(HandshakeStatus.NEED_WRAP, res.getHandshakeStatus()); + } else { + assertEquals(HandshakeStatus.NEED_UNWRAP, res.getHandshakeStatus()); + } } private SSLEngine getEngine() throws Exception { diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketTest.java index 5a0cf6f84..117a1a078 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketTest.java @@ -18,8 +18,6 @@ import junit.framework.TestCase; -import libcore.io.Base64; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -27,6 +25,7 @@ import java.security.KeyStore; import java.security.SecureRandom; import java.util.Arrays; +import java.util.Base64; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; @@ -391,7 +390,7 @@ public void test_WantClientAuth() throws Exception { */ private KeyManager[] getKeyManagers() throws Exception { String keys = (useBKS ? SERVER_KEYS_BKS : SERVER_KEYS_JKS); - byte[] bytes = Base64.decode(keys.getBytes()); + byte[] bytes = Base64.getDecoder().decode(keys.getBytes()); InputStream inputStream = new ByteArrayInputStream(bytes); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionTest.java index 018de8ce6..fde1ff809 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionTest.java @@ -26,6 +26,7 @@ import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.Base64; import javax.net.ssl.ExtendedSSLSession; import javax.net.ssl.KeyManager; @@ -42,7 +43,6 @@ import org.apache.harmony.tests.javax.net.ssl.HandshakeCompletedEventTest.TestTrustManager; import junit.framework.TestCase; -import libcore.io.Base64; import libcore.java.security.StandardNames; public class SSLSessionTest extends TestCase { @@ -643,7 +643,7 @@ public KeyStore getStore() { * for the result. */ private KeyStore getKeyStore(String keys) throws Exception { - byte[] bytes = Base64.decode(keys.getBytes()); + byte[] bytes = Base64.getDecoder().decode(keys.getBytes()); InputStream inputStream = new ByteArrayInputStream(bytes); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketTest.java index 861f4a89d..5712a48bd 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketTest.java @@ -24,6 +24,7 @@ import java.security.KeyStore; import java.security.SecureRandom; import java.util.Arrays; +import java.util.Base64; import javax.net.ssl.HandshakeCompletedEvent; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.KeyManager; @@ -35,7 +36,6 @@ import javax.net.ssl.TrustManager; import javax.security.cert.X509Certificate; import junit.framework.TestCase; -import libcore.io.Base64; import libcore.java.security.StandardNames; import org.apache.harmony.tests.javax.net.ssl.HandshakeCompletedEventTest.TestTrustManager; @@ -249,6 +249,7 @@ public void test_removeHandshakeCompletedListener() throws IOException { try { ssl.removeHandshakeCompletedListener(ls); + fail(); } catch (IllegalArgumentException expected) { } @@ -586,7 +587,7 @@ public X509Certificate[] getChain() { * for the result. */ private KeyManager[] getKeyManagers(String keys) throws Exception { - byte[] bytes = Base64.decode(keys.getBytes()); + byte[] bytes = Base64.getDecoder().decode(keys.getBytes()); InputStream inputStream = new ByteArrayInputStream(bytes); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory1Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory1Test.java index 9b5b9296c..176a832ae 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory1Test.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory1Test.java @@ -295,6 +295,7 @@ public void test_getInstanceLjava_lang_StringLjava_security_Provider01() throws for (String validValue : getValidValues()) { try { TrustManagerFactory.getInstance(validValue, (Provider) null); + fail(); } catch (IllegalArgumentException expected) { } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/SubjectTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/SubjectTest.java index f2ef564eb..6d775ecbc 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/SubjectTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/SubjectTest.java @@ -19,12 +19,18 @@ import junit.framework.TestCase; import javax.security.auth.Subject; +import javax.security.auth.x500.X500Principal; + import java.security.AccessControlContext; import java.security.AccessController; +import java.security.Principal; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; +import java.util.HashSet; +import java.util.Set; + import org.apache.harmony.testframework.serialization.SerializationTest; /** @@ -48,6 +54,29 @@ public void test_Constructor_01() { } } + public void test_Constructor_failsWithNullArguments() { + try { + new Subject(false /* readOnly */, + null /* principals */, + new HashSet() /* pubCredentials */, + new HashSet() /* privCredentials */); + fail(); + } catch (NullPointerException expected) { + } + + try { + new Subject(false , new HashSet(), null, new HashSet()); + fail(); + } catch (NullPointerException expected) { + } + + try { + new Subject(false , new HashSet(), new HashSet(), null); + fail(); + } catch (NullPointerException expected) { + } + } + /** * javax.security.auth.Subject#doAs(Subject subject, PrivilegedAction action) */ @@ -234,6 +263,36 @@ public void testSerializationGolden() throws Exception { SerializationTest.verifyGolden(this, getSerializationData()); } + public void testSerialization_nullPrincipalsAllowed() throws Exception { + Set principalsSet = new HashSet<>(); + principalsSet.add(new X500Principal("CN=SomePrincipal")); + principalsSet.add(null); + principalsSet.add(new X500Principal("CN=SomeOtherPrincipal")); + Subject subject = new Subject( + false /* readOnly */, principalsSet, new HashSet(), new HashSet()); + SerializationTest.verifySelf(subject); + } + + public void testSecureTest_removeAllNull_throwsException() throws Exception { + Subject subject = new Subject( + false, new HashSet(), new HashSet(), new HashSet()); + try { + subject.getPrincipals().removeAll(null); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testSecureTest_retainAllNull_throwsException() throws Exception { + Subject subject = new Subject( + false, new HashSet(), new HashSet(), new HashSet()); + try { + subject.getPrincipals().retainAll(null); + fail(); + } catch (NullPointerException expected) { + } + } + private Object[] getSerializationData() { Subject subject = new Subject(); return new Object[] { subject, subject.getPrincipals(), diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/callback/PasswordCallbackTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/callback/PasswordCallbackTest.java index 024c9e90b..8682de77b 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/callback/PasswordCallbackTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/callback/PasswordCallbackTest.java @@ -93,7 +93,7 @@ public void test_Password() { } pc.clearPassword(); res = pc.getPassword(); - if (res.equals(psw2)) { + if (Arrays.equals(res, psw2)) { fail("Incorrect password was returned after clear"); } pc.setPassword(psw1); diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/x500/X500PrincipalTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/x500/X500PrincipalTest.java index 1933eb78b..14b21f7bf 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/x500/X500PrincipalTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/x500/X500PrincipalTest.java @@ -1322,6 +1322,25 @@ public void testSemiIllegalInputName_14() { new X500Principal(dn); } + /** + * Change rev/d1c04dac850d upstream addresses the case of the string CN=prefix\<>suffix. + * + * Before said change, the string can be used to construct an X500Principal, although according + * to RFC2253 is not possible. Also, characters after '<' are ignored. We have tests documenting + * that we allow such strings, like testIllegalInputName_07, so we modified the change as to + * allow the string. We check that the characters after '<' are not ignored. + * + * Note: the string CN=prefix\<>suffix in the test is escaped as CN=prefix\\<>suffix + */ + public void testSemiIllegalInputName_15() { + String dn = "CN=prefix\\<>suffix"; + + X500Principal principal = new X500Principal(dn); + assertEquals("CN=\"prefix<>suffix\"", principal.getName(X500Principal.RFC1779)); + assertEquals("CN=prefix\\<\\>suffix", principal.getName(X500Principal.RFC2253)); + assertEquals("cn=prefix\\<\\>suffix", principal.getName(X500Principal.CANONICAL)); + } + public void testInitClause() { try { byte[] mess = { 0x30, 0x18, 0x31, 0x0A, 0x30, 0x08, 0x06, 0x03, diff --git a/harmony-tests/src/test/resources/serialization/org/apache/harmony/tests/java/text/ParseException.ser b/harmony-tests/src/test/resources/serialization/org/apache/harmony/tests/java/text/ParseException.ser new file mode 100644 index 000000000..a25d6b104 Binary files /dev/null and b/harmony-tests/src/test/resources/serialization/org/apache/harmony/tests/java/text/ParseException.ser differ diff --git a/include/LocalArray.h b/include/LocalArray.h deleted file mode 100644 index 2ab708aff..000000000 --- a/include/LocalArray.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2009 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 LOCAL_ARRAY_H_included -#define LOCAL_ARRAY_H_included - -#include -#include - -/** - * A fixed-size array with a size hint. That number of bytes will be allocated - * on the stack, and used if possible, but if more bytes are requested at - * construction time, a buffer will be allocated on the heap (and deallocated - * by the destructor). - * - * The API is intended to be a compatible subset of C++0x's std::array. - */ -template -class LocalArray { -public: - /** - * Allocates a new fixed-size array of the given size. If this size is - * less than or equal to the template parameter STACK_BYTE_COUNT, an - * internal on-stack buffer will be used. Otherwise a heap buffer will - * be allocated. - */ - LocalArray(size_t desiredByteCount) : mSize(desiredByteCount) { - if (desiredByteCount > STACK_BYTE_COUNT) { - mPtr = new char[mSize]; - } else { - mPtr = &mOnStackBuffer[0]; - } - } - - /** - * Frees the heap-allocated buffer, if there was one. - */ - ~LocalArray() { - if (mPtr != &mOnStackBuffer[0]) { - delete[] mPtr; - } - } - - // Capacity. - size_t size() { return mSize; } - bool empty() { return mSize == 0; } - - // Element access. - char& operator[](size_t n) { return mPtr[n]; } - const char& operator[](size_t n) const { return mPtr[n]; } - -private: - char mOnStackBuffer[STACK_BYTE_COUNT]; - char* mPtr; - size_t mSize; - - // Disallow copy and assignment. - LocalArray(const LocalArray&); - void operator=(const LocalArray&); -}; - -#endif // LOCAL_ARRAY_H_included diff --git a/include/ScopedPthreadMutexLock.h b/include/ScopedPthreadMutexLock.h deleted file mode 100644 index 90f4596cc..000000000 --- a/include/ScopedPthreadMutexLock.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2010 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 SCOPED_PTHREAD_MUTEX_LOCK_H_included -#define SCOPED_PTHREAD_MUTEX_LOCK_H_included - -#include - -/** - * Locks and unlocks a pthread_mutex_t as it goes in and out of scope. - */ -class ScopedPthreadMutexLock { -public: - explicit ScopedPthreadMutexLock(pthread_mutex_t* mutex) : mMutexPtr(mutex) { - pthread_mutex_lock(mMutexPtr); - } - - ~ScopedPthreadMutexLock() { - pthread_mutex_unlock(mMutexPtr); - } - -private: - pthread_mutex_t* mMutexPtr; - - // Disallow copy and assignment. - ScopedPthreadMutexLock(const ScopedPthreadMutexLock&); - void operator=(const ScopedPthreadMutexLock&); -}; - -#endif // SCOPED_PTHREAD_MUTEX_LOCK_H_included diff --git a/json/src/main/java/org/json/JSONObject.java b/json/src/main/java/org/json/JSONObject.java index 9ea91a6a1..790238979 100644 --- a/json/src/main/java/org/json/JSONObject.java +++ b/json/src/main/java/org/json/JSONObject.java @@ -21,6 +21,7 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; // Note: this class was written without inspecting the non-free org.json sourcecode. @@ -100,6 +101,8 @@ public class JSONObject { @Override public boolean equals(Object o) { return o == this || o == null; // API specifies this broken equals implementation } + // at least make the broken equals(null) consistent with Objects.hashCode(null). + @Override public int hashCode() { return Objects.hashCode(null); } @Override public String toString() { return "null"; } diff --git a/json/src/test/java/org/json/JSONObjectTest.java b/json/src/test/java/org/json/JSONObjectTest.java index 9029ec6c6..07d1cf643 100644 --- a/json/src/test/java/org/json/JSONObjectTest.java +++ b/json/src/test/java/org/json/JSONObjectTest.java @@ -27,6 +27,7 @@ import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Set; import java.util.TreeMap; import junit.framework.TestCase; @@ -825,6 +826,12 @@ public void testNullValue() throws JSONException { assertTrue(object.isNull("bar")); } + public void testNullValue_equalsAndHashCode() { + assertTrue(JSONObject.NULL.equals(null)); // guaranteed by javadoc + // not guaranteed by javadoc, but seems like a good idea + assertEquals(Objects.hashCode(null), JSONObject.NULL.hashCode()); + } + public void testHas() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", 5); diff --git a/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java b/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java index 37bc28560..28517aab0 100644 --- a/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java +++ b/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java @@ -3730,9 +3730,10 @@ public void testMinimalCompletionStage_minimality() { (method) -> method.getName() + Arrays.toString(method.getParameterTypes()); Predicate isNotStatic = (method) -> (method.getModifiers() & Modifier.STATIC) == 0; + // Android-changed: Added a cast to workaround an ECJ bug. http://b/33371837 List minimalMethods = Stream.of(Object.class, CompletionStage.class) - .flatMap((klazz) -> Stream.of(klazz.getMethods())) + .flatMap((klazz) -> (Stream) Stream.of(klazz.getMethods())) .filter(isNotStatic) .collect(Collectors.toList()); // Methods from CompletableFuture permitted NOT to throw UOE diff --git a/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java b/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java index e42ac2dfb..61e8f8bd0 100644 --- a/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java +++ b/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java @@ -27,7 +27,7 @@ public class DelayQueueTest extends JSR166TestCase { - // android-changed: Extend BlockingQueueTest directly instead of creating + // Android-changed: Extend BlockingQueueTest directly instead of creating // an inner class and its associated suite. // // public static class Generic extends BlockingQueueTest { diff --git a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java index fc1632cb8..ea6e57657 100644 --- a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java +++ b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java @@ -1089,7 +1089,7 @@ public void shouldThrow(String exceptionName) { * getPolicy/setPolicy. */ public void runWithPermissions(Runnable r, Permission... permissions) { - // Android-changed - no SecurityManager + // Android-changed: no SecurityManager // SecurityManager sm = System.getSecurityManager(); // if (sm == null) { // r.run(); @@ -1107,7 +1107,7 @@ public void runWithPermissions(Runnable r, Permission... permissions) { */ public void runWithSecurityManagerWithPermissions(Runnable r, Permission... permissions) { - // Android-changed - no SecurityManager + // Android-changed: no SecurityManager // SecurityManager sm = System.getSecurityManager(); // if (sm == null) { // Policy savedPolicy = Policy.getPolicy(); @@ -1221,20 +1221,60 @@ else if (s == Thread.State.TERMINATED) fail("Unexpected thread termination"); else if (millisElapsedSince(startTime) > timeoutMillis) { threadAssertTrue(thread.isAlive()); - return; + fail("timed out waiting for thread to enter wait state"); } Thread.yield(); } } /** - * Waits up to LONG_DELAY_MS for the given thread to enter a wait - * state: BLOCKED, WAITING, or TIMED_WAITING. + * Spin-waits up to the specified number of milliseconds for the given + * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING, + * and additionally satisfy the given condition. + */ + void waitForThreadToEnterWaitState( + Thread thread, long timeoutMillis, Callable waitingForGodot) { + long startTime = 0L; + for (;;) { + Thread.State s = thread.getState(); + if (s == Thread.State.BLOCKED || + s == Thread.State.WAITING || + s == Thread.State.TIMED_WAITING) { + try { + if (waitingForGodot.call()) + return; + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + else if (s == Thread.State.TERMINATED) + fail("Unexpected thread termination"); + else if (startTime == 0L) + startTime = System.nanoTime(); + else if (millisElapsedSince(startTime) > timeoutMillis) { + threadAssertTrue(thread.isAlive()); + fail("timed out waiting for thread to enter wait state"); + } + Thread.yield(); + } + } + + /** + * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to + * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING. */ void waitForThreadToEnterWaitState(Thread thread) { waitForThreadToEnterWaitState(thread, LONG_DELAY_MS); } + /** + * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to + * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING, + * and additionally satisfy the given condition. + */ + void waitForThreadToEnterWaitState( + Thread thread, Callable waitingForGodot) { + waitForThreadToEnterWaitState(thread, LONG_DELAY_MS, waitingForGodot); + } + /** * Returns the number of milliseconds since time given by * startNanoTime, which must have been previously returned from a diff --git a/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java b/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java index 05fc68911..efe5a5828 100644 --- a/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java +++ b/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java @@ -17,6 +17,7 @@ import java.util.NoSuchElementException; import java.util.Queue; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; @@ -750,9 +751,11 @@ public void realRun() throws InterruptedException { }}); threadStarted.await(); - waitForThreadToEnterWaitState(t); - assertEquals(1, q.getWaitingConsumerCount()); - assertTrue(q.hasWaitingConsumer()); + Callable oneConsumer + = new Callable() { public Boolean call() { + return q.hasWaitingConsumer() + && q.getWaitingConsumerCount() == 1; }}; + waitForThreadToEnterWaitState(t, oneConsumer); assertTrue(q.offer(one)); assertEquals(0, q.getWaitingConsumerCount()); @@ -789,8 +792,11 @@ public void realRun() throws InterruptedException { }}); threadStarted.await(); - waitForThreadToEnterWaitState(t); - assertEquals(1, q.size()); + Callable oneElement + = new Callable() { public Boolean call() { + return !q.isEmpty() && q.size() == 1; }}; + waitForThreadToEnterWaitState(t, oneElement); + assertSame(five, q.poll()); checkEmpty(q); awaitTermination(t); diff --git a/jsr166-tests/src/test/java/jsr166/PhaserTest.java b/jsr166-tests/src/test/java/jsr166/PhaserTest.java index 673e556a1..121901776 100644 --- a/jsr166-tests/src/test/java/jsr166/PhaserTest.java +++ b/jsr166-tests/src/test/java/jsr166/PhaserTest.java @@ -527,7 +527,7 @@ public void realRun() { }}); await(pleaseArrive); - waitForThreadToEnterWaitState(t, SHORT_DELAY_MS); + waitForThreadToEnterWaitState(t); assertEquals(0, phaser.arrive()); awaitTermination(t); @@ -555,7 +555,7 @@ public void realRun() { }}); await(pleaseArrive); - waitForThreadToEnterWaitState(t, SHORT_DELAY_MS); + waitForThreadToEnterWaitState(t); t.interrupt(); assertEquals(0, phaser.arrive()); awaitTermination(t); @@ -571,20 +571,20 @@ public void realRun() { public void testArriveAndAwaitAdvanceAfterInterrupt() { final Phaser phaser = new Phaser(); assertEquals(0, phaser.register()); - final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + final CountDownLatch pleaseArrive = new CountDownLatch(1); Thread t = newStartedThread(new CheckedRunnable() { public void realRun() { Thread.currentThread().interrupt(); assertEquals(0, phaser.register()); - pleaseInterrupt.countDown(); + pleaseArrive.countDown(); assertTrue(Thread.currentThread().isInterrupted()); assertEquals(1, phaser.arriveAndAwaitAdvance()); - assertTrue(Thread.currentThread().isInterrupted()); + assertTrue(Thread.interrupted()); }}); - await(pleaseInterrupt); - waitForThreadToEnterWaitState(t, SHORT_DELAY_MS); + await(pleaseArrive); + waitForThreadToEnterWaitState(t); Thread.currentThread().interrupt(); assertEquals(1, phaser.arriveAndAwaitAdvance()); assertTrue(Thread.interrupted()); @@ -605,11 +605,11 @@ public void realRun() { assertFalse(Thread.currentThread().isInterrupted()); pleaseInterrupt.countDown(); assertEquals(1, phaser.arriveAndAwaitAdvance()); - assertTrue(Thread.currentThread().isInterrupted()); + assertTrue(Thread.interrupted()); }}); await(pleaseInterrupt); - waitForThreadToEnterWaitState(t, SHORT_DELAY_MS); + waitForThreadToEnterWaitState(t); t.interrupt(); Thread.currentThread().interrupt(); assertEquals(1, phaser.arriveAndAwaitAdvance()); @@ -784,7 +784,7 @@ public void realRun() { assertEquals(THREADS, phaser.getArrivedParties()); assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); for (Thread thread : threads) - waitForThreadToEnterWaitState(thread, SHORT_DELAY_MS); + waitForThreadToEnterWaitState(thread); for (Thread thread : threads) assertTrue(thread.isAlive()); assertState(phaser, 0, THREADS + 1, 1); diff --git a/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java b/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java index 2546626e8..0865ed4f6 100644 --- a/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java +++ b/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java @@ -1371,11 +1371,11 @@ public void testPoolSizeInvariants() { assertEquals(s, p.getMaximumPoolSize()); try { p.setCorePoolSize(s + 1); - // android-changed: changeset dfec9b5386ca028cc1468f3e2717120ab6274702 + // Android-changed: changeset dfec9b5386ca028cc1468f3e2717120ab6274702 // disables this check for compatibility reason. // shouldThrow(); } catch (IllegalArgumentException success) {} - // android-changed: changeset dfec9b5386ca028cc1468f3e2717120ab6274702 + // Android-changed: changeset dfec9b5386ca028cc1468f3e2717120ab6274702 // disables maximumpoolsize check for compatibility reason. // assertEquals(s, p.getCorePoolSize()); assertEquals(s + 1, p.getCorePoolSize()); diff --git a/libart/src/main/java/dalvik/system/ClassExt.java b/libart/src/main/java/dalvik/system/ClassExt.java new file mode 100644 index 000000000..3daa971e3 --- /dev/null +++ b/libart/src/main/java/dalvik/system/ClassExt.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 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. + */ + +package dalvik.system; + +/** + * Holder class for extraneous Class data. + * + * This class holds data for Class objects that is either rarely useful, only necessary for + * debugging purposes or both. This allows us to extend the Class class without impacting memory + * use. + * + * @hide For internal runtime use only. + */ +public final class ClassExt { + /** + * An array of all obsolete DexCache objects that are needed for obsolete methods. + * + * These entries are associated with the obsolete ArtMethod pointers at the same indexes in the + * obsoleteMethods array. + * + * This field has native components and is a logical part of the 'Class' type. + */ + private Object[] obsoleteDexCaches; + + /** + * An array of all native obsolete ArtMethod pointers. + * + * These are associated with their DexCaches at the same index in the obsoleteDexCaches array. + * + * This field is actually either an int[] or a long[] depending on size of a pointer. + * + * This field contains native pointers and is a logical part of the 'Class' type. + */ + private Object obsoleteMethods; + + /** + * If set, the bytes or DexCache of the original dex-file associated with the related class. + * + * In this instance 'original' means either (1) the dex-file loaded for this class when it was + * first loaded after all non-retransformation capable transformations had been performed but + * before any retransformation capable ones had been done or (2) the most recent dex-file bytes + * given for a class redefinition. + * + * Needed in order to implement retransformation of classes. + * + * This field is a logical part of the 'Class' type. + */ + private Object originalDexFile; + + /** + * If class verify fails, we must return same error on subsequent tries. We may store either + * the class of the error, or an actual instance of Throwable here. + * + * This field is a logical part of the 'Class' type. + */ + private Object verifyError; + + /** + * Private constructor. + * + * Only created by the runtime. + */ + private ClassExt() {} +} diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java index e53e1032b..6a673f2a4 100644 --- a/libart/src/main/java/dalvik/system/VMRuntime.java +++ b/libart/src/main/java/dalvik/system/VMRuntime.java @@ -16,6 +16,7 @@ package dalvik.system; +import dalvik.annotation.optimization.FastNative; import java.lang.ref.FinalizerReference; import java.util.HashMap; import java.util.Map; @@ -50,7 +51,15 @@ public final class VMRuntime { ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a", "arm64"); } - private int targetSdkVersion; + /** + * Magic version number for a current development build, which has not + * yet turned into an official release. This number must be larger than + * any released version in {@code android.os.Build.VERSION_CODES}. + * @hide + */ + public static final int SDK_VERSION_CUR_DEVELOPMENT = 10000; + + private int targetSdkVersion = SDK_VERSION_CUR_DEVELOPMENT; /** * Prevents this class from being instantiated. @@ -102,11 +111,13 @@ public static VMRuntime getRuntime() { /** * Returns whether the VM is running in 64-bit mode. */ + @FastNative public native boolean is64Bit(); /** * Returns whether the VM is running with JNI checking enabled. */ + @FastNative public native boolean isCheckJniEnabled(); /** @@ -151,10 +162,7 @@ public float setTargetHeapUtilization(float newTarget) { /** * Sets the target SDK version. Should only be called before the * app starts to run, because it may change the VM's behavior in - * dangerous ways. Use 0 to mean "current" (since callers won't - * necessarily know the actual current SDK version, and the - * allocated version numbers start at 1), and 10000 to mean - * CUR_DEVELOPMENT. + * dangerous ways. Defaults to {@link #SDK_VERSION_CUR_DEVELOPMENT}. */ public synchronized void setTargetSdkVersion(int targetSdkVersion) { this.targetSdkVersion = targetSdkVersion; @@ -255,6 +263,7 @@ public long getExternalBytesAllocated() { * This is used to implement native allocations on the Java heap, such as DirectByteBuffers * and Bitmaps. */ + @FastNative public native Object newNonMovableArray(Class componentType, int length); /** @@ -262,12 +271,14 @@ public long getExternalBytesAllocated() { * avoiding any padding after the array. The amount of padding varies depending on the * componentType and the memory allocator implementation. */ + @FastNative public native Object newUnpaddedArray(Class componentType, int minLength); /** * Returns the address of array[0]. This differs from using JNI in that JNI might lie and * give you the address of a copy of the array when in forcecopy mode. */ + @FastNative public native long addressOf(Object array); /** @@ -285,11 +296,13 @@ public long getExternalBytesAllocated() { /** * Returns true if either a Java debugger or native debugger is active. */ + @FastNative public native boolean isDebuggerActive(); /** * Returns true if native debugging is on. */ + @FastNative public native boolean isNativeDebuggable(); /** @@ -352,10 +365,11 @@ public static void runFinalization(long timeout) { public native void preloadDexCaches(); /** - * Register application info + * Register application info. + * @param profileFile the path of the file where the profile information should be stored. + * @param codePaths the code paths that should be profiled. */ - public static native void registerAppInfo(String packageName, String appDir, - String[] codePaths, String foreignDexProfileDir); + public static native void registerAppInfo(String profileFile, String[] codePaths); /** * Returns the runtime instruction set corresponding to a given ABI. Multiple diff --git a/libart/src/main/java/dalvik/system/VMStack.java b/libart/src/main/java/dalvik/system/VMStack.java index b69ab6009..ef911c438 100644 --- a/libart/src/main/java/dalvik/system/VMStack.java +++ b/libart/src/main/java/dalvik/system/VMStack.java @@ -16,6 +16,8 @@ package dalvik.system; +import dalvik.annotation.optimization.FastNative; + /** * Provides a limited interface to the Dalvik VM stack. This class is mostly * used for implementing security checks. @@ -29,6 +31,7 @@ public final class VMStack { * @return the requested class loader, or {@code null} if this is the * bootstrap class loader. */ + @FastNative native public static ClassLoader getCallingClassLoader(); /** @@ -45,12 +48,14 @@ public static Class getStackClass1() { * * @return the requested class, or {@code null}. */ + @FastNative native public static Class getStackClass2(); /** * Returns the first ClassLoader on the call stack that isn't the * bootstrap class loader. */ + @FastNative public native static ClassLoader getClosestUserClassLoader(); /** @@ -61,6 +66,7 @@ public static Class getStackClass1() { * @return an array of stack trace elements, or null if the thread * doesn't have a stack trace (e.g. because it exited) */ + @FastNative native public static StackTraceElement[] getThreadStackTrace(Thread t); /** @@ -74,6 +80,7 @@ public static Class getStackClass1() { * desired. Unused elements will be filled with null values. * @return the number of elements filled */ + @FastNative native public static int fillStackTraceElements(Thread t, StackTraceElement[] stackTraceElements); } diff --git a/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java b/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java index 13e931786..5a84c8e53 100644 --- a/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java +++ b/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java @@ -106,6 +106,9 @@ public final class AndroidHardcodedSystemProperties { // Hardcode default value for AVA. b/28174137 { "com.sun.security.preserveOldDCEncoding", null }, + + // Hardcode default value for LogManager. b/28174137 + { "java.util.logging.manager", null }, }; } diff --git a/libart/src/main/java/java/lang/CaseMapper.java b/libart/src/main/java/java/lang/CaseMapper.java index 7f9d2e307..66d503085 100644 --- a/libart/src/main/java/java/lang/CaseMapper.java +++ b/libart/src/main/java/java/lang/CaseMapper.java @@ -50,7 +50,7 @@ public static String toLowerCase(Locale locale, String s) { return ICU.toLowerCase(s, locale); } - String newString = null; + char[] newValue = null; for (int i = 0, end = s.length(); i < end; ++i) { char ch = s.charAt(i); char newCh; @@ -63,13 +63,14 @@ public static String toLowerCase(Locale locale, String s) { newCh = Character.toLowerCase(ch); } if (ch != newCh) { - if (newString == null) { - newString = StringFactory.newStringFromString(s); + if (newValue == null) { + newValue = new char[end]; + s.getCharsNoCheck(0, end, newValue, 0); } - newString.setCharAt(i, newCh); + newValue[i] = newCh; } } - return newString != null ? newString : s; + return newValue != null ? new String(newValue) : s; } /** @@ -152,9 +153,8 @@ public static String toUpperCase(Locale locale, String s, int count) { } char[] output = null; - String newString = null; int i = 0; - for (int o = 0, end = count; o < end; o++) { + for (int o = 0; o < count; o++) { char ch = s.charAt(o); if (Character.isHighSurrogate(ch)) { return ICU.toUpperCase(s, locale); @@ -170,10 +170,10 @@ public static String toUpperCase(Locale locale, String s, int count) { if (output != null) { output[i++] = upch; } else if (ch != upch) { - if (newString == null) { - newString = StringFactory.newStringFromString(s); - } - newString.setCharAt(o, upch); + output = new char[count]; + i = o; + s.getCharsNoCheck(0, i, output, 0); + output[i++] = upch; } } else { int target = index * 3; @@ -181,11 +181,7 @@ public static String toUpperCase(Locale locale, String s, int count) { if (output == null) { output = new char[count + (count / 6) + 2]; i = o; - if (newString != null) { - System.arraycopy(newString.toCharArray(), 0, output, 0, i); - } else { - System.arraycopy(s.toCharArray(), 0, output, 0, i); - } + s.getCharsNoCheck(0, i, output, 0); } else if (i + (val3 == 0 ? 1 : 2) >= output.length) { char[] newoutput = new char[output.length + (count / 6) + 3]; System.arraycopy(output, 0, newoutput, 0, output.length); @@ -202,11 +198,7 @@ public static String toUpperCase(Locale locale, String s, int count) { } } if (output == null) { - if (newString != null) { - return newString; - } else { - return s; - } + return s; } return output.length == i || output.length - i < 8 ? new String(0, i, output) : new String(output, 0, i); } diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java index 37c1a1df0..864196df9 100644 --- a/libart/src/main/java/java/lang/DexCache.java +++ b/libart/src/main/java/java/lang/DexCache.java @@ -32,27 +32,36 @@ package java.lang; -import com.android.dex.Dex; +import dalvik.annotation.optimization.FastNative; /** * A dex cache holds resolved copies of strings, fields, methods, and classes from the dexfile. */ final class DexCache { - /** Lazily initialized dex file wrapper. Volatile to avoid double-check locking issues. */ - private volatile Dex dex; - /** The location of the associated dex file. */ - String location; + private String location; /** Holds C pointer to dexFile. */ private long dexFile; + /** + * References to CallSite (C array pointer) as they become resolved following + * interpreter semantics. + */ + private long resolvedCallSites; + /** * References to fields (C array pointer) as they become resolved following * interpreter semantics. May refer to fields defined in other dex files. */ private long resolvedFields; + /** + * References to MethodType (C array pointer) as they become resolved following + * interpreter semantics. + */ + private long resolvedMethodTypes; + /** * References to methods (C array pointer) as they become resolved following * interpreter semantics. May refer to methods defined in other dex files. @@ -71,11 +80,21 @@ final class DexCache { */ private long strings; + /** + * The number of elements in the native call sites array. + */ + private int numResolvedCallSites; + /** * The number of elements in the native resolvedFields array. */ private int numResolvedFields; + /** + * The number of elements in the native method types array. + */ + private int numResolvedMethodTypes; + /** * The number of elements in the native resolvedMethods array. */ @@ -93,24 +112,5 @@ final class DexCache { // Only created by the VM. private DexCache() {} - - Dex getDex() { - Dex result = dex; - if (result == null) { - synchronized (this) { - result = dex; - if (result == null) { - dex = result = getDexNative(); - } - } - } - return result; - } - - native Class getResolvedType(int typeIndex); - native String getResolvedString(int stringIndex); - native void setResolvedType(int typeIndex, Class type); - native void setResolvedString(int stringIndex, String string); - private native Dex getDexNative(); } diff --git a/libart/src/main/java/java/lang/StringFactory.java b/libart/src/main/java/java/lang/StringFactory.java index 0a8974041..208a657fb 100644 --- a/libart/src/main/java/java/lang/StringFactory.java +++ b/libart/src/main/java/java/lang/StringFactory.java @@ -17,6 +17,7 @@ package java.lang; +import dalvik.annotation.optimization.FastNative; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; @@ -53,6 +54,7 @@ public static String newStringFromBytes(byte[] data, int offset, int byteCount) return newStringFromBytes(data, offset, byteCount, Charset.defaultCharset()); } + @FastNative public static native String newStringFromBytes(byte[] data, int high, int offset, int byteCount); public static String newStringFromBytes(byte[] data, int offset, int byteCount, String charsetName) throws UnsupportedEncodingException { @@ -219,8 +221,10 @@ public static String newStringFromChars(char[] data, int offset, int charCount) } // The char array passed as {@code java_data} must not be a null reference. + @FastNative static native String newStringFromChars(int offset, int charCount, char[] data); + @FastNative public static native String newStringFromString(String toCopy); public static String newStringFromStringBuffer(StringBuffer stringBuffer) { diff --git a/libart/src/main/java/java/lang/VMClassLoader.java b/libart/src/main/java/java/lang/VMClassLoader.java index a9d6253ad..d44f888f4 100644 --- a/libart/src/main/java/java/lang/VMClassLoader.java +++ b/libart/src/main/java/java/lang/VMClassLoader.java @@ -16,6 +16,7 @@ package java.lang; +import dalvik.annotation.optimization.FastNative; import java.io.File; import java.io.IOException; import java.net.URL; @@ -37,7 +38,6 @@ class VMClassLoader { */ private static ClassPathURLStreamHandler[] createBootClassPathUrlHandlers() { String[] bootClassPathEntries = getBootClassPathEntries(); - ArrayList zipFileUris = new ArrayList(bootClassPathEntries.length); ArrayList urlStreamHandlers = new ArrayList(bootClassPathEntries.length); for (String bootClassPathEntry : bootClassPathEntries) { @@ -47,7 +47,6 @@ private static ClassPathURLStreamHandler[] createBootClassPathUrlHandlers() { // We assume all entries are zip or jar files. URLStreamHandler urlStreamHandler = new ClassPathURLStreamHandler(bootClassPathEntry); - zipFileUris.add(entryUri); urlStreamHandlers.add(urlStreamHandler); } catch (IOException e) { // Skip it @@ -87,6 +86,7 @@ static List getResources(String name) { return list; } + @FastNative native static Class findLoadedClass(ClassLoader cl, String name); /** diff --git a/libart/src/main/java/java/lang/reflect/AbstractMethod.java b/libart/src/main/java/java/lang/reflect/AbstractMethod.java deleted file mode 100644 index 01267655a..000000000 --- a/libart/src/main/java/java/lang/reflect/AbstractMethod.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ -/* - * Copyright (C) 2012 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. - */ - -package java.lang.reflect; - -import com.android.dex.Dex; -import java.lang.annotation.Annotation; -import java.util.List; -import libcore.reflect.GenericSignatureParser; -import libcore.reflect.ListOfTypes; -import libcore.reflect.Types; -import libcore.util.EmptyArray; - -/** - * This class represents an abstract method. Abstract methods are either methods or constructors. - * @hide - */ -public abstract class AbstractMethod extends AccessibleObject { - /** Bits encoding access (e.g. public, private) as well as other runtime specific flags */ - protected int accessFlags; - - /** - * The ArtMethod associated with this Method, requried for dispatching due to entrypoints - * Classloader is held live by the declaring class. - * Hidden to workaround b/16828157. - * @hide - */ - protected long artMethod; - - /** Method's declaring class */ - protected Class declaringClass; - - /** Overriden method's declaring class (same as declaringClass unless declaringClass - * is a proxy class) */ - protected Class declaringClassOfOverriddenMethod; - - /** The method index of this method within its defining dex file */ - protected int dexMethodIndex; - - /** - * Hidden to workaround b/16828157. - * @hide - */ - protected AbstractMethod() { - } - - public T getAnnotation(Class annotationClass) { - return super.getAnnotation(annotationClass); - } - - /** - * We insert native method stubs for abstract methods so we don't have to - * check the access flags at the time of the method call. This results in - * "native abstract" methods, which can't exist. If we see the "abstract" - * flag set, clear the "native" flag. - * - * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED - * position, because the callers of this function are trying to convey - * the "traditional" meaning of the flags to their callers. - */ - private static int fixMethodFlags(int flags) { - if ((flags & Modifier.ABSTRACT) != 0) { - flags &= ~Modifier.NATIVE; - } - flags &= ~Modifier.SYNCHRONIZED; - int ACC_DECLARED_SYNCHRONIZED = 0x00020000; - if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) { - flags |= Modifier.SYNCHRONIZED; - } - return flags & 0xffff; // mask out bits not used by Java - } - - int getModifiers() { - return fixMethodFlags(accessFlags); - } - - boolean isVarArgs() { - return (accessFlags & Modifier.VARARGS) != 0; - } - - boolean isBridge() { - return (accessFlags & Modifier.BRIDGE) != 0; - } - - boolean isSynthetic() { - return (accessFlags & Modifier.SYNTHETIC) != 0; - } - - boolean isDefault() { - return (accessFlags & Modifier.DEFAULT) != 0; - } - - /** - * @hide - */ - public final int getAccessFlags() { - return accessFlags; - } - - /** - * Returns the class that declares this constructor or method. - */ - Class getDeclaringClass() { - return declaringClass; - } - - /** - * Returns the index of this method's ID in its dex file. - * - * @hide - */ - public final int getDexMethodIndex() { - return dexMethodIndex; - } - - /** - * Returns the name of the method or constructor represented by this - * instance. - * - * @return the name of this method - */ - abstract public String getName(); - - /** - * Returns an array of {@code Class} objects associated with the parameter types of this - * abstract method. If the method was declared with no parameters, an - * empty array will be returned. - * - * @return the parameter types - */ - Class[] getParameterTypes() { - Dex dex = declaringClassOfOverriddenMethod.getDex(); - short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); - if (types.length == 0) { - return EmptyArray.CLASS; - } - Class[] parametersArray = new Class[types.length]; - for (int i = 0; i < types.length; i++) { - // Note, in the case of a Proxy the dex cache types are equal. - parametersArray[i] = declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]); - } - return parametersArray; - } - - /** - * Returns true if {@code other} has the same declaring class, name, - * parameters and return type as this method. - */ - @Override public boolean equals(Object other) { - if (!(other instanceof AbstractMethod)) { - return false; - } - // Exactly one instance of each member in this runtime, todo, does this work for proxies? - AbstractMethod otherMethod = (AbstractMethod) other; - return this.declaringClass == otherMethod.declaringClass && - this.dexMethodIndex == otherMethod.dexMethodIndex; - } - - String toGenericString() { - return toGenericStringHelper(); - } - - Type[] getGenericParameterTypes() { - return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericParameterTypes, false); - } - - Type[] getGenericExceptionTypes() { - return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericExceptionTypes, false); - } - - @Override public native Annotation[] getDeclaredAnnotations(); - - @Override public boolean isAnnotationPresent(Class annotationType) { - if (annotationType == null) { - throw new NullPointerException("annotationType == null"); - } - return isAnnotationPresentNative(annotationType); - } - - private native boolean isAnnotationPresentNative(Class annotationType); - - public Annotation[] getAnnotations() { - return super.getAnnotations(); - } - - /** - * Returns an array of arrays that represent the annotations of the formal - * parameters of this method. If there are no parameters on this method, - * then an empty array is returned. If there are no annotations set, then - * and array of empty arrays is returned. - * - * @return an array of arrays of {@code Annotation} instances - */ - public abstract Annotation[][] getParameterAnnotations(); - - /** - * Returns the constructor's signature in non-printable form. This is called - * (only) from IO native code and needed for deriving the serialVersionUID - * of the class - * - * @return The constructor's signature. - */ - @SuppressWarnings("unused") - abstract String getSignature(); - - static final class GenericInfo { - final ListOfTypes genericExceptionTypes; - final ListOfTypes genericParameterTypes; - final Type genericReturnType; - final TypeVariable[] formalTypeParameters; - - GenericInfo(ListOfTypes exceptions, ListOfTypes parameters, Type ret, - TypeVariable[] formal) { - genericExceptionTypes = exceptions; - genericParameterTypes = parameters; - genericReturnType = ret; - formalTypeParameters = formal; - } - } - - /** - * Returns generic information associated with this method/constructor member. - */ - final GenericInfo getMethodOrConstructorGenericInfo() { - String signatureAttribute = getSignatureAttribute(); - Member member; - Class[] exceptionTypes; - boolean method = this instanceof Method; - if (method) { - Method m = (Method) this; - member = m; - exceptionTypes = m.getExceptionTypes(); - } else { - Constructor c = (Constructor) this; - member = c; - exceptionTypes = c.getExceptionTypes(); - } - GenericSignatureParser parser = - new GenericSignatureParser(member.getDeclaringClass().getClassLoader()); - if (method) { - parser.parseForMethod((GenericDeclaration) this, signatureAttribute, exceptionTypes); - } else { - parser.parseForConstructor((GenericDeclaration) this, - signatureAttribute, - exceptionTypes); - } - return new GenericInfo(parser.exceptionTypes, parser.parameterTypes, - parser.returnType, parser.formalTypeParameters); - } - - private String getSignatureAttribute() { - String[] annotation = getSignatureAnnotation(); - if (annotation == null) { - return null; - } - StringBuilder result = new StringBuilder(); - for (String s : annotation) { - result.append(s); - } - return result.toString(); - } - - private native String[] getSignatureAnnotation(); - - protected boolean equalMethodParameters(Class[] params) { - Dex dex = declaringClassOfOverriddenMethod.getDex(); - short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); - if (types.length != params.length) { - return false; - } - for (int i = 0; i < types.length; i++) { - if (declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]) != params[i]) { - return false; - } - } - return true; - } - - protected int compareParameters(Class[] params) { - Dex dex = declaringClassOfOverriddenMethod.getDex(); - short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); - int length = Math.min(types.length, params.length); - for (int i = 0; i < length; i++) { - Class aType = declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]); - Class bType = params[i]; - if (aType != bType) { - int comparison = aType.getName().compareTo(bType.getName()); - if (comparison != 0) { - return comparison; - } - } - } - return types.length - params.length; - } - - /** - * Helper for Method and Constructor for toGenericString - */ - final String toGenericStringHelper() { - StringBuilder sb = new StringBuilder(80); - GenericInfo info = getMethodOrConstructorGenericInfo(); - int modifiers = ((Member)this).getModifiers(); - // append modifiers if any - if (modifiers != 0) { - sb.append(Modifier.toString(modifiers & ~Modifier.VARARGS)).append(' '); - } - // append type parameters - if (info.formalTypeParameters != null && info.formalTypeParameters.length > 0) { - sb.append('<'); - for (int i = 0; i < info.formalTypeParameters.length; i++) { - Types.appendGenericType(sb, info.formalTypeParameters[i]); - if (i < info.formalTypeParameters.length - 1) { - sb.append(","); - } - } - sb.append("> "); - } - Class declaringClass = ((Member) this).getDeclaringClass(); - if (this instanceof Constructor) { - // append constructor name - Types.appendTypeName(sb, declaringClass); - } else { - // append return type - Types.appendGenericType(sb, Types.getType(info.genericReturnType)); - sb.append(' '); - // append method name - Types.appendTypeName(sb, declaringClass); - sb.append(".").append(((Method) this).getName()); - } - // append parameters - sb.append('('); - Types.appendArrayGenericType(sb, info.genericParameterTypes.getResolvedTypes()); - sb.append(')'); - // append exceptions if any - Type[] genericExceptionTypeArray = - Types.getTypeArray(info.genericExceptionTypes, false); - if (genericExceptionTypeArray.length > 0) { - sb.append(" throws "); - Types.appendArrayGenericType(sb, genericExceptionTypeArray); - } - return sb.toString(); - } -} diff --git a/luni/src/benchmark/native/libcore_io_Memory_bench.cpp b/luni/src/benchmark/native/libcore_io_Memory_bench.cpp index b5a9d5f3d..ce66067e0 100644 --- a/luni/src/benchmark/native/libcore_io_Memory_bench.cpp +++ b/luni/src/benchmark/native/libcore_io_Memory_bench.cpp @@ -21,7 +21,7 @@ template void swap_bench(benchmark::State& state, void (*swap_func)(T*, const T*, size_t)) { - size_t num_elements = state.range_x(); + size_t num_elements = state.range(0); T* src; T* dst; diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java index c43c1ba21..7db7f75e2 100644 --- a/luni/src/main/java/android/system/Os.java +++ b/luni/src/main/java/android/system/Os.java @@ -62,6 +62,25 @@ private Os() {} /** @hide */ public static void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { Libcore.os.bind(fd, address); } + /** + * See capget(2). + * + * @hide + */ + public static StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException { + return Libcore.os.capget(hdr); + } + + /** + * See capset(2). + * + * @hide + */ + public static void capset(StructCapUserHeader hdr, StructCapUserData[] data) + throws ErrnoException { + Libcore.os.capset(hdr, data); + } + /** * See chmod(2). */ @@ -173,6 +192,11 @@ private Os() {} */ public static String getenv(String name) { return Libcore.os.getenv(name); } + /** + * See getifaddrs(3). + */ + /** @hide */ public static StructIfaddrs[] getifaddrs() throws ErrnoException { return Libcore.os.getifaddrs(); } + /** @hide */ public static String getnameinfo(InetAddress address, int flags) throws GaiException { return Libcore.os.getnameinfo(address, flags); } /** @@ -221,13 +245,21 @@ private Os() {} */ public static int getuid() { return Libcore.os.getuid(); } - /** @hide */ public static int getxattr(String path, String name, byte[] outValue) throws ErrnoException { return Libcore.os.getxattr(path, name, outValue); } + /** + * See getxattr(2) + */ + public static byte[] getxattr(String path, String name) throws ErrnoException { return Libcore.os.getxattr(path, name); } /** * See if_indextoname(3). */ public static String if_indextoname(int index) { return Libcore.os.if_indextoname(index); } + /** + * See if_nametoindex(3). + */ + public static int if_nametoindex(String name) { return Libcore.os.if_nametoindex(name); } + /** * See inet_pton(3). */ @@ -261,6 +293,11 @@ private Os() {} */ public static void listen(FileDescriptor fd, int backlog) throws ErrnoException { Libcore.os.listen(fd, backlog); } + /** + * See listxattr(2) + */ + public static String[] listxattr(String path) throws ErrnoException { return Libcore.os.listxattr(path); } + /** * See lseek(2). */ @@ -333,7 +370,7 @@ private Os() {} public static int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return Libcore.os.poll(fds, timeoutMs); } /** - * See posix_fallocate(2). + * See posix_fallocate(3). */ public static void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException { Libcore.os.posix_fallocate(fd, offset, length); } @@ -397,7 +434,10 @@ private Os() {} */ public static void remove(String path) throws ErrnoException { Libcore.os.remove(path); } - /** @hide */ public static void removexattr(String path, String name) throws ErrnoException { Libcore.os.removexattr(path, name); } + /** + * See removexattr(2). + */ + public static void removexattr(String path, String name) throws ErrnoException { Libcore.os.removexattr(path, name); } /** * See rename(2). @@ -466,7 +506,12 @@ private Os() {} /** @hide */ public static void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptByte(fd, level, option, value); } /** @hide */ public static void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { Libcore.os.setsockoptIfreq(fd, level, option, value); } - /** @hide */ public static void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptInt(fd, level, option, value); } + + /** + * See setsockopt(2). + */ + public static void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptInt(fd, level, option, value); } + /** @hide */ public static void setsockoptIpMreqn(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptIpMreqn(fd, level, option, value); } /** @hide */ public static void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException { Libcore.os.setsockoptGroupReq(fd, level, option, value); } /** @hide */ public static void setsockoptGroupSourceReq(FileDescriptor fd, int level, int option, StructGroupSourceReq value) throws ErrnoException { Libcore.os.setsockoptGroupSourceReq(fd, level, option, value); } @@ -478,7 +523,10 @@ private Os() {} */ public static void setuid(int uid) throws ErrnoException { Libcore.os.setuid(uid); } - /** @hide */ public static void setxattr(String path, String name, byte[] value, int flags) throws ErrnoException { Libcore.os.setxattr(path, name, value, flags); }; + /** + * See setxattr(2) + */ + public static void setxattr(String path, String name, byte[] value, int flags) throws ErrnoException { Libcore.os.setxattr(path, name, value, flags); }; /** * See shutdown(2). diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java index 31f84e9af..adad301e0 100644 --- a/luni/src/main/java/android/system/OsConstants.java +++ b/luni/src/main/java/android/system/OsConstants.java @@ -23,6 +23,20 @@ public final class OsConstants { private OsConstants() { } + /** + * Returns the index of the element in the cap_user_data array that this capability is stored + * in. + * @hide + */ + public static int CAP_TO_INDEX(int x) { return x >>> 5; } + + /** + * Returns the mask for the given capability. This is relative to the capability's cap_user_data + * element, the index of which can be retrieved with CAP_TO_INDEX. + * @hide + */ + public static int CAP_TO_MASK(int x) { return 1 << (x & 31); } + /** * Tests whether the given mode is a block device. */ @@ -264,6 +278,10 @@ private OsConstants() { public static final int F_SETOWN = placeholder(); public static final int F_UNLCK = placeholder(); public static final int F_WRLCK = placeholder(); + /** @hide */ public static final int ICMP_ECHO = placeholder(); + /** @hide */ public static final int ICMP_ECHOREPLY = placeholder(); + /** @hide */ public static final int ICMP6_ECHO_REQUEST = placeholder(); + /** @hide */ public static final int ICMP6_ECHO_REPLY = placeholder(); public static final int IFA_F_DADFAILED = placeholder(); public static final int IFA_F_DEPRECATED = placeholder(); public static final int IFA_F_HOMEADDRESS = placeholder(); @@ -309,12 +327,14 @@ private OsConstants() { public static final int IPV6_TCLASS = placeholder(); public static final int IPV6_UNICAST_HOPS = placeholder(); public static final int IPV6_V6ONLY = placeholder(); + /** @hide */ public static final int IP_MULTICAST_ALL = placeholder(); public static final int IP_MULTICAST_IF = placeholder(); public static final int IP_MULTICAST_LOOP = placeholder(); public static final int IP_MULTICAST_TTL = placeholder(); /** @hide */ public static final int IP_RECVTOS = placeholder(); public static final int IP_TOS = placeholder(); public static final int IP_TTL = placeholder(); + /** @hide */ public static final int _LINUX_CAPABILITY_VERSION_3 = placeholder(); public static final int MAP_FIXED = placeholder(); /** @hide */ public static final int MAP_POPULATE = placeholder(); public static final int MAP_PRIVATE = placeholder(); @@ -367,6 +387,8 @@ private OsConstants() { public static final int POLLRDNORM = placeholder(); public static final int POLLWRBAND = placeholder(); public static final int POLLWRNORM = placeholder(); + /** @hide */ public static final int PR_CAP_AMBIENT = placeholder(); + /** @hide */ public static final int PR_CAP_AMBIENT_RAISE = placeholder(); public static final int PR_GET_DUMPABLE = placeholder(); public static final int PR_SET_DUMPABLE = placeholder(); public static final int PR_SET_NO_NEW_PRIVS = placeholder(); @@ -444,6 +466,7 @@ private OsConstants() { public static final int SO_BINDTODEVICE = placeholder(); public static final int SO_BROADCAST = placeholder(); public static final int SO_DEBUG = placeholder(); + /** @hide */ public static final int SO_DOMAIN = placeholder(); public static final int SO_DONTROUTE = placeholder(); public static final int SO_ERROR = placeholder(); public static final int SO_KEEPALIVE = placeholder(); @@ -451,6 +474,7 @@ private OsConstants() { public static final int SO_OOBINLINE = placeholder(); public static final int SO_PASSCRED = placeholder(); public static final int SO_PEERCRED = placeholder(); + /** @hide */ public static final int SO_PROTOCOL = placeholder(); public static final int SO_RCVBUF = placeholder(); public static final int SO_RCVLOWAT = placeholder(); public static final int SO_RCVTIMEO = placeholder(); @@ -495,6 +519,7 @@ private OsConstants() { public static final int S_IXOTH = placeholder(); public static final int S_IXUSR = placeholder(); public static final int TCP_NODELAY = placeholder(); + public static final int TCP_USER_TIMEOUT = placeholder(); /** @hide */ public static final int TIOCOUTQ = placeholder(); /** @hide */ public static final int UNIX_PATH_MAX = placeholder(); public static final int WCONTINUED = placeholder(); diff --git a/luni/src/main/java/android/system/StructCapUserData.java b/luni/src/main/java/android/system/StructCapUserData.java new file mode 100644 index 000000000..af63caf76 --- /dev/null +++ b/luni/src/main/java/android/system/StructCapUserData.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 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. + */ + +package android.system; + +import libcore.util.Objects; + +/** + * Corresponds to Linux' __user_cap_data_struct for capget and capset. + * + * @hide + */ +public final class StructCapUserData { + /** Effective capability mask. */ + public final int effective; /* __u32 */ + + /** Permitted capability mask. */ + public final int permitted; /* __u32 */ + + /** Inheritable capability mask. */ + public final int inheritable; /* __u32 */ + + /** + * Constructs an instance with the given field values. + */ + public StructCapUserData(int effective, int permitted, int inheritable) { + this.effective = effective; + this.permitted = permitted; + this.inheritable = inheritable; + } + + @Override public String toString() { + return Objects.toString(this); + } +} diff --git a/luni/src/main/java/android/system/StructCapUserHeader.java b/luni/src/main/java/android/system/StructCapUserHeader.java new file mode 100644 index 000000000..abbb3954c --- /dev/null +++ b/luni/src/main/java/android/system/StructCapUserHeader.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 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. + */ + +package android.system; + +import libcore.util.Objects; + +/** + * Corresponds to Linux' __user_cap_header_struct for capget and capset. + * + * @hide + */ +public final class StructCapUserHeader { + /** + * Version of the header. Note this is not final as capget() may mutate the field when an + * invalid version is provided. See + * capget(2). + */ + public int version; /* __u32 */ + + /** Pid of the header. The pid a call applies to. */ + public final int pid; + + /** + * Constructs an instance with the given field values. + */ + public StructCapUserHeader(int version, int pid) { + this.version = version; + this.pid = pid; + } + + @Override public String toString() { + return Objects.toString(this); + } +} diff --git a/luni/src/main/java/android/system/StructIcmpHdr.java b/luni/src/main/java/android/system/StructIcmpHdr.java new file mode 100644 index 000000000..87ae679da --- /dev/null +++ b/luni/src/main/java/android/system/StructIcmpHdr.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 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. + */ + +package android.system; + +import static android.system.OsConstants.ICMP6_ECHO_REQUEST; +import static android.system.OsConstants.ICMP_ECHO; + +/** + * Corresponds to C's {@code struct icmphdr} from linux/icmp.h and {@code struct icmp6hdr} from + * linux/icmpv6.h + * + * @hide + */ +public final class StructIcmpHdr { + private byte[] packet; + + private StructIcmpHdr() { + packet = new byte[8]; + } + + /* + * Echo or Echo Reply Message + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Code | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Identifier | Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data ... + * +-+-+-+-+- + */ + public static StructIcmpHdr IcmpEchoHdr(boolean ipv4, int seq) { + StructIcmpHdr hdr = new StructIcmpHdr(); + hdr.packet[0] = ipv4 ? (byte) ICMP_ECHO : (byte) ICMP6_ECHO_REQUEST; + // packet[1]: Code is always zero. + // packet[2,3]: Checksum is computed by kernel. + // packet[4,5]: ID (= port) inserted by kernel. + hdr.packet[6] = (byte) (seq >> 8); + hdr.packet[7] = (byte) seq; + return hdr; + } + + public byte[] getBytes() { + return packet.clone(); + } +} diff --git a/luni/src/main/java/android/system/StructIfaddrs.java b/luni/src/main/java/android/system/StructIfaddrs.java new file mode 100644 index 000000000..7769f282a --- /dev/null +++ b/luni/src/main/java/android/system/StructIfaddrs.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 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. + */ + +package android.system; + +import java.net.InetAddress; + +/** + * Information returned by {@link Os#getifaddrs}. Loosely corresponds to C's + * {@code struct ifaddrs} from {@code }. + * + * @hide + */ +public final class StructIfaddrs { + public final String ifa_name; + public final int ifa_flags; + public final InetAddress ifa_addr; + public final InetAddress ifa_netmask; + public final InetAddress ifa_broadaddr; + public final byte[] hwaddr; + + /** + * Constructs an instance with the given field values. + */ + public StructIfaddrs(String ifa_name, int ifa_flags, InetAddress ifa_addr, InetAddress ifa_netmask, + InetAddress ifa_broadaddr, byte[] hwaddr) { + this.ifa_name = ifa_name; + this.ifa_flags = ifa_flags; + this.ifa_addr = ifa_addr; + this.ifa_netmask = ifa_netmask; + this.ifa_broadaddr = ifa_broadaddr; + this.hwaddr = hwaddr; + } +} diff --git a/luni/src/main/java/java/lang/ref/FinalizerReference.java b/luni/src/main/java/java/lang/ref/FinalizerReference.java index 02cfa01cd..d7e803e4f 100644 --- a/luni/src/main/java/java/lang/ref/FinalizerReference.java +++ b/luni/src/main/java/java/lang/ref/FinalizerReference.java @@ -16,6 +16,8 @@ package java.lang.ref; +import dalvik.annotation.optimization.FastNative; + /** * @hide */ @@ -100,9 +102,12 @@ private static boolean enqueueSentinelReference(Sentinel sentinel) { // We search the list for that FinalizerReference (it should be at or near the head), // and then put it on the queue so that it can be finalized. for (FinalizerReference r = head; r != null; r = r.next) { - if (r.referent == sentinel) { + // Use getReferent() instead of directly accessing the referent field not to race + // with GC reference processing. Can't use get() either because it's overridden to + // return the zombie. + if (r.getReferent() == sentinel) { FinalizerReference sentinelReference = (FinalizerReference) r; - sentinelReference.referent = null; + sentinelReference.clearReferent(); sentinelReference.zombie = sentinel; // Make a single element list, then enqueue the reference on the daemon unenqueued // list. This is required instead of enqueuing directly on the finalizer queue @@ -126,6 +131,9 @@ private static boolean enqueueSentinelReference(Sentinel sentinel) { throw new AssertionError("newly-created live Sentinel not on list!"); } + @FastNative + private final native T getReferent(); + @FastNative private native boolean makeCircularListIfUnenqueued(); /** diff --git a/luni/src/main/java/java/math/BigDecimal.java b/luni/src/main/java/java/math/BigDecimal.java index 0e8976248..d03b66fe9 100644 --- a/luni/src/main/java/java/math/BigDecimal.java +++ b/luni/src/main/java/java/math/BigDecimal.java @@ -937,8 +937,14 @@ public BigDecimal multiply(BigDecimal multiplicand) { } /* Let be: this = [u1,s1] and multiplicand = [u2,s2] so: * this x multiplicand = [ s1 * s2 , s1 + s2 ] */ - if(this.bitLength + multiplicand.bitLength < 64) { - return valueOf(this.smallValue*multiplicand.smallValue, safeLongToInt(newScale)); + if (this.bitLength + multiplicand.bitLength < 64) { + long unscaledValue = this.smallValue * multiplicand.smallValue; + // b/19185440 Case where result should be +2^63 but unscaledValue overflowed to -2^63 + boolean longMultiplicationOverflowed = (unscaledValue == Long.MIN_VALUE) && + (Math.signum(smallValue) * Math.signum(multiplicand.smallValue) > 0); + if (!longMultiplicationOverflowed) { + return valueOf(unscaledValue, safeLongToInt(newScale)); + } } return new BigDecimal(this.getUnscaledValue().multiply( multiplicand.getUnscaledValue()), safeLongToInt(newScale)); @@ -1035,10 +1041,13 @@ public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMod if(this.bitLength < 64 && divisor.bitLength < 64 ) { if(diffScale == 0) { - return dividePrimitiveLongs(this.smallValue, - divisor.smallValue, - scale, - roundingMode ); + // http://b/26105053 - corner case: Long.MIN_VALUE / (-1) overflows a long + if (this.smallValue != Long.MIN_VALUE || divisor.smallValue != -1) { + return dividePrimitiveLongs(this.smallValue, + divisor.smallValue, + scale, + roundingMode); + } } else if(diffScale > 0) { if(diffScale < MathUtils.LONG_POWERS_OF_TEN.length && divisor.bitLength + LONG_POWERS_OF_TEN_BIT_LENGTH[(int)diffScale] < 64) { @@ -1085,7 +1094,7 @@ private static BigDecimal divideBigIntegers(BigInteger scaledDividend, BigIntege if(scaledDivisor.bitLength() < 63) { // 63 in order to avoid out of long after *2 long rem = remainder.longValue(); long divisor = scaledDivisor.longValue(); - compRem = longCompareTo(Math.abs(rem) * 2,Math.abs(divisor)); + compRem = compareForRounding(rem, divisor); // To look if there is a carry compRem = roundingBehavior(quotient.testBit(0) ? 1 : 0, sign * (5 + compRem), roundingMode); @@ -1113,8 +1122,7 @@ private static BigDecimal dividePrimitiveLongs(long scaledDividend, long scaledD int sign = Long.signum( scaledDividend ) * Long.signum( scaledDivisor ); if (remainder != 0) { // Checking if: remainder * 2 >= scaledDivisor - int compRem; // 'compare to remainder' - compRem = longCompareTo(Math.abs(remainder) * 2,Math.abs(scaledDivisor)); + int compRem = compareForRounding(remainder, scaledDivisor); // 'compare to remainder' // To look if there is a carry quotient += roundingBehavior(((int)quotient) & 1, sign * (5 + compRem), @@ -1340,7 +1348,7 @@ public BigDecimal divide(BigDecimal divisor, MathContext mc) { public BigDecimal divideToIntegralValue(BigDecimal divisor) { BigInteger integralValue; // the integer of result BigInteger powerOfTen; // some power of ten - BigInteger quotAndRem[] = {getUnscaledValue()}; + long newScale = (long)this.scale - divisor.scale; long tempScale = 0; int i = 1; @@ -1365,7 +1373,7 @@ public BigDecimal divideToIntegralValue(BigDecimal divisor) { integralValue = getUnscaledValue().multiply(powerOfTen).divide( divisor.getUnscaledValue() ); // To strip trailing zeros approximating to the preferred scale while (!integralValue.testBit(0)) { - quotAndRem = integralValue.divideAndRemainder(TEN_POW[i]); + BigInteger[] quotAndRem = integralValue.divideAndRemainder(TEN_POW[i]); if ((quotAndRem[1].signum() == 0) && (tempScale - i >= newScale)) { tempScale -= i; @@ -2700,9 +2708,56 @@ private void inplaceRound(MathContext mc) { setUnscaledValue(integerAndFraction[0]); } - private static int longCompareTo(long value1, long value2) { + /** + * Returns -1, 0, and 1 if {@code value1 < value2}, {@code value1 == value2}, + * and {@code value1 > value2}, respectively, when comparing without regard + * to the values' sign. + * + *

Note that this implementation deals correctly with Long.MIN_VALUE, + * whose absolute magnitude is larger than any other {@code long} value. + */ + private static int compareAbsoluteValues(long value1, long value2) { + // Map long values to the range -1 .. Long.MAX_VALUE so that comparison + // of absolute magnitude can be done using regular long arithmetics. + // This deals correctly with Long.MIN_VALUE, whose absolute magnitude + // is larger than any other long value, and which is mapped to + // Long.MAX_VALUE here. + // Values that only differ by sign get mapped to the same value, for + // example both +3 and -3 get mapped to +2. + value1 = Math.abs(value1) - 1; + value2 = Math.abs(value2) - 1; + // Unlike Long.compare(), we guarantee to return specifically -1 and +1 return value1 > value2 ? 1 : (value1 < value2 ? -1 : 0); } + + /** + * Compares {@code n} against {@code 0.5 * d} in absolute terms (ignoring sign) + * and with arithmetics that are safe against overflow or loss of precision. + * Returns -1 if {@code n} is less than {@code 0.5 * d}, 0 if {@code n == 0.5 * d}, + * or +1 if {@code n > 0.5 * d} when comparing the absolute values under such + * arithmetics. + */ + private static int compareForRounding(long n, long d) { + long halfD = d / 2; // rounds towards 0 + if (n == halfD || n == -halfD) { + // In absolute terms: Because n == halfD, we know that 2 * n + lsb == d + // for some lsb value 0 or 1. This means that n == d/2 (result 0) if + // lsb is 0, or n < d/2 (result -1) if lsb is 1. In either case, the + // result is -lsb. + // Since we're calculating in absolute terms, we need the absolute lsb + // (d & 1) as opposed to the signed lsb (d % 2) which would be -1 for + // negative odd values of d. + int lsb = (int) d & 1; + return -lsb; // returns 0 or -1 + } else { + // In absolute terms, either 2 * n + 1 < d (in the case of n < halfD), + // or 2 * n > d (in the case of n > halfD). + // In either case, comparing n against halfD gets the right result + // -1 or +1, respectively. + return compareAbsoluteValues(n, halfD); + } + } + /** * This method implements an efficient rounding for numbers which unscaled * value fits in the type {@code long}. @@ -2724,7 +2779,7 @@ private void smallRound(MathContext mc, int discardedPrecision) { // If the discarded fraction is non-zero perform rounding if (fraction != 0) { // To check if the discarded fraction >= 0.5 - compRem = longCompareTo(Math.abs(fraction) * 2, sizeOfFraction); + compRem = compareForRounding(fraction, sizeOfFraction); // To look if there is a carry integer += roundingBehavior( ((int)integer) & 1, Long.signum(fraction) * (5 + compRem), diff --git a/luni/src/main/java/java/math/BigInt.java b/luni/src/main/java/java/math/BigInt.java index 2cffee652..5e28a73df 100644 --- a/luni/src/main/java/java/math/BigInt.java +++ b/luni/src/main/java/java/math/BigInt.java @@ -334,11 +334,11 @@ static BigInt modInverse(BigInt a, BigInt m) { static BigInt generatePrimeDefault(int bitLength) { BigInt r = newBigInt(); - NativeBN.BN_generate_prime_ex(r.bignum, bitLength, false, 0, 0, 0); + NativeBN.BN_generate_prime_ex(r.bignum, bitLength, false, 0, 0); return r; } boolean isPrime(int certainty) { - return NativeBN.BN_is_prime_ex(bignum, certainty, 0); + return NativeBN.BN_primality_test(bignum, certainty, false); } } diff --git a/luni/src/main/java/java/math/Multiplication.java b/luni/src/main/java/java/math/Multiplication.java index 093b1b72a..2a4285b56 100644 --- a/luni/src/main/java/java/math/Multiplication.java +++ b/luni/src/main/java/java/math/Multiplication.java @@ -25,13 +25,13 @@ class Multiplication { /** Just to denote that this class can't be instantiated. */ private Multiplication() {} - // BEGIN android-removed + // BEGIN Android-removed // /** // * Break point in digits (number of {@code int} elements) // * between Karatsuba and Pencil and Paper multiply. // */ // static final int whenUseKaratsuba = 63; // an heuristic value - // END android-removed + // END Android-removed /** * An array with powers of ten that fit in the type {@code int}. diff --git a/luni/src/main/java/java/math/NativeBN.java b/luni/src/main/java/java/math/NativeBN.java index 64b446810..d269f2e27 100644 --- a/luni/src/main/java/java/math/NativeBN.java +++ b/luni/src/main/java/java/math/NativeBN.java @@ -120,12 +120,15 @@ final class NativeBN { public static native void BN_generate_prime_ex(long ret, int bits, boolean safe, - long add, long rem, long cb); + long add, long rem); // int BN_generate_prime_ex(BIGNUM *ret, int bits, int safe, // const BIGNUM *add, const BIGNUM *rem, BN_GENCB *cb); - public static native boolean BN_is_prime_ex(long p, int nchecks, long cb); - // int BN_is_prime_ex(const BIGNUM *p, int nchecks, BN_CTX *ctx, BN_GENCB *cb); + public static native boolean BN_primality_test(long candidate, int checks, + boolean do_trial_division); + // int BN_primality_test(int *is_probably_prime, const BIGNUM *candidate, int checks, + // BN_CTX *ctx, int do_trial_division, BN_GENCB *cb); + // Returns *is_probably_prime on success and throws an exception on error. public static native long getNativeFinalizer(); // &BN_free diff --git a/luni/src/main/java/java/net/DefaultFileNameMap.java b/luni/src/main/java/java/net/DefaultFileNameMap.java index 2f254d991..6222d7576 100644 --- a/luni/src/main/java/java/net/DefaultFileNameMap.java +++ b/luni/src/main/java/java/net/DefaultFileNameMap.java @@ -37,6 +37,6 @@ public String getContentTypeFor(String filename) { if (firstCharInExtension > filename.lastIndexOf('/')) { ext = filename.substring(firstCharInExtension, lastCharInExtension); } - return MimeUtils.guessMimeTypeFromExtension(ext.toLowerCase(Locale.US)); + return MimeUtils.guessMimeTypeFromExtension(ext); } } diff --git a/luni/src/main/java/java/security/security.properties b/luni/src/main/java/java/security/security.properties index 6b9007ba2..b5f4d25d0 100644 --- a/luni/src/main/java/java/security/security.properties +++ b/luni/src/main/java/java/security/security.properties @@ -62,3 +62,5 @@ ssl.disablePeerCertificateChainVerification=false # Disable weak algorithms in CertPathVerifier and CertPathBuilder. jdk.certpath.disabledAlgorithms=MD2, MD4, RSA keySize < 1024, DSA keySize < 1024, EC keySize < 160 + +securerandom.strongAlgorithms=SHA1PRNG:AndroidOpenSSL diff --git a/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java b/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java deleted file mode 100644 index 9fe707d8e..000000000 --- a/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * Exception thrown when a thread tries to wait upon a barrier that is - * in a broken state, or which enters the broken state while the thread - * is waiting. - * - * @see CyclicBarrier - * - * @since 1.5 - * @author Doug Lea - */ -public class BrokenBarrierException extends Exception { - private static final long serialVersionUID = 7117394618823254244L; - - /** - * Constructs a {@code BrokenBarrierException} with no specified detail - * message. - */ - public BrokenBarrierException() {} - - /** - * Constructs a {@code BrokenBarrierException} with the specified - * detail message. - * - * @param message the detail message - */ - public BrokenBarrierException(String message) { - super(message); - } -} diff --git a/luni/src/main/java/java/util/concurrent/Callable.java b/luni/src/main/java/java/util/concurrent/Callable.java deleted file mode 100644 index a22ec500f..000000000 --- a/luni/src/main/java/java/util/concurrent/Callable.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * A task that returns a result and may throw an exception. - * Implementors define a single method with no arguments called - * {@code call}. - * - *

The {@code Callable} interface is similar to {@link - * java.lang.Runnable}, in that both are designed for classes whose - * instances are potentially executed by another thread. A - * {@code Runnable}, however, does not return a result and cannot - * throw a checked exception. - * - *

The {@link Executors} class contains utility methods to - * convert from other common forms to {@code Callable} classes. - * - * @see Executor - * @since 1.5 - * @author Doug Lea - * @param the result type of method {@code call} - */ -@FunctionalInterface -public interface Callable { - /** - * Computes a result, or throws an exception if unable to do so. - * - * @return computed result - * @throws Exception if unable to compute a result - */ - V call() throws Exception; -} diff --git a/luni/src/main/java/java/util/concurrent/CancellationException.java b/luni/src/main/java/java/util/concurrent/CancellationException.java deleted file mode 100644 index 25ab2715a..000000000 --- a/luni/src/main/java/java/util/concurrent/CancellationException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * Exception indicating that the result of a value-producing task, - * such as a {@link FutureTask}, cannot be retrieved because the task - * was cancelled. - * - * @since 1.5 - * @author Doug Lea - */ -public class CancellationException extends IllegalStateException { - private static final long serialVersionUID = -9202173006928992231L; - - /** - * Constructs a {@code CancellationException} with no detail message. - */ - public CancellationException() {} - - /** - * Constructs a {@code CancellationException} with the specified detail - * message. - * - * @param message the detail message - */ - public CancellationException(String message) { - super(message); - } -} diff --git a/luni/src/main/java/java/util/concurrent/CompletionException.java b/luni/src/main/java/java/util/concurrent/CompletionException.java deleted file mode 100644 index 9b905d2d3..000000000 --- a/luni/src/main/java/java/util/concurrent/CompletionException.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * Exception thrown when an error or other exception is encountered - * in the course of completing a result or task. - * - * @since 1.8 - * @author Doug Lea - */ -public class CompletionException extends RuntimeException { - private static final long serialVersionUID = 7830266012832686185L; - - /** - * Constructs a {@code CompletionException} with no detail message. - * The cause is not initialized, and may subsequently be - * initialized by a call to {@link #initCause(Throwable) initCause}. - */ - protected CompletionException() { } - - /** - * Constructs a {@code CompletionException} with the specified detail - * message. The cause is not initialized, and may subsequently be - * initialized by a call to {@link #initCause(Throwable) initCause}. - * - * @param message the detail message - */ - protected CompletionException(String message) { - super(message); - } - - /** - * Constructs a {@code CompletionException} with the specified detail - * message and cause. - * - * @param message the detail message - * @param cause the cause (which is saved for later retrieval by the - * {@link #getCause()} method) - */ - public CompletionException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Constructs a {@code CompletionException} with the specified cause. - * The detail message is set to {@code (cause == null ? null : - * cause.toString())} (which typically contains the class and - * detail message of {@code cause}). - * - * @param cause the cause (which is saved for later retrieval by the - * {@link #getCause()} method) - */ - public CompletionException(Throwable cause) { - super(cause); - } -} diff --git a/luni/src/main/java/java/util/concurrent/CompletionService.java b/luni/src/main/java/java/util/concurrent/CompletionService.java deleted file mode 100644 index 06075962e..000000000 --- a/luni/src/main/java/java/util/concurrent/CompletionService.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * A service that decouples the production of new asynchronous tasks - * from the consumption of the results of completed tasks. Producers - * {@code submit} tasks for execution. Consumers {@code take} - * completed tasks and process their results in the order they - * complete. A {@code CompletionService} can for example be used to - * manage asynchronous I/O, in which tasks that perform reads are - * submitted in one part of a program or system, and then acted upon - * in a different part of the program when the reads complete, - * possibly in a different order than they were requested. - * - *

Typically, a {@code CompletionService} relies on a separate - * {@link Executor} to actually execute the tasks, in which case the - * {@code CompletionService} only manages an internal completion - * queue. The {@link ExecutorCompletionService} class provides an - * implementation of this approach. - * - *

Memory consistency effects: Actions in a thread prior to - * submitting a task to a {@code CompletionService} - * happen-before - * actions taken by that task, which in turn happen-before - * actions following a successful return from the corresponding {@code take()}. - */ -public interface CompletionService { - /** - * Submits a value-returning task for execution and returns a Future - * representing the pending results of the task. Upon completion, - * this task may be taken or polled. - * - * @param task the task to submit - * @return a Future representing pending completion of the task - * @throws RejectedExecutionException if the task cannot be - * scheduled for execution - * @throws NullPointerException if the task is null - */ - Future submit(Callable task); - - /** - * Submits a Runnable task for execution and returns a Future - * representing that task. Upon completion, this task may be - * taken or polled. - * - * @param task the task to submit - * @param result the result to return upon successful completion - * @return a Future representing pending completion of the task, - * and whose {@code get()} method will return the given - * result value upon completion - * @throws RejectedExecutionException if the task cannot be - * scheduled for execution - * @throws NullPointerException if the task is null - */ - Future submit(Runnable task, V result); - - /** - * Retrieves and removes the Future representing the next - * completed task, waiting if none are yet present. - * - * @return the Future representing the next completed task - * @throws InterruptedException if interrupted while waiting - */ - Future take() throws InterruptedException; - - /** - * Retrieves and removes the Future representing the next - * completed task, or {@code null} if none are present. - * - * @return the Future representing the next completed task, or - * {@code null} if none are present - */ - Future poll(); - - /** - * Retrieves and removes the Future representing the next - * completed task, waiting if necessary up to the specified wait - * time if none are yet present. - * - * @param timeout how long to wait before giving up, in units of - * {@code unit} - * @param unit a {@code TimeUnit} determining how to interpret the - * {@code timeout} parameter - * @return the Future representing the next completed task or - * {@code null} if the specified waiting time elapses - * before one is present - * @throws InterruptedException if interrupted while waiting - */ - Future poll(long timeout, TimeUnit unit) throws InterruptedException; -} diff --git a/luni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java b/luni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java deleted file mode 100644 index 96225b135..000000000 --- a/luni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java +++ /dev/null @@ -1,851 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -package java.util.concurrent; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.AbstractList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.NoSuchElementException; -import java.util.RandomAccess; -import java.util.function.Consumer; -import java.util.function.UnaryOperator; - -import libcore.util.EmptyArray; -import libcore.util.Objects; - -/** - * A thread-safe random-access list. - * - *

Read operations (including {@link #get}) do not block and may overlap with - * update operations. Reads reflect the results of the most recently completed - * operations. Aggregate operations like {@link #addAll} and {@link #clear} are - * atomic; they never expose an intermediate state. - * - *

Iterators of this list never throw {@link - * ConcurrentModificationException}. When an iterator is created, it keeps a - * copy of the list's contents. It is always safe to iterate this list, but - * iterations may not reflect the latest state of the list. - * - *

Iterators returned by this list and its sub lists cannot modify the - * underlying list. In particular, {@link Iterator#remove}, {@link - * ListIterator#add} and {@link ListIterator#set} all throw {@link - * UnsupportedOperationException}. - * - *

This class offers extended API beyond the {@link List} interface. It - * includes additional overloads for indexed search ({@link #indexOf} and {@link - * #lastIndexOf}) and methods for conditional adds ({@link #addIfAbsent} and - * {@link #addAllAbsent}). - */ -public class CopyOnWriteArrayList implements List, RandomAccess, Cloneable, Serializable { - - private static final long serialVersionUID = 8673264195747942595L; - - /** - * Holds the latest snapshot of the list's data. This field is volatile so - * that data can be read without synchronization. As a consequence, all - * writes to this field must be atomic; it is an error to modify the - * contents of an array after it has been assigned to this field. - * - * Synchronization is required by all update operations. This defends - * against one update clobbering the result of another operation. For - * example, 100 threads simultaneously calling add() will grow the list's - * size by 100 when they have completed. No update operations are lost! - * - * Maintainers should be careful to read this field only once in - * non-blocking read methods. Write methods must be synchronized to avoid - * clobbering concurrent writes. - */ - private transient volatile Object[] elements; - - /** - * Creates a new empty instance. - */ - public CopyOnWriteArrayList() { - elements = EmptyArray.OBJECT; - } - - /** - * Creates a new instance containing the elements of {@code collection}. - */ - @SuppressWarnings("unchecked") - public CopyOnWriteArrayList(Collection collection) { - this((E[]) collection.toArray()); - } - - /** - * Creates a new instance containing the elements of {@code array}. - */ - public CopyOnWriteArrayList(E[] array) { - this.elements = Arrays.copyOf(array, array.length, Object[].class); - } - - @Override public Object clone() { - try { - CopyOnWriteArrayList result = (CopyOnWriteArrayList) super.clone(); - result.elements = result.elements.clone(); - return result; - } catch (CloneNotSupportedException e) { - throw new AssertionError(e); - } - } - - public int size() { - return elements.length; - } - - @SuppressWarnings("unchecked") - public E get(int index) { - return (E) elements[index]; - } - - public boolean contains(Object o) { - return indexOf(o) != -1; - } - - public boolean containsAll(Collection collection) { - Object[] snapshot = elements; - return containsAll(collection, snapshot, 0, snapshot.length); - } - - static boolean containsAll(Collection collection, Object[] snapshot, int from, int to) { - for (Object o : collection) { - if (indexOf(o, snapshot, from, to) == -1) { - return false; - } - } - return true; - } - - /** - * Searches this list for {@code object} and returns the index of the first - * occurrence that is at or after {@code from}. - * - * @return the index or -1 if the object was not found. - */ - public int indexOf(E object, int from) { - Object[] snapshot = elements; - return indexOf(object, snapshot, from, snapshot.length); - } - - public int indexOf(Object object) { - Object[] snapshot = elements; - return indexOf(object, snapshot, 0, snapshot.length); - } - - /** - * Searches this list for {@code object} and returns the index of the last - * occurrence that is before {@code to}. - * - * @return the index or -1 if the object was not found. - */ - public int lastIndexOf(E object, int to) { - Object[] snapshot = elements; - return lastIndexOf(object, snapshot, 0, to); - } - - public int lastIndexOf(Object object) { - Object[] snapshot = elements; - return lastIndexOf(object, snapshot, 0, snapshot.length); - } - - public boolean isEmpty() { - return elements.length == 0; - } - - /** - * Returns an {@link Iterator} that iterates over the elements of this list - * as they were at the time of this method call. Changes to the list made - * after this method call will not be reflected by the iterator, nor will - * they trigger a {@link ConcurrentModificationException}. - * - *

The returned iterator does not support {@link Iterator#remove()}. - */ - public Iterator iterator() { - Object[] snapshot = elements; - return new CowIterator(snapshot, 0, snapshot.length); - } - - /** - * Returns a {@link ListIterator} that iterates over the elements of this - * list as they were at the time of this method call. Changes to the list - * made after this method call will not be reflected by the iterator, nor - * will they trigger a {@link ConcurrentModificationException}. - * - *

The returned iterator does not support {@link ListIterator#add}, - * {@link ListIterator#set} or {@link Iterator#remove()}, - */ - public ListIterator listIterator(int index) { - Object[] snapshot = elements; - if (index < 0 || index > snapshot.length) { - throw new IndexOutOfBoundsException("index=" + index + ", length=" + snapshot.length); - } - CowIterator result = new CowIterator(snapshot, 0, snapshot.length); - result.index = index; - return result; - } - - /** - * Equivalent to {@code listIterator(0)}. - */ - public ListIterator listIterator() { - Object[] snapshot = elements; - return new CowIterator(snapshot, 0, snapshot.length); - } - - public List subList(int from, int to) { - Object[] snapshot = elements; - if (from < 0 || from > to || to > snapshot.length) { - throw new IndexOutOfBoundsException("from=" + from + ", to=" + to + - ", list size=" + snapshot.length); - } - return new CowSubList(snapshot, from, to); - } - - public Object[] toArray() { - return elements.clone(); - } - - @SuppressWarnings({"unchecked","SuspiciousSystemArraycopy"}) - public T[] toArray(T[] contents) { - Object[] snapshot = elements; - if (snapshot.length > contents.length) { - return (T[]) Arrays.copyOf(snapshot, snapshot.length, contents.getClass()); - } - System.arraycopy(snapshot, 0, contents, 0, snapshot.length); - if (snapshot.length < contents.length) { - contents[snapshot.length] = null; - } - return contents; - } - - @Override public boolean equals(Object other) { - if (other instanceof CopyOnWriteArrayList) { - return this == other - || Arrays.equals(elements, ((CopyOnWriteArrayList) other).elements); - } else if (other instanceof List) { - Object[] snapshot = elements; - Iterator i = ((List) other).iterator(); - for (Object o : snapshot) { - if (!i.hasNext() || !Objects.equal(o, i.next())) { - return false; - } - } - return !i.hasNext(); - } else { - return false; - } - } - - @Override public int hashCode() { - return Arrays.hashCode(elements); - } - - @Override public String toString() { - return Arrays.toString(elements); - } - - public synchronized boolean add(E e) { - Object[] newElements = new Object[elements.length + 1]; - System.arraycopy(elements, 0, newElements, 0, elements.length); - newElements[elements.length] = e; - elements = newElements; - return true; - } - - public synchronized void add(int index, E e) { - Object[] newElements = new Object[elements.length + 1]; - System.arraycopy(elements, 0, newElements, 0, index); - newElements[index] = e; - System.arraycopy(elements, index, newElements, index + 1, elements.length - index); - elements = newElements; - } - - public synchronized boolean addAll(Collection collection) { - return addAll(elements.length, collection); - } - - public synchronized boolean addAll(int index, Collection collection) { - Object[] toAdd = collection.toArray(); - Object[] newElements = new Object[elements.length + toAdd.length]; - System.arraycopy(elements, 0, newElements, 0, index); - System.arraycopy(toAdd, 0, newElements, index, toAdd.length); - System.arraycopy(elements, index, - newElements, index + toAdd.length, elements.length - index); - elements = newElements; - return toAdd.length > 0; - } - - /** - * Adds the elements of {@code collection} that are not already present in - * this list. If {@code collection} includes a repeated value, at most one - * occurrence of that value will be added to this list. Elements are added - * at the end of this list. - * - *

Callers of this method may prefer {@link CopyOnWriteArraySet}, whose - * API is more appropriate for set operations. - */ - public synchronized int addAllAbsent(Collection collection) { - Object[] toAdd = collection.toArray(); - Object[] newElements = new Object[elements.length + toAdd.length]; - System.arraycopy(elements, 0, newElements, 0, elements.length); - int addedCount = 0; - for (Object o : toAdd) { - if (indexOf(o, newElements, 0, elements.length + addedCount) == -1) { - newElements[elements.length + addedCount++] = o; - } - } - if (addedCount < toAdd.length) { - newElements = Arrays.copyOfRange( - newElements, 0, elements.length + addedCount); // trim to size - } - elements = newElements; - return addedCount; - } - - /** - * Adds {@code object} to the end of this list if it is not already present. - * - *

Callers of this method may prefer {@link CopyOnWriteArraySet}, whose - * API is more appropriate for set operations. - */ - public synchronized boolean addIfAbsent(E object) { - if (contains(object)) { - return false; - } - add(object); - return true; - } - - @Override public synchronized void clear() { - elements = EmptyArray.OBJECT; - } - - public synchronized E remove(int index) { - @SuppressWarnings("unchecked") - E removed = (E) elements[index]; - removeRange(index, index + 1); - return removed; - } - - public synchronized boolean remove(Object o) { - int index = indexOf(o); - if (index == -1) { - return false; - } - remove(index); - return true; - } - - public synchronized boolean removeAll(Collection collection) { - return removeOrRetain(collection, false, 0, elements.length) != 0; - } - - public synchronized boolean retainAll(Collection collection) { - return removeOrRetain(collection, true, 0, elements.length) != 0; - } - - @Override - public synchronized void replaceAll(UnaryOperator operator) { - replaceInRange(0, elements.length,operator); - } - - private void replaceInRange(int from, int to, UnaryOperator operator) { - java.util.Objects.requireNonNull(operator); - Object[] newElements = new Object[elements.length]; - System.arraycopy(elements, 0, newElements, 0, newElements.length); - for (int i = from; i < to; i++) { - @SuppressWarnings("unchecked") E e = (E) elements[i]; - newElements[i] = operator.apply(e); - } - elements = newElements; - } - - @Override - public synchronized void sort(Comparator c) { - sortInRange(0, elements.length, c); - } - - private synchronized void sortInRange(int from, int to, Comparator c) { - java.util.Objects.requireNonNull(c); - Object[] newElements = new Object[elements.length]; - System.arraycopy(elements, 0, newElements, 0, newElements.length); - Arrays.sort((E[])newElements, from, to, c); - elements = newElements; - } - - @Override - public void forEach(Consumer action) { - forInRange(0, elements.length, action); - } - - private void forInRange(int from, int to, Consumer action) { - java.util.Objects.requireNonNull(action); - Object[] newElements = new Object[elements.length]; - System.arraycopy(elements, 0, newElements, 0, newElements.length); - for (int i = from; i < to; i++) { - action.accept((E)newElements[i]); - } - } - - /** - * Removes or retains the elements in {@code collection}. Returns the number - * of elements removed. - */ - private int removeOrRetain(Collection collection, boolean retain, int from, int to) { - for (int i = from; i < to; i++) { - if (collection.contains(elements[i]) == retain) { - continue; - } - - /* - * We've encountered an element that must be removed! Create a new - * array and copy in the surviving elements one by one. - */ - Object[] newElements = new Object[elements.length - 1]; - System.arraycopy(elements, 0, newElements, 0, i); - int newSize = i; - for (int j = i + 1; j < to; j++) { - if (collection.contains(elements[j]) == retain) { - newElements[newSize++] = elements[j]; - } - } - - /* - * Copy the elements after 'to'. This is only useful for sub lists, - * where 'to' will be less than elements.length. - */ - System.arraycopy(elements, to, newElements, newSize, elements.length - to); - newSize += (elements.length - to); - - if (newSize < newElements.length) { - newElements = Arrays.copyOfRange(newElements, 0, newSize); // trim to size - } - int removed = elements.length - newElements.length; - elements = newElements; - return removed; - } - - // we made it all the way through the loop without making any changes - return 0; - } - - public synchronized E set(int index, E e) { - Object[] newElements = elements.clone(); - @SuppressWarnings("unchecked") - E result = (E) newElements[index]; - newElements[index] = e; - elements = newElements; - return result; - } - - private void removeRange(int from, int to) { - Object[] newElements = new Object[elements.length - (to - from)]; - System.arraycopy(elements, 0, newElements, 0, from); - System.arraycopy(elements, to, newElements, from, elements.length - to); - elements = newElements; - } - - static int lastIndexOf(Object o, Object[] data, int from, int to) { - if (o == null) { - for (int i = to - 1; i >= from; i--) { - if (data[i] == null) { - return i; - } - } - } else { - for (int i = to - 1; i >= from; i--) { - if (o.equals(data[i])) { - return i; - } - } - } - return -1; - } - - static int indexOf(Object o, Object[] data, int from, int to) { - if (o == null) { - for (int i = from; i < to; i++) { - if (data[i] == null) { - return i; - } - } - } else { - for (int i = from; i < to; i++) { - if (o.equals(data[i])) { - return i; - } - } - } - return -1; - } - - final Object[] getArray() { - // CopyOnWriteArraySet needs this. - return elements; - } - - /** - * The sub list is thread safe and supports non-blocking reads. Doing so is - * more difficult than in the full list, because each read needs to examine - * four fields worth of state: - * - the elements array of the full list - * - two integers for the bounds of this sub list - * - the expected elements array (to detect concurrent modification) - * - * This is accomplished by aggregating the sub list's three fields into a - * single snapshot object representing the current slice. This permits reads - * to be internally consistent without synchronization. This takes advantage - * of Java's concurrency semantics for final fields. - */ - class CowSubList extends AbstractList { - - /* - * An immutable snapshot of a sub list's state. By gathering all three - * of the sub list's fields in an immutable object, - */ - private volatile Slice slice; - - public CowSubList(Object[] expectedElements, int from, int to) { - this.slice = new Slice(expectedElements, from, to); - } - - @Override public int size() { - Slice slice = this.slice; - return slice.to - slice.from; - } - - @Override public boolean isEmpty() { - Slice slice = this.slice; - return slice.from == slice.to; - } - - @SuppressWarnings("unchecked") - @Override public E get(int index) { - Slice slice = this.slice; - Object[] snapshot = elements; - slice.checkElementIndex(index); - slice.checkConcurrentModification(snapshot); - return (E) snapshot[index + slice.from]; - } - - @Override public Iterator iterator() { - return listIterator(0); - } - - @Override public ListIterator listIterator() { - return listIterator(0); - } - - @Override public ListIterator listIterator(int index) { - Slice slice = this.slice; - Object[] snapshot = elements; - slice.checkPositionIndex(index); - slice.checkConcurrentModification(snapshot); - CowIterator result = new CowIterator(snapshot, slice.from, slice.to); - result.index = slice.from + index; - return result; - } - - @Override public int indexOf(Object object) { - Slice slice = this.slice; - Object[] snapshot = elements; - slice.checkConcurrentModification(snapshot); - int result = CopyOnWriteArrayList.indexOf(object, snapshot, slice.from, slice.to); - return (result != -1) ? (result - slice.from) : -1; - } - - @Override public int lastIndexOf(Object object) { - Slice slice = this.slice; - Object[] snapshot = elements; - slice.checkConcurrentModification(snapshot); - int result = CopyOnWriteArrayList.lastIndexOf(object, snapshot, slice.from, slice.to); - return (result != -1) ? (result - slice.from) : -1; - } - - @Override public boolean contains(Object object) { - return indexOf(object) != -1; - } - - @Override public boolean containsAll(Collection collection) { - Slice slice = this.slice; - Object[] snapshot = elements; - slice.checkConcurrentModification(snapshot); - return CopyOnWriteArrayList.containsAll(collection, snapshot, slice.from, slice.to); - } - - @Override public List subList(int from, int to) { - Slice slice = this.slice; - if (from < 0 || from > to || to > size()) { - throw new IndexOutOfBoundsException("from=" + from + ", to=" + to + - ", list size=" + size()); - } - return new CowSubList(slice.expectedElements, slice.from + from, slice.from + to); - } - - @Override public E remove(int index) { - synchronized (CopyOnWriteArrayList.this) { - slice.checkElementIndex(index); - slice.checkConcurrentModification(elements); - E removed = CopyOnWriteArrayList.this.remove(slice.from + index); - slice = new Slice(elements, slice.from, slice.to - 1); - return removed; - } - } - - @Override public void clear() { - synchronized (CopyOnWriteArrayList.this) { - slice.checkConcurrentModification(elements); - CopyOnWriteArrayList.this.removeRange(slice.from, slice.to); - slice = new Slice(elements, slice.from, slice.from); - } - } - - @Override public void add(int index, E object) { - synchronized (CopyOnWriteArrayList.this) { - slice.checkPositionIndex(index); - slice.checkConcurrentModification(elements); - CopyOnWriteArrayList.this.add(index + slice.from, object); - slice = new Slice(elements, slice.from, slice.to + 1); - } - } - - @Override public boolean add(E object) { - synchronized (CopyOnWriteArrayList.this) { - add(slice.to - slice.from, object); - return true; - } - } - - @Override public boolean addAll(int index, Collection collection) { - synchronized (CopyOnWriteArrayList.this) { - slice.checkPositionIndex(index); - slice.checkConcurrentModification(elements); - int oldSize = elements.length; - boolean result = CopyOnWriteArrayList.this.addAll(index + slice.from, collection); - slice = new Slice(elements, slice.from, slice.to + (elements.length - oldSize)); - return result; - } - } - - @Override public boolean addAll(Collection collection) { - synchronized (CopyOnWriteArrayList.this) { - return addAll(size(), collection); - } - } - - @Override public E set(int index, E object) { - synchronized (CopyOnWriteArrayList.this) { - slice.checkElementIndex(index); - slice.checkConcurrentModification(elements); - E result = CopyOnWriteArrayList.this.set(index + slice.from, object); - slice = new Slice(elements, slice.from, slice.to); - return result; - } - } - - @Override public boolean remove(Object object) { - synchronized (CopyOnWriteArrayList.this) { - int index = indexOf(object); - if (index == -1) { - return false; - } - remove(index); - return true; - } - } - - @Override public boolean removeAll(Collection collection) { - synchronized (CopyOnWriteArrayList.this) { - slice.checkConcurrentModification(elements); - int removed = removeOrRetain(collection, false, slice.from, slice.to); - slice = new Slice(elements, slice.from, slice.to - removed); - return removed != 0; - } - } - - @Override public boolean retainAll(Collection collection) { - synchronized (CopyOnWriteArrayList.this) { - slice.checkConcurrentModification(elements); - int removed = removeOrRetain(collection, true, slice.from, slice.to); - slice = new Slice(elements, slice.from, slice.to - removed); - return removed != 0; - } - } - - @Override - public void forEach(Consumer action) { - CopyOnWriteArrayList.this.forInRange(slice.from, slice.to, action); - } - - @Override - public void replaceAll(UnaryOperator operator) { - synchronized (CopyOnWriteArrayList.this) { - slice.checkConcurrentModification(elements); - CopyOnWriteArrayList.this.replaceInRange(slice.from, slice.to, operator); - slice = new Slice(elements, slice.from, slice.to); - } - } - - @Override - public synchronized void sort(Comparator c) { - synchronized (CopyOnWriteArrayList.this) { - slice.checkConcurrentModification(elements); - CopyOnWriteArrayList.this.sortInRange(slice.from, slice.to, c); - slice = new Slice(elements, slice.from, slice.to); - } - } - } - - static class Slice { - private final Object[] expectedElements; - private final int from; - private final int to; - - Slice(Object[] expectedElements, int from, int to) { - this.expectedElements = expectedElements; - this.from = from; - this.to = to; - } - - /** - * Throws if {@code index} doesn't identify an element in the array. - */ - void checkElementIndex(int index) { - if (index < 0 || index >= to - from) { - throw new IndexOutOfBoundsException("index=" + index + ", size=" + (to - from)); - } - } - - /** - * Throws if {@code index} doesn't identify an insertion point in the - * array. Unlike element index, it's okay to add or iterate at size(). - */ - void checkPositionIndex(int index) { - if (index < 0 || index > to - from) { - throw new IndexOutOfBoundsException("index=" + index + ", size=" + (to - from)); - } - } - - void checkConcurrentModification(Object[] snapshot) { - if (expectedElements != snapshot) { - throw new ConcurrentModificationException(); - } - } - } - - /** - * Iterates an immutable snapshot of the list. - */ - static class CowIterator implements ListIterator { - private final Object[] snapshot; - private final int from; - private final int to; - private int index = 0; - - CowIterator(Object[] snapshot, int from, int to) { - this.snapshot = snapshot; - this.from = from; - this.to = to; - this.index = from; - } - - public void add(E object) { - throw new UnsupportedOperationException(); - } - - public boolean hasNext() { - return index < to; - } - - public boolean hasPrevious() { - return index > from; - } - - @SuppressWarnings("unchecked") - public E next() { - if (index < to) { - return (E) snapshot[index++]; - } else { - throw new NoSuchElementException(); - } - } - - public int nextIndex() { - return index; - } - - @SuppressWarnings("unchecked") - public E previous() { - if (index > from) { - return (E) snapshot[--index]; - } else { - throw new NoSuchElementException(); - } - } - - public int previousIndex() { - return index - 1; - } - - public void remove() { - throw new UnsupportedOperationException(); - } - - public void set(E object) { - throw new UnsupportedOperationException(); - } - - @Override - public void forEachRemaining(Consumer action) { - java.util.Objects.requireNonNull(action); - Object[] elements = snapshot; - for (int i = index; i < to; i++) { - @SuppressWarnings("unchecked") E e = (E) elements[i]; - action.accept(e); - } - index = to; - } - } - - private void writeObject(ObjectOutputStream out) throws IOException { - Object[] snapshot = elements; - out.defaultWriteObject(); - out.writeInt(snapshot.length); - for (Object o : snapshot) { - out.writeObject(o); - } - } - - private synchronized void readObject(ObjectInputStream in) - throws IOException, ClassNotFoundException { - in.defaultReadObject(); - Object[] snapshot = new Object[in.readInt()]; - for (int i = 0; i < snapshot.length; i++) { - snapshot[i] = in.readObject(); - } - elements = snapshot; - } -} diff --git a/luni/src/main/java/java/util/concurrent/Delayed.java b/luni/src/main/java/java/util/concurrent/Delayed.java deleted file mode 100644 index 6a9527d73..000000000 --- a/luni/src/main/java/java/util/concurrent/Delayed.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * A mix-in style interface for marking objects that should be - * acted upon after a given delay. - * - *

An implementation of this interface must define a - * {@code compareTo} method that provides an ordering consistent with - * its {@code getDelay} method. - * - * @since 1.5 - * @author Doug Lea - */ -public interface Delayed extends Comparable { - - /** - * Returns the remaining delay associated with this object, in the - * given time unit. - * - * @param unit the time unit - * @return the remaining delay; zero or negative values indicate - * that the delay has already elapsed - */ - long getDelay(TimeUnit unit); -} diff --git a/luni/src/main/java/java/util/concurrent/ExecutionException.java b/luni/src/main/java/java/util/concurrent/ExecutionException.java deleted file mode 100644 index dbfbe6506..000000000 --- a/luni/src/main/java/java/util/concurrent/ExecutionException.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * Exception thrown when attempting to retrieve the result of a task - * that aborted by throwing an exception. This exception can be - * inspected using the {@link #getCause()} method. - * - * @see Future - * @since 1.5 - * @author Doug Lea - */ -public class ExecutionException extends Exception { - private static final long serialVersionUID = 7830266012832686185L; - - /** - * Constructs an {@code ExecutionException} with no detail message. - * The cause is not initialized, and may subsequently be - * initialized by a call to {@link #initCause(Throwable) initCause}. - */ - protected ExecutionException() { } - - /** - * Constructs an {@code ExecutionException} with the specified detail - * message. The cause is not initialized, and may subsequently be - * initialized by a call to {@link #initCause(Throwable) initCause}. - * - * @param message the detail message - */ - protected ExecutionException(String message) { - super(message); - } - - /** - * Constructs an {@code ExecutionException} with the specified detail - * message and cause. - * - * @param message the detail message - * @param cause the cause (which is saved for later retrieval by the - * {@link #getCause()} method) - */ - public ExecutionException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Constructs an {@code ExecutionException} with the specified cause. - * The detail message is set to {@code (cause == null ? null : - * cause.toString())} (which typically contains the class and - * detail message of {@code cause}). - * - * @param cause the cause (which is saved for later retrieval by the - * {@link #getCause()} method) - */ - public ExecutionException(Throwable cause) { - super(cause); - } -} diff --git a/luni/src/main/java/java/util/concurrent/Executor.java b/luni/src/main/java/java/util/concurrent/Executor.java deleted file mode 100644 index 9dd3efb62..000000000 --- a/luni/src/main/java/java/util/concurrent/Executor.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * An object that executes submitted {@link Runnable} tasks. This - * interface provides a way of decoupling task submission from the - * mechanics of how each task will be run, including details of thread - * use, scheduling, etc. An {@code Executor} is normally used - * instead of explicitly creating threads. For example, rather than - * invoking {@code new Thread(new RunnableTask()).start()} for each - * of a set of tasks, you might use: - * - *

 {@code
- * Executor executor = anExecutor();
- * executor.execute(new RunnableTask1());
- * executor.execute(new RunnableTask2());
- * ...}
- * - * However, the {@code Executor} interface does not strictly require - * that execution be asynchronous. In the simplest case, an executor - * can run the submitted task immediately in the caller's thread: - * - *
 {@code
- * class DirectExecutor implements Executor {
- *   public void execute(Runnable r) {
- *     r.run();
- *   }
- * }}
- * - * More typically, tasks are executed in some thread other than the - * caller's thread. The executor below spawns a new thread for each - * task. - * - *
 {@code
- * class ThreadPerTaskExecutor implements Executor {
- *   public void execute(Runnable r) {
- *     new Thread(r).start();
- *   }
- * }}
- * - * Many {@code Executor} implementations impose some sort of - * limitation on how and when tasks are scheduled. The executor below - * serializes the submission of tasks to a second executor, - * illustrating a composite executor. - * - *
 {@code
- * class SerialExecutor implements Executor {
- *   final Queue tasks = new ArrayDeque<>();
- *   final Executor executor;
- *   Runnable active;
- *
- *   SerialExecutor(Executor executor) {
- *     this.executor = executor;
- *   }
- *
- *   public synchronized void execute(final Runnable r) {
- *     tasks.add(new Runnable() {
- *       public void run() {
- *         try {
- *           r.run();
- *         } finally {
- *           scheduleNext();
- *         }
- *       }
- *     });
- *     if (active == null) {
- *       scheduleNext();
- *     }
- *   }
- *
- *   protected synchronized void scheduleNext() {
- *     if ((active = tasks.poll()) != null) {
- *       executor.execute(active);
- *     }
- *   }
- * }}
- * - * The {@code Executor} implementations provided in this package - * implement {@link ExecutorService}, which is a more extensive - * interface. The {@link ThreadPoolExecutor} class provides an - * extensible thread pool implementation. The {@link Executors} class - * provides convenient factory methods for these Executors. - * - *

Memory consistency effects: Actions in a thread prior to - * submitting a {@code Runnable} object to an {@code Executor} - * happen-before - * its execution begins, perhaps in another thread. - * - * @since 1.5 - * @author Doug Lea - */ -public interface Executor { - - /** - * Executes the given command at some time in the future. The command - * may execute in a new thread, in a pooled thread, or in the calling - * thread, at the discretion of the {@code Executor} implementation. - * - * @param command the runnable task - * @throws RejectedExecutionException if this task cannot be - * accepted for execution - * @throws NullPointerException if command is null - */ - void execute(Runnable command); -} diff --git a/luni/src/main/java/java/util/concurrent/Helpers.java b/luni/src/main/java/java/util/concurrent/Helpers.java deleted file mode 100644 index 9051e2f66..000000000 --- a/luni/src/main/java/java/util/concurrent/Helpers.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Written by Martin Buchholz with assistance from members of JCP - * JSR-166 Expert Group and released to the public domain, as - * explained at http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -import java.util.Collection; - -/** Shared implementation code for java.util.concurrent. */ -class Helpers { - private Helpers() {} // non-instantiable - - /** - * An implementation of Collection.toString() suitable for classes - * with locks. Instead of holding a lock for the entire duration of - * toString(), or acquiring a lock for each call to Iterator.next(), - * we hold the lock only during the call to toArray() (less - * disruptive to other threads accessing the collection) and follows - * the maxim "Never call foreign code while holding a lock". - */ - static String collectionToString(Collection c) { - final Object[] a = c.toArray(); - final int size = a.length; - if (size == 0) - return "[]"; - int charLength = 0; - - // Replace every array element with its string representation - for (int i = 0; i < size; i++) { - Object e = a[i]; - // Extreme compatibility with AbstractCollection.toString() - String s = (e == c) ? "(this Collection)" : objectToString(e); - a[i] = s; - charLength += s.length(); - } - - return toString(a, size, charLength); - } - - /** - * Like Arrays.toString(), but caller guarantees that size > 0, - * each element with index 0 <= i < size is a non-null String, - * and charLength is the sum of the lengths of the input Strings. - */ - static String toString(Object[] a, int size, int charLength) { - // assert a != null; - // assert size > 0; - - // Copy each string into a perfectly sized char[] - // Length of [ , , , ] == 2 * size - final char[] chars = new char[charLength + 2 * size]; - chars[0] = '['; - int j = 1; - for (int i = 0; i < size; i++) { - if (i > 0) { - chars[j++] = ','; - chars[j++] = ' '; - } - String s = (String) a[i]; - int len = s.length(); - s.getChars(0, len, chars, j); - j += len; - } - chars[j] = ']'; - // assert j == chars.length - 1; - return new String(chars); - } - - /** Optimized form of: key + "=" + val */ - static String mapEntryToString(Object key, Object val) { - final String k, v; - final int klen, vlen; - final char[] chars = - new char[(klen = (k = objectToString(key)).length()) + - (vlen = (v = objectToString(val)).length()) + 1]; - k.getChars(0, klen, chars, 0); - chars[klen] = '='; - v.getChars(0, vlen, chars, klen + 1); - return new String(chars); - } - - private static String objectToString(Object x) { - // Extreme compatibility with StringBuilder.append(null) - String s; - return (x == null || (s = x.toString()) == null) ? "null" : s; - } -} diff --git a/luni/src/main/java/java/util/concurrent/RecursiveTask.java b/luni/src/main/java/java/util/concurrent/RecursiveTask.java deleted file mode 100644 index 5cba1dac3..000000000 --- a/luni/src/main/java/java/util/concurrent/RecursiveTask.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * A recursive result-bearing {@link ForkJoinTask}. - * - *

For a classic example, here is a task computing Fibonacci numbers: - * - *

 {@code
- * class Fibonacci extends RecursiveTask {
- *   final int n;
- *   Fibonacci(int n) { this.n = n; }
- *   protected Integer compute() {
- *     if (n <= 1)
- *       return n;
- *     Fibonacci f1 = new Fibonacci(n - 1);
- *     f1.fork();
- *     Fibonacci f2 = new Fibonacci(n - 2);
- *     return f2.compute() + f1.join();
- *   }
- * }}
- * - * However, besides being a dumb way to compute Fibonacci functions - * (there is a simple fast linear algorithm that you'd use in - * practice), this is likely to perform poorly because the smallest - * subtasks are too small to be worthwhile splitting up. Instead, as - * is the case for nearly all fork/join applications, you'd pick some - * minimum granularity size (for example 10 here) for which you always - * sequentially solve rather than subdividing. - * - * @since 1.7 - * @author Doug Lea - */ -public abstract class RecursiveTask extends ForkJoinTask { - private static final long serialVersionUID = 5232453952276485270L; - - /** - * The result of the computation. - */ - V result; - - /** - * The main computation performed by this task. - * @return the result of the computation - */ - protected abstract V compute(); - - public final V getRawResult() { - return result; - } - - protected final void setRawResult(V value) { - result = value; - } - - /** - * Implements execution conventions for RecursiveTask. - */ - protected final boolean exec() { - result = compute(); - return true; - } - -} diff --git a/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java b/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java deleted file mode 100644 index c61365fae..000000000 --- a/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * Exception thrown by an {@link Executor} when a task cannot be - * accepted for execution. - * - * @since 1.5 - * @author Doug Lea - */ -public class RejectedExecutionException extends RuntimeException { - private static final long serialVersionUID = -375805702767069545L; - - /** - * Constructs a {@code RejectedExecutionException} with no detail message. - * The cause is not initialized, and may subsequently be - * initialized by a call to {@link #initCause(Throwable) initCause}. - */ - public RejectedExecutionException() { } - - /** - * Constructs a {@code RejectedExecutionException} with the - * specified detail message. The cause is not initialized, and may - * subsequently be initialized by a call to {@link - * #initCause(Throwable) initCause}. - * - * @param message the detail message - */ - public RejectedExecutionException(String message) { - super(message); - } - - /** - * Constructs a {@code RejectedExecutionException} with the - * specified detail message and cause. - * - * @param message the detail message - * @param cause the cause (which is saved for later retrieval by the - * {@link #getCause()} method) - */ - public RejectedExecutionException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Constructs a {@code RejectedExecutionException} with the - * specified cause. The detail message is set to {@code (cause == - * null ? null : cause.toString())} (which typically contains - * the class and detail message of {@code cause}). - * - * @param cause the cause (which is saved for later retrieval by the - * {@link #getCause()} method) - */ - public RejectedExecutionException(Throwable cause) { - super(cause); - } -} diff --git a/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java b/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java deleted file mode 100644 index 8c000ea8d..000000000 --- a/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * A handler for tasks that cannot be executed by a {@link ThreadPoolExecutor}. - * - * @since 1.5 - * @author Doug Lea - */ -public interface RejectedExecutionHandler { - - /** - * Method that may be invoked by a {@link ThreadPoolExecutor} when - * {@link ThreadPoolExecutor#execute execute} cannot accept a - * task. This may occur when no more threads or queue slots are - * available because their bounds would be exceeded, or upon - * shutdown of the Executor. - * - *

In the absence of other alternatives, the method may throw - * an unchecked {@link RejectedExecutionException}, which will be - * propagated to the caller of {@code execute}. - * - * @param r the runnable task requested to be executed - * @param executor the executor attempting to execute this task - * @throws RejectedExecutionException if there is no remedy - */ - void rejectedExecution(Runnable r, ThreadPoolExecutor executor); -} diff --git a/luni/src/main/java/java/util/concurrent/RunnableFuture.java b/luni/src/main/java/java/util/concurrent/RunnableFuture.java deleted file mode 100644 index ccd28e35a..000000000 --- a/luni/src/main/java/java/util/concurrent/RunnableFuture.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * A {@link Future} that is {@link Runnable}. Successful execution of - * the {@code run} method causes completion of the {@code Future} - * and allows access to its results. - * @see FutureTask - * @see Executor - * @since 1.6 - * @author Doug Lea - * @param The result type returned by this Future's {@code get} method - */ -public interface RunnableFuture extends Runnable, Future { - /** - * Sets this Future to the result of its computation - * unless it has been cancelled. - */ - void run(); -} diff --git a/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java b/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java deleted file mode 100644 index 604f180bc..000000000 --- a/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * A {@link ScheduledFuture} that is {@link Runnable}. Successful - * execution of the {@code run} method causes completion of the - * {@code Future} and allows access to its results. - * @see FutureTask - * @see Executor - * @since 1.6 - * @author Doug Lea - * @param The result type returned by this Future's {@code get} method - */ -public interface RunnableScheduledFuture extends RunnableFuture, ScheduledFuture { - - /** - * Returns {@code true} if this task is periodic. A periodic task may - * re-run according to some schedule. A non-periodic task can be - * run only once. - * - * @return {@code true} if this task is periodic - */ - boolean isPeriodic(); -} diff --git a/luni/src/main/java/java/util/concurrent/ScheduledFuture.java b/luni/src/main/java/java/util/concurrent/ScheduledFuture.java deleted file mode 100644 index 3745cb0f6..000000000 --- a/luni/src/main/java/java/util/concurrent/ScheduledFuture.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * A delayed result-bearing action that can be cancelled. - * Usually a scheduled future is the result of scheduling - * a task with a {@link ScheduledExecutorService}. - * - * @since 1.5 - * @author Doug Lea - * @param The result type returned by this Future - */ -public interface ScheduledFuture extends Delayed, Future { -} diff --git a/luni/src/main/java/java/util/concurrent/ThreadFactory.java b/luni/src/main/java/java/util/concurrent/ThreadFactory.java deleted file mode 100644 index fdedea34b..000000000 --- a/luni/src/main/java/java/util/concurrent/ThreadFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * An object that creates new threads on demand. Using thread factories - * removes hardwiring of calls to {@link Thread#Thread(Runnable) new Thread}, - * enabling applications to use special thread subclasses, priorities, etc. - * - *

- * The simplest implementation of this interface is just: - *

 {@code
- * class SimpleThreadFactory implements ThreadFactory {
- *   public Thread newThread(Runnable r) {
- *     return new Thread(r);
- *   }
- * }}
- * - * The {@link Executors#defaultThreadFactory} method provides a more - * useful simple implementation, that sets the created thread context - * to known values before returning it. - * @since 1.5 - * @author Doug Lea - */ -public interface ThreadFactory { - - /** - * Constructs a new {@code Thread}. Implementations may also initialize - * priority, name, daemon status, {@code ThreadGroup}, etc. - * - * @param r a runnable to be executed by new thread instance - * @return constructed thread, or {@code null} if the request to - * create a thread is rejected - */ - Thread newThread(Runnable r); -} diff --git a/luni/src/main/java/java/util/concurrent/TimeoutException.java b/luni/src/main/java/java/util/concurrent/TimeoutException.java deleted file mode 100644 index 1d7e634a3..000000000 --- a/luni/src/main/java/java/util/concurrent/TimeoutException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent; - -/** - * Exception thrown when a blocking operation times out. Blocking - * operations for which a timeout is specified need a means to - * indicate that the timeout has occurred. For many such operations it - * is possible to return a value that indicates timeout; when that is - * not possible or desirable then {@code TimeoutException} should be - * declared and thrown. - * - * @since 1.5 - * @author Doug Lea - */ -public class TimeoutException extends Exception { - private static final long serialVersionUID = 1900926677490660714L; - - /** - * Constructs a {@code TimeoutException} with no specified detail - * message. - */ - public TimeoutException() {} - - /** - * Constructs a {@code TimeoutException} with the specified detail - * message. - * - * @param message the detail message - */ - public TimeoutException(String message) { - super(message); - } -} diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java deleted file mode 100644 index 01e4b072d..000000000 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent.atomic; - -/** - * A {@code boolean} value that may be updated atomically. See the - * {@link java.util.concurrent.atomic} package specification for - * description of the properties of atomic variables. An - * {@code AtomicBoolean} is used in applications such as atomically - * updated flags, and cannot be used as a replacement for a - * {@link java.lang.Boolean}. - * - * @since 1.5 - * @author Doug Lea - */ -public class AtomicBoolean implements java.io.Serializable { - private static final long serialVersionUID = 4654671469794556979L; - - private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); - private static final long VALUE; - - static { - try { - VALUE = U.objectFieldOffset - (AtomicBoolean.class.getDeclaredField("value")); - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - } - - private volatile int value; - - /** - * Creates a new {@code AtomicBoolean} with the given initial value. - * - * @param initialValue the initial value - */ - public AtomicBoolean(boolean initialValue) { - value = initialValue ? 1 : 0; - } - - /** - * Creates a new {@code AtomicBoolean} with initial value {@code false}. - */ - public AtomicBoolean() { - } - - /** - * Returns the current value. - * - * @return the current value - */ - public final boolean get() { - return value != 0; - } - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful. False return indicates that - * the actual value was not equal to the expected value. - */ - public final boolean compareAndSet(boolean expect, boolean update) { - return U.compareAndSwapInt(this, VALUE, - (expect ? 1 : 0), - (update ? 1 : 0)); - } - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful - */ - public boolean weakCompareAndSet(boolean expect, boolean update) { - return U.compareAndSwapInt(this, VALUE, - (expect ? 1 : 0), - (update ? 1 : 0)); - } - - /** - * Unconditionally sets to the given value. - * - * @param newValue the new value - */ - public final void set(boolean newValue) { - value = newValue ? 1 : 0; - } - - /** - * Eventually sets to the given value. - * - * @param newValue the new value - * @since 1.6 - */ - public final void lazySet(boolean newValue) { - U.putOrderedInt(this, VALUE, (newValue ? 1 : 0)); - } - - /** - * Atomically sets to the given value and returns the previous value. - * - * @param newValue the new value - * @return the previous value - */ - public final boolean getAndSet(boolean newValue) { - boolean prev; - do { - prev = get(); - } while (!compareAndSet(prev, newValue)); - return prev; - } - - /** - * Returns the String representation of the current value. - * @return the String representation of the current value - */ - public String toString() { - return Boolean.toString(get()); - } - -} diff --git a/luni/src/main/java/java/util/concurrent/atomic/package-info.java b/luni/src/main/java/java/util/concurrent/atomic/package-info.java deleted file mode 100644 index b19dd49a0..000000000 --- a/luni/src/main/java/java/util/concurrent/atomic/package-info.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -/** - * A small toolkit of classes that support lock-free thread-safe - * programming on single variables. In essence, the classes in this - * package extend the notion of {@code volatile} values, fields, and - * array elements to those that also provide an atomic conditional update - * operation of the form: - * - *

 {@code boolean compareAndSet(expectedValue, updateValue);}
- * - *

This method (which varies in argument types across different - * classes) atomically sets a variable to the {@code updateValue} if it - * currently holds the {@code expectedValue}, reporting {@code true} on - * success. The classes in this package also contain methods to get and - * unconditionally set values, as well as a weaker conditional atomic - * update operation {@code weakCompareAndSet} described below. - * - *

The specifications of these methods enable implementations to - * employ efficient machine-level atomic instructions that are available - * on contemporary processors. However on some platforms, support may - * entail some form of internal locking. Thus the methods are not - * strictly guaranteed to be non-blocking -- - * a thread may block transiently before performing the operation. - * - *

Instances of classes - * {@link java.util.concurrent.atomic.AtomicBoolean}, - * {@link java.util.concurrent.atomic.AtomicInteger}, - * {@link java.util.concurrent.atomic.AtomicLong}, and - * {@link java.util.concurrent.atomic.AtomicReference} - * each provide access and updates to a single variable of the - * corresponding type. Each class also provides appropriate utility - * methods for that type. For example, classes {@code AtomicLong} and - * {@code AtomicInteger} provide atomic increment methods. One - * application is to generate sequence numbers, as in: - * - *

 {@code
- * class Sequencer {
- *   private final AtomicLong sequenceNumber
- *     = new AtomicLong(0);
- *   public long next() {
- *     return sequenceNumber.getAndIncrement();
- *   }
- * }}
- * - *

It is straightforward to define new utility functions that, like - * {@code getAndIncrement}, apply a function to a value atomically. - * For example, given some transformation - *

 {@code long transform(long input)}
- * - * write your utility method as follows: - *
 {@code
- * long getAndTransform(AtomicLong var) {
- *   long prev, next;
- *   do {
- *     prev = var.get();
- *     next = transform(prev);
- *   } while (!var.compareAndSet(prev, next));
- *   return prev; // return next; for transformAndGet
- * }}
- * - *

The memory effects for accesses and updates of atomics generally - * follow the rules for volatiles, as stated in - * - * Chapter 17 of - * The Java™ Language Specification: - * - *

    - * - *
  • {@code get} has the memory effects of reading a - * {@code volatile} variable. - * - *
  • {@code set} has the memory effects of writing (assigning) a - * {@code volatile} variable. - * - *
  • {@code lazySet} has the memory effects of writing (assigning) - * a {@code volatile} variable except that it permits reorderings with - * subsequent (but not previous) memory actions that do not themselves - * impose reordering constraints with ordinary non-{@code volatile} - * writes. Among other usage contexts, {@code lazySet} may apply when - * nulling out, for the sake of garbage collection, a reference that is - * never accessed again. - * - *
  • {@code weakCompareAndSet} atomically reads and conditionally - * writes a variable but does not - * create any happens-before orderings, so provides no guarantees - * with respect to previous or subsequent reads and writes of any - * variables other than the target of the {@code weakCompareAndSet}. - * - *
  • {@code compareAndSet} - * and all other read-and-update operations such as {@code getAndIncrement} - * have the memory effects of both reading and - * writing {@code volatile} variables. - *
- * - *

In addition to classes representing single values, this package - * contains Updater classes that can be used to obtain - * {@code compareAndSet} operations on any selected {@code volatile} - * field of any selected class. - * - * {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater}, - * {@link java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and - * {@link java.util.concurrent.atomic.AtomicLongFieldUpdater} are - * reflection-based utilities that provide access to the associated - * field types. These are mainly of use in atomic data structures in - * which several {@code volatile} fields of the same node (for - * example, the links of a tree node) are independently subject to - * atomic updates. These classes enable greater flexibility in how - * and when to use atomic updates, at the expense of more awkward - * reflection-based setup, less convenient usage, and weaker - * guarantees. - * - *

The - * {@link java.util.concurrent.atomic.AtomicIntegerArray}, - * {@link java.util.concurrent.atomic.AtomicLongArray}, and - * {@link java.util.concurrent.atomic.AtomicReferenceArray} classes - * further extend atomic operation support to arrays of these types. - * These classes are also notable in providing {@code volatile} access - * semantics for their array elements, which is not supported for - * ordinary arrays. - * - *

The atomic classes also support method - * {@code weakCompareAndSet}, which has limited applicability. On some - * platforms, the weak version may be more efficient than {@code - * compareAndSet} in the normal case, but differs in that any given - * invocation of the {@code weakCompareAndSet} method may return {@code - * false} spuriously (that is, for no apparent reason). A - * {@code false} return means only that the operation may be retried if - * desired, relying on the guarantee that repeated invocation when the - * variable holds {@code expectedValue} and no other thread is also - * attempting to set the variable will eventually succeed. (Such - * spurious failures may for example be due to memory contention effects - * that are unrelated to whether the expected and current values are - * equal.) Additionally {@code weakCompareAndSet} does not provide - * ordering guarantees that are usually needed for synchronization - * control. However, the method may be useful for updating counters and - * statistics when such updates are unrelated to the other - * happens-before orderings of a program. When a thread sees an update - * to an atomic variable caused by a {@code weakCompareAndSet}, it does - * not necessarily see updates to any other variables that - * occurred before the {@code weakCompareAndSet}. This may be - * acceptable when, for example, updating performance statistics, but - * rarely otherwise. - * - *

The {@link java.util.concurrent.atomic.AtomicMarkableReference} - * class associates a single boolean with a reference. For example, this - * bit might be used inside a data structure to mean that the object - * being referenced has logically been deleted. - * - * The {@link java.util.concurrent.atomic.AtomicStampedReference} - * class associates an integer value with a reference. This may be - * used for example, to represent version numbers corresponding to - * series of updates. - * - *

Atomic classes are designed primarily as building blocks for - * implementing non-blocking data structures and related infrastructure - * classes. The {@code compareAndSet} method is not a general - * replacement for locking. It applies only when critical updates for an - * object are confined to a single variable. - * - *

Atomic classes are not general purpose replacements for - * {@code java.lang.Integer} and related classes. They do not - * define methods such as {@code equals}, {@code hashCode} and - * {@code compareTo}. (Because atomic variables are expected to be - * mutated, they are poor choices for hash table keys.) Additionally, - * classes are provided only for those types that are commonly useful in - * intended applications. For example, there is no atomic class for - * representing {@code byte}. In those infrequent cases where you would - * like to do so, you can use an {@code AtomicInteger} to hold - * {@code byte} values, and cast appropriately. - * - * You can also hold floats using - * {@link java.lang.Float#floatToRawIntBits} and - * {@link java.lang.Float#intBitsToFloat} conversions, and doubles using - * {@link java.lang.Double#doubleToRawLongBits} and - * {@link java.lang.Double#longBitsToDouble} conversions. - * - * @since 1.5 - */ -package java.util.concurrent.atomic; diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java deleted file mode 100644 index 66a2f8e57..000000000 --- a/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent.locks; - -/** - * A synchronizer that may be exclusively owned by a thread. This - * class provides a basis for creating locks and related synchronizers - * that may entail a notion of ownership. The - * {@code AbstractOwnableSynchronizer} class itself does not manage or - * use this information. However, subclasses and tools may use - * appropriately maintained values to help control and monitor access - * and provide diagnostics. - * - * @since 1.6 - * @author Doug Lea - */ -public abstract class AbstractOwnableSynchronizer - implements java.io.Serializable { - - /** Use serial ID even though all fields transient. */ - private static final long serialVersionUID = 3737899427754241961L; - - /** - * Empty constructor for use by subclasses. - */ - protected AbstractOwnableSynchronizer() { } - - /** - * The current owner of exclusive mode synchronization. - */ - private transient Thread exclusiveOwnerThread; - - /** - * Sets the thread that currently owns exclusive access. - * A {@code null} argument indicates that no thread owns access. - * This method does not otherwise impose any synchronization or - * {@code volatile} field accesses. - * @param thread the owner thread - */ - protected final void setExclusiveOwnerThread(Thread thread) { - exclusiveOwnerThread = thread; - } - - /** - * Returns the thread last set by {@code setExclusiveOwnerThread}, - * or {@code null} if never set. This method does not otherwise - * impose any synchronization or {@code volatile} field accesses. - * @return the owner thread - */ - protected final Thread getExclusiveOwnerThread() { - return exclusiveOwnerThread; - } -} diff --git a/luni/src/main/java/java/util/concurrent/locks/package-info.java b/luni/src/main/java/java/util/concurrent/locks/package-info.java deleted file mode 100644 index 433f86908..000000000 --- a/luni/src/main/java/java/util/concurrent/locks/package-info.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -/** - * Interfaces and classes providing a framework for locking and waiting - * for conditions that is distinct from built-in synchronization and - * monitors. The framework permits much greater flexibility in the use of - * locks and conditions, at the expense of more awkward syntax. - * - *

The {@link java.util.concurrent.locks.Lock} interface supports - * locking disciplines that differ in semantics (reentrant, fair, etc), - * and that can be used in non-block-structured contexts including - * hand-over-hand and lock reordering algorithms. The main implementation - * is {@link java.util.concurrent.locks.ReentrantLock}. - * - *

The {@link java.util.concurrent.locks.ReadWriteLock} interface - * similarly defines locks that may be shared among readers but are - * exclusive to writers. Only a single implementation, {@link - * java.util.concurrent.locks.ReentrantReadWriteLock}, is provided, since - * it covers most standard usage contexts. But programmers may create - * their own implementations to cover nonstandard requirements. - * - *

The {@link java.util.concurrent.locks.Condition} interface - * describes condition variables that may be associated with Locks. - * These are similar in usage to the implicit monitors accessed using - * {@code Object.wait}, but offer extended capabilities. - * In particular, multiple {@code Condition} objects may be associated - * with a single {@code Lock}. To avoid compatibility issues, the - * names of {@code Condition} methods are different from the - * corresponding {@code Object} versions. - * - *

The {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} - * class serves as a useful superclass for defining locks and other - * synchronizers that rely on queuing blocked threads. The {@link - * java.util.concurrent.locks.AbstractQueuedLongSynchronizer} class - * provides the same functionality but extends support to 64 bits of - * synchronization state. Both extend class {@link - * java.util.concurrent.locks.AbstractOwnableSynchronizer}, a simple - * class that helps record the thread currently holding exclusive - * synchronization. The {@link java.util.concurrent.locks.LockSupport} - * class provides lower-level blocking and unblocking support that is - * useful for those developers implementing their own customized lock - * classes. - * - * @since 1.5 - */ -package java.util.concurrent.locks; diff --git a/luni/src/main/java/java/util/concurrent/package-info.java b/luni/src/main/java/java/util/concurrent/package-info.java deleted file mode 100644 index 5dc12284b..000000000 --- a/luni/src/main/java/java/util/concurrent/package-info.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -/** - * Utility classes commonly useful in concurrent programming. This - * package includes a few small standardized extensible frameworks, as - * well as some classes that provide useful functionality and are - * otherwise tedious or difficult to implement. Here are brief - * descriptions of the main components. See also the - * {@link java.util.concurrent.locks} and - * {@link java.util.concurrent.atomic} packages. - * - *

Executors

- * - * Interfaces. - * - * {@link java.util.concurrent.Executor} is a simple standardized - * interface for defining custom thread-like subsystems, including - * thread pools, asynchronous I/O, and lightweight task frameworks. - * Depending on which concrete Executor class is being used, tasks may - * execute in a newly created thread, an existing task-execution thread, - * or the thread calling {@link java.util.concurrent.Executor#execute - * execute}, and may execute sequentially or concurrently. - * - * {@link java.util.concurrent.ExecutorService} provides a more - * complete asynchronous task execution framework. An - * ExecutorService manages queuing and scheduling of tasks, - * and allows controlled shutdown. - * - * The {@link java.util.concurrent.ScheduledExecutorService} - * subinterface and associated interfaces add support for - * delayed and periodic task execution. ExecutorServices - * provide methods arranging asynchronous execution of any - * function expressed as {@link java.util.concurrent.Callable}, - * the result-bearing analog of {@link java.lang.Runnable}. - * - * A {@link java.util.concurrent.Future} returns the results of - * a function, allows determination of whether execution has - * completed, and provides a means to cancel execution. - * - * A {@link java.util.concurrent.RunnableFuture} is a {@code Future} - * that possesses a {@code run} method that upon execution, - * sets its results. - * - *

- * - * Implementations. - * - * Classes {@link java.util.concurrent.ThreadPoolExecutor} and - * {@link java.util.concurrent.ScheduledThreadPoolExecutor} - * provide tunable, flexible thread pools. - * - * The {@link java.util.concurrent.Executors} class provides - * factory methods for the most common kinds and configurations - * of Executors, as well as a few utility methods for using - * them. Other utilities based on {@code Executors} include the - * concrete class {@link java.util.concurrent.FutureTask} - * providing a common extensible implementation of Futures, and - * {@link java.util.concurrent.ExecutorCompletionService}, that - * assists in coordinating the processing of groups of - * asynchronous tasks. - * - *

Class {@link java.util.concurrent.ForkJoinPool} provides an - * Executor primarily designed for processing instances of {@link - * java.util.concurrent.ForkJoinTask} and its subclasses. These - * classes employ a work-stealing scheduler that attains high - * throughput for tasks conforming to restrictions that often hold in - * computation-intensive parallel processing. - * - *

Queues

- * - * The {@link java.util.concurrent.ConcurrentLinkedQueue} class - * supplies an efficient scalable thread-safe non-blocking FIFO queue. - * The {@link java.util.concurrent.ConcurrentLinkedDeque} class is - * similar, but additionally supports the {@link java.util.Deque} - * interface. - * - *

Five implementations in {@code java.util.concurrent} support - * the extended {@link java.util.concurrent.BlockingQueue} - * interface, that defines blocking versions of put and take: - * {@link java.util.concurrent.LinkedBlockingQueue}, - * {@link java.util.concurrent.ArrayBlockingQueue}, - * {@link java.util.concurrent.SynchronousQueue}, - * {@link java.util.concurrent.PriorityBlockingQueue}, and - * {@link java.util.concurrent.DelayQueue}. - * The different classes cover the most common usage contexts - * for producer-consumer, messaging, parallel tasking, and - * related concurrent designs. - * - *

Extended interface {@link java.util.concurrent.TransferQueue}, - * and implementation {@link java.util.concurrent.LinkedTransferQueue} - * introduce a synchronous {@code transfer} method (along with related - * features) in which a producer may optionally block awaiting its - * consumer. - * - *

The {@link java.util.concurrent.BlockingDeque} interface - * extends {@code BlockingQueue} to support both FIFO and LIFO - * (stack-based) operations. - * Class {@link java.util.concurrent.LinkedBlockingDeque} - * provides an implementation. - * - *

Timing

- * - * The {@link java.util.concurrent.TimeUnit} class provides - * multiple granularities (including nanoseconds) for - * specifying and controlling time-out based operations. Most - * classes in the package contain operations based on time-outs - * in addition to indefinite waits. In all cases that - * time-outs are used, the time-out specifies the minimum time - * that the method should wait before indicating that it - * timed-out. Implementations make a "best effort" - * to detect time-outs as soon as possible after they occur. - * However, an indefinite amount of time may elapse between a - * time-out being detected and a thread actually executing - * again after that time-out. All methods that accept timeout - * parameters treat values less than or equal to zero to mean - * not to wait at all. To wait "forever", you can use a value - * of {@code Long.MAX_VALUE}. - * - *

Synchronizers

- * - * Five classes aid common special-purpose synchronization idioms. - *
    - * - *
  • {@link java.util.concurrent.Semaphore} is a classic concurrency tool. - * - *
  • {@link java.util.concurrent.CountDownLatch} is a very simple yet - * very common utility for blocking until a given number of signals, - * events, or conditions hold. - * - *
  • A {@link java.util.concurrent.CyclicBarrier} is a resettable - * multiway synchronization point useful in some styles of parallel - * programming. - * - *
  • A {@link java.util.concurrent.Phaser} provides - * a more flexible form of barrier that may be used to control phased - * computation among multiple threads. - * - *
  • An {@link java.util.concurrent.Exchanger} allows two threads to - * exchange objects at a rendezvous point, and is useful in several - * pipeline designs. - * - *
- * - *

Concurrent Collections

- * - * Besides Queues, this package supplies Collection implementations - * designed for use in multithreaded contexts: - * {@link java.util.concurrent.ConcurrentHashMap}, - * {@link java.util.concurrent.ConcurrentSkipListMap}, - * {@link java.util.concurrent.ConcurrentSkipListSet}, - * {@link java.util.concurrent.CopyOnWriteArrayList}, and - * {@link java.util.concurrent.CopyOnWriteArraySet}. - * When many threads are expected to access a given collection, a - * {@code ConcurrentHashMap} is normally preferable to a synchronized - * {@code HashMap}, and a {@code ConcurrentSkipListMap} is normally - * preferable to a synchronized {@code TreeMap}. - * A {@code CopyOnWriteArrayList} is preferable to a synchronized - * {@code ArrayList} when the expected number of reads and traversals - * greatly outnumber the number of updates to a list. - * - *

The "Concurrent" prefix used with some classes in this package - * is a shorthand indicating several differences from similar - * "synchronized" classes. For example {@code java.util.Hashtable} and - * {@code Collections.synchronizedMap(new HashMap())} are - * synchronized. But {@link - * java.util.concurrent.ConcurrentHashMap} is "concurrent". A - * concurrent collection is thread-safe, but not governed by a - * single exclusion lock. In the particular case of - * ConcurrentHashMap, it safely permits any number of - * concurrent reads as well as a tunable number of concurrent - * writes. "Synchronized" classes can be useful when you need - * to prevent all access to a collection via a single lock, at - * the expense of poorer scalability. In other cases in which - * multiple threads are expected to access a common collection, - * "concurrent" versions are normally preferable. And - * unsynchronized collections are preferable when either - * collections are unshared, or are accessible only when - * holding other locks. - * - *

Most concurrent Collection implementations - * (including most Queues) also differ from the usual {@code java.util} - * conventions in that their {@linkplain java.util.Iterator Iterators} - * and {@linkplain java.util.Spliterator Spliterators} provide - * weakly consistent rather than fast-fail traversal: - *

    - *
  • they may proceed concurrently with other operations - *
  • they will never throw {@link java.util.ConcurrentModificationException - * ConcurrentModificationException} - *
  • they are guaranteed to traverse elements as they existed upon - * construction exactly once, and may (but are not guaranteed to) - * reflect any modifications subsequent to construction. - *
- * - *

Memory Consistency Properties

- * - * - * Chapter 17 of - * The Java™ Language Specification defines the - * happens-before relation on memory operations such as reads and - * writes of shared variables. The results of a write by one thread are - * guaranteed to be visible to a read by another thread only if the write - * operation happens-before the read operation. The - * {@code synchronized} and {@code volatile} constructs, as well as the - * {@code Thread.start()} and {@code Thread.join()} methods, can form - * happens-before relationships. In particular: - * - *
    - *
  • Each action in a thread happens-before every action in that - * thread that comes later in the program's order. - * - *
  • An unlock ({@code synchronized} block or method exit) of a - * monitor happens-before every subsequent lock ({@code synchronized} - * block or method entry) of that same monitor. And because - * the happens-before relation is transitive, all actions - * of a thread prior to unlocking happen-before all actions - * subsequent to any thread locking that monitor. - * - *
  • A write to a {@code volatile} field happens-before every - * subsequent read of that same field. Writes and reads of - * {@code volatile} fields have similar memory consistency effects - * as entering and exiting monitors, but do not entail - * mutual exclusion locking. - * - *
  • A call to {@code start} on a thread happens-before any - * action in the started thread. - * - *
  • All actions in a thread happen-before any other thread - * successfully returns from a {@code join} on that thread. - * - *
- * - * - * The methods of all classes in {@code java.util.concurrent} and its - * subpackages extend these guarantees to higher-level - * synchronization. In particular: - * - *
    - * - *
  • Actions in a thread prior to placing an object into any concurrent - * collection happen-before actions subsequent to the access or - * removal of that element from the collection in another thread. - * - *
  • Actions in a thread prior to the submission of a {@code Runnable} - * to an {@code Executor} happen-before its execution begins. - * Similarly for {@code Callables} submitted to an {@code ExecutorService}. - * - *
  • Actions taken by the asynchronous computation represented by a - * {@code Future} happen-before actions subsequent to the - * retrieval of the result via {@code Future.get()} in another thread. - * - *
  • Actions prior to "releasing" synchronizer methods such as - * {@code Lock.unlock}, {@code Semaphore.release}, and - * {@code CountDownLatch.countDown} happen-before actions - * subsequent to a successful "acquiring" method such as - * {@code Lock.lock}, {@code Semaphore.acquire}, - * {@code Condition.await}, and {@code CountDownLatch.await} on the - * same synchronizer object in another thread. - * - *
  • For each pair of threads that successfully exchange objects via - * an {@code Exchanger}, actions prior to the {@code exchange()} - * in each thread happen-before those subsequent to the - * corresponding {@code exchange()} in another thread. - * - *
  • Actions prior to calling {@code CyclicBarrier.await} and - * {@code Phaser.awaitAdvance} (as well as its variants) - * happen-before actions performed by the barrier action, and - * actions performed by the barrier action happen-before actions - * subsequent to a successful return from the corresponding {@code await} - * in other threads. - * - *
- * - * @since 1.5 - */ -package java.util.concurrent; diff --git a/luni/src/main/java/javax/xml/datatype/FactoryFinder.java b/luni/src/main/java/javax/xml/datatype/FactoryFinder.java index 1fbca2faa..c31bee3ab 100644 --- a/luni/src/main/java/javax/xml/datatype/FactoryFinder.java +++ b/luni/src/main/java/javax/xml/datatype/FactoryFinder.java @@ -61,8 +61,8 @@ private static class CacheHolder { File f = new File(configFile); if (f.exists()) { if (debug) debugPrintln("Read properties file " + f); - try { - cacheProps.load(new FileInputStream(f)); + try (FileInputStream inputStream = new FileInputStream(f)) { + cacheProps.load(inputStream); } catch (Exception ex) { if (debug) { ex.printStackTrace(); diff --git a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java index 0060612df..50a644fba 100644 --- a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java +++ b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java @@ -62,8 +62,8 @@ private static class CacheHolder { File f = new File(configFile); if (f.exists()) { if (debug) debugPrintln("Read properties file " + f); - try { - cacheProps.load(new FileInputStream(f)); + try (FileInputStream inputStream = new FileInputStream(f)) { + cacheProps.load(inputStream); } catch (Exception ex) { if (debug) { ex.printStackTrace(); diff --git a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java index 5a7663c75..7a4f6b33c 100644 --- a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java +++ b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java @@ -69,8 +69,8 @@ private static class CacheHolder { File f = new File(configFile); if (f.exists()) { if (debug) debugPrintln("Read properties file " + f); - try { - cacheProps.load(new FileInputStream(f)); + try (FileInputStream inputStream = new FileInputStream(f)) { + cacheProps.load(inputStream); } catch (Exception ex) { if (debug) { ex.printStackTrace(); diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java index cf52b9c7a..7d1809862 100644 --- a/luni/src/main/java/libcore/icu/LocaleData.java +++ b/luni/src/main/java/libcore/icu/LocaleData.java @@ -17,7 +17,6 @@ package libcore.icu; import java.text.DateFormat; -import java.util.Arrays; import java.util.HashMap; import java.util.Locale; import libcore.util.Objects; @@ -83,10 +82,6 @@ public final class LocaleData { public String narrowAm; // "a". public String narrowPm; // "p". - // shortDateFormat, but guaranteed to have 4-digit years. - // Used by android.text.format.DateFormat.getDateFormatStringForSetting. - public String shortDateFormat4; - // Used by DateFormat to implement 12- and 24-hour SHORT and MEDIUM. // The first two are also used directly by frameworks code. public String timeFormat_hm; @@ -229,7 +224,6 @@ private static LocaleData initLocaleData(Locale locale) { // accidentally eat too much. localeData.integerPattern = localeData.numberPattern.replaceAll("\\.[#,]*", ""); } - localeData.shortDateFormat4 = localeData.shortDateFormat.replaceAll("\\byy\\b", "y"); return localeData; } } diff --git a/luni/src/main/java/libcore/icu/TimeZoneNames.java b/luni/src/main/java/libcore/icu/TimeZoneNames.java index daa915edf..917d9ce76 100644 --- a/luni/src/main/java/libcore/icu/TimeZoneNames.java +++ b/luni/src/main/java/libcore/icu/TimeZoneNames.java @@ -68,7 +68,7 @@ public ZoneStringsCache() { } long nativeStart = System.nanoTime(); - fillZoneStrings(locale.toString(), result); + fillZoneStrings(locale.toLanguageTag(), result); long nativeEnd = System.nanoTime(); internStrings(result); diff --git a/luni/src/main/java/libcore/io/Base64.java b/luni/src/main/java/libcore/io/Base64.java deleted file mode 100644 index 236c1669c..000000000 --- a/luni/src/main/java/libcore/io/Base64.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (C) 2015 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 - */ - -package libcore.io; - -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; - -/** - * Perform encoding and decoding of Base64 byte arrays as described in - * http://www.ietf.org/rfc/rfc2045.txt - */ -public final class Base64 { - private static final byte[] BASE_64_ALPHABET = initializeBase64Alphabet(); - - private static byte[] initializeBase64Alphabet() { - return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - .getBytes(StandardCharsets.US_ASCII); - } - - // Bit masks for the 4 output 6-bit values from 3 input bytes. - private static final int FIRST_OUTPUT_BYTE_MASK = 0x3f << 18; - private static final int SECOND_OUTPUT_BYTE_MASK = 0x3f << 12; - private static final int THIRD_OUTPUT_BYTE_MASK = 0x3f << 6; - private static final int FOURTH_OUTPUT_BYTE_MASK = 0x3f; - - private Base64() {} - - public static String encode(byte[] in) { - int len = in.length; - int outputLen = computeEncodingOutputLen(len); - byte[] output = new byte[outputLen]; - - int outputIndex = 0; - for (int i = 0; i < len; i += 3) { - // Only a "triplet" if there are there are at least three remaining bytes - // in the input... - // Mask with 0xff to avoid signed extension. - int byteTripletAsInt = in[i] & 0xff; - if (i + 1 < len) { - // Add second byte to the triplet. - byteTripletAsInt <<= 8; - byteTripletAsInt |= in[i + 1] & 0xff; - if (i + 2 < len) { - byteTripletAsInt <<= 8; - byteTripletAsInt |= in[i + 2] & 0xff; - } else { - // Insert 2 zero bits as to make output 18 bits long. - byteTripletAsInt <<= 2; - } - } else { - // Insert 4 zero bits as to make output 12 bits long. - byteTripletAsInt <<= 4; - } - - if (i + 2 < len) { - // The int may have up to 24 non-zero bits. - output[outputIndex++] = BASE_64_ALPHABET[ - (byteTripletAsInt & FIRST_OUTPUT_BYTE_MASK) >>> 18]; - } - if (i + 1 < len) { - // The int may have up to 18 non-zero bits. - output[outputIndex++] = BASE_64_ALPHABET[ - (byteTripletAsInt & SECOND_OUTPUT_BYTE_MASK) >>> 12]; - } - output[outputIndex++] = BASE_64_ALPHABET[ - (byteTripletAsInt & THIRD_OUTPUT_BYTE_MASK) >>> 6]; - output[outputIndex++] = BASE_64_ALPHABET[ - byteTripletAsInt & FOURTH_OUTPUT_BYTE_MASK]; - } - - int inLengthMod3 = len % 3; - // Add padding as per the spec. - if (inLengthMod3 > 0) { - output[outputIndex++] = '='; - if (inLengthMod3 == 1) { - output[outputIndex++] = '='; - } - } - - return new String(output, StandardCharsets.US_ASCII); - } - - private static int computeEncodingOutputLen(int inLength) { - int inLengthMod3 = inLength % 3; - int outputLen = (inLength / 3) * 4; - if (inLengthMod3 == 2) { - // Need 3 6-bit characters as to express the last 16 bits, plus 1 padding. - outputLen += 4; - } else if (inLengthMod3 == 1) { - // Need 2 6-bit characters as to express the last 8 bits, plus 2 padding. - outputLen += 4; - } - return outputLen; - } - - public static byte[] decode(byte[] in) { - return decode(in, in.length); - } - - /** Decodes the input from position 0 (inclusive) to len (exclusive). */ - public static byte[] decode(byte[] in, int len) { - final int inLength = Math.min(in.length, len); - // Overestimating 3 bytes per each 4 blocks of input (plus a possibly incomplete one). - ByteArrayOutputStream output = new ByteArrayOutputStream((inLength / 4) * 3 + 3); - // Position in the input. Use an array so we can pass it to {@code getNextByte}. - int[] pos = new int[1]; - - try { - while (pos[0] < inLength) { - int byteTripletAsInt = 0; - - // j is the index in a 4-tuple of 6-bit characters where are trying to read from the - // input. - for (int j = 0; j < 4; j++) { - byte c = getNextByte(in, pos, inLength); - if (c == END_OF_INPUT || c == PAD_AS_BYTE) { - // Padding or end of file... - switch (j) { - case 0: - case 1: - return (c == END_OF_INPUT) ? output.toByteArray() : null; - case 2: - // The input is over with two 6-bit characters: a single byte padded - // with 4 extra 0's. - - if (c == END_OF_INPUT) { - // Do not consider the block, since padding is not present. - return checkNoTrailingAndReturn(output, in, pos[0], inLength); - } - // We are at a pad character, consume and look for the second one. - pos[0]++; - c = getNextByte(in, pos, inLength); - if (c == END_OF_INPUT) { - // Do not consider the block, since padding is not present. - return checkNoTrailingAndReturn(output, in, pos[0], inLength); - } - if (c == PAD_AS_BYTE) { - byteTripletAsInt >>= 4; - output.write(byteTripletAsInt); - return checkNoTrailingAndReturn(output, in, pos[0], inLength); - } - // Something other than pad and non-alphabet characters, illegal. - return null; - - - case 3: - // The input is over with three 6-bit characters: two bytes padded - // with 2 extra 0's. - if (c == PAD_AS_BYTE) { - // Consider the block only if padding is present. - byteTripletAsInt >>= 2; - output.write(byteTripletAsInt >> 8); - output.write(byteTripletAsInt & 0xff); - } - return checkNoTrailingAndReturn(output, in, pos[0], inLength); - } - } else { - byteTripletAsInt <<= 6; - byteTripletAsInt += (c & 0xff); - pos[0]++; - } - } - // We have four 6-bit characters: output the corresponding 3 bytes - output.write(byteTripletAsInt >> 16); - output.write((byteTripletAsInt >> 8) & 0xff); - output.write(byteTripletAsInt & 0xff); - } - return checkNoTrailingAndReturn(output, in, pos[0], inLength); - } catch (InvalidBase64ByteException e) { - return null; - } - } - - /** - * On decoding, an illegal character always return null. - * - * Using this exception to avoid "if" checks every time. - */ - - private static class InvalidBase64ByteException extends Exception { } - - /** - * Obtain the numeric value corresponding to the next relevant byte in the input. - * - * Calculates the numeric value (6-bit, 0 <= x <= 63) of the next Base64 encoded byte in - * {@code in} at or after {@code pos[0]} and before {@code inLength}. Returns - * {@link #WHITESPACE_AS_BYTE}, {@link #PAD_AS_BYTE}, {@link #END_OF_INPUT} or the 6-bit value. - * {@code pos[0]} is updated as a side effect of this method. - */ - private static byte getNextByte(byte[] in, int[] pos, int inLength) - throws InvalidBase64ByteException { - // Ignore all whitespace. - while (pos[0] < inLength) { - byte c = base64AlphabetToNumericalValue(in[pos[0]]); - if (c != WHITESPACE_AS_BYTE) { - return c; - } - pos[0]++; - } - return END_OF_INPUT; - } - - /** - * Check that there are no invalid trailing characters (ie, other then whitespace and padding) - * - * Returns {@code output} as a byte array in case of success, {@code null} in case of invalid - * characters. - */ - private static byte[] checkNoTrailingAndReturn( - ByteArrayOutputStream output, byte[] in, int i, int inLength) - throws InvalidBase64ByteException{ - while (i < inLength) { - byte c = base64AlphabetToNumericalValue(in[i]); - if (c != WHITESPACE_AS_BYTE && c != PAD_AS_BYTE) { - return null; - } - i++; - } - return output.toByteArray(); - } - - private static final byte PAD_AS_BYTE = -1; - private static final byte WHITESPACE_AS_BYTE = -2; - private static final byte END_OF_INPUT = -3; - private static byte base64AlphabetToNumericalValue(byte c) throws InvalidBase64ByteException { - if ('A' <= c && c <= 'Z') { - return (byte) (c - 'A'); - } - if ('a' <= c && c <= 'z') { - return (byte) (c - 'a' + 26); - } - if ('0' <= c && c <= '9') { - return (byte) (c - '0' + 52); - } - if (c == '+') { - return (byte) 62; - } - if (c == '/') { - return (byte) 63; - } - if (c == '=') { - return PAD_AS_BYTE; - } - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { - return WHITESPACE_AS_BYTE; - } - throw new InvalidBase64ByteException(); - } -} diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java index 2523c7189..111c5842f 100644 --- a/luni/src/main/java/libcore/io/BlockGuardOs.java +++ b/luni/src/main/java/libcore/io/BlockGuardOs.java @@ -17,6 +17,7 @@ package libcore.io; import android.system.ErrnoException; +import android.system.OsConstants; import android.system.StructLinger; import android.system.StructPollfd; import android.system.StructStat; @@ -32,7 +33,6 @@ import java.net.SocketException; import java.nio.ByteBuffer; import static android.system.OsConstants.*; -import static dalvik.system.BlockGuard.DISALLOW_NETWORK; /** * Informs BlockGuard of any activity it should be aware of. @@ -61,7 +61,11 @@ private void untagSocket(FileDescriptor fd) throws ErrnoException { @Override public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException { BlockGuard.getThreadPolicy().onNetwork(); - return tagSocket(os.accept(fd, peerAddress)); + final FileDescriptor acceptFd = os.accept(fd, peerAddress); + if (isInetSocket(acceptFd)) { + tagSocket(acceptFd); + } + return acceptFd; } @Override public boolean access(String path, int mode) throws ErrnoException { @@ -91,7 +95,9 @@ private void untagSocket(FileDescriptor fd) throws ErrnoException { // connections in methods like onDestroy which will run on the UI thread. BlockGuard.getThreadPolicy().onNetwork(); } - untagSocket(fd); + if (isInetSocket(fd)) { + untagSocket(fd); + } } } catch (ErrnoException ignored) { // We're called via Socket.close (which doesn't ask for us to be called), so we @@ -102,6 +108,14 @@ private void untagSocket(FileDescriptor fd) throws ErrnoException { os.close(fd); } + private static boolean isInetSocket(FileDescriptor fd) throws ErrnoException{ + return isInetDomain(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_DOMAIN)); + } + + private static boolean isInetDomain(int domain) { + return (domain == AF_INET) || (domain == AF_INET6); + } + private static boolean isLingerSocket(FileDescriptor fd) throws ErrnoException { StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER); return linger.isOn() && linger.l_linger > 0; @@ -112,6 +126,12 @@ private static boolean isLingerSocket(FileDescriptor fd) throws ErrnoException { os.connect(fd, address, port); } + @Override public void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, + SocketException { + BlockGuard.getThreadPolicy().onNetwork(); + os.connect(fd, address); + } + @Override public void fchmod(FileDescriptor fd, int mode) throws ErrnoException { BlockGuard.getThreadPolicy().onWriteToDisk(); os.fchmod(fd, mode); @@ -181,7 +201,7 @@ private static boolean isLingerSocket(FileDescriptor fd) throws ErrnoException { @Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException { BlockGuard.getThreadPolicy().onReadFromDisk(); - if ((mode & O_ACCMODE) != O_RDONLY) { + if ((flags & O_ACCMODE) != O_RDONLY) { BlockGuard.getThreadPolicy().onWriteToDisk(); } return os.open(path, flags, mode); @@ -285,13 +305,19 @@ private static boolean isLingerSocket(FileDescriptor fd) throws ErrnoException { } @Override public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { - return tagSocket(os.socket(domain, type, protocol)); + final FileDescriptor fd = os.socket(domain, type, protocol); + if (isInetDomain(domain)) { + tagSocket(fd); + } + return fd; } @Override public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { os.socketpair(domain, type, protocol, fd1, fd2); - tagSocket(fd1); - tagSocket(fd2); + if (isInetDomain(domain)) { + tagSocket(fd1); + tagSocket(fd2); + } } @Override public StructStat stat(String path) throws ErrnoException { @@ -323,4 +349,49 @@ private static boolean isLingerSocket(FileDescriptor fd) throws ErrnoException { BlockGuard.getThreadPolicy().onWriteToDisk(); return os.writev(fd, buffers, offsets, byteCounts); } + + @Override public void execv(String filename, String[] argv) throws ErrnoException { + BlockGuard.getThreadPolicy().onReadFromDisk(); + os.execv(filename, argv); + } + + @Override public void execve(String filename, String[] argv, String[] envp) + throws ErrnoException { + BlockGuard.getThreadPolicy().onReadFromDisk(); + os.execve(filename, argv, envp); + } + + @Override public byte[] getxattr(String path, String name) throws ErrnoException { + BlockGuard.getThreadPolicy().onReadFromDisk(); + return os.getxattr(path, name); + } + + @Override public void msync(long address, long byteCount, int flags) throws ErrnoException { + if ((flags & OsConstants.MS_SYNC) != 0) { + BlockGuard.getThreadPolicy().onWriteToDisk(); + } + os.msync(address, byteCount, flags); + } + + @Override public void removexattr(String path, String name) throws ErrnoException { + BlockGuard.getThreadPolicy().onWriteToDisk(); + os.removexattr(path, name); + } + + @Override public void setxattr(String path, String name, byte[] value, int flags) + throws ErrnoException { + BlockGuard.getThreadPolicy().onWriteToDisk(); + os.setxattr(path, name, value, flags); + } + + @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, + int flags, SocketAddress address) throws ErrnoException, SocketException { + BlockGuard.getThreadPolicy().onNetwork(); + return os.sendto(fd, bytes, byteOffset, byteCount, flags, address); + } + + @Override public void unlink(String pathname) throws ErrnoException { + BlockGuard.getThreadPolicy().onWriteToDisk(); + os.unlink(pathname); + } } diff --git a/luni/src/main/java/libcore/io/BufferIterator.java b/luni/src/main/java/libcore/io/BufferIterator.java index 7f3ad472b..0d167e340 100644 --- a/luni/src/main/java/libcore/io/BufferIterator.java +++ b/luni/src/main/java/libcore/io/BufferIterator.java @@ -20,11 +20,12 @@ * Iterates over big- or little-endian bytes. See {@link MemoryMappedFile#bigEndianIterator} and * {@link MemoryMappedFile#littleEndianIterator}. * - * @hide don't make this public without adding bounds checking. + * @hide */ public abstract class BufferIterator { /** - * Seeks to the absolute position {@code offset}, measured in bytes from the start. + * Seeks to the absolute position {@code offset}, measured in bytes from the start of the + * buffer. */ public abstract void seek(int offset); @@ -33,30 +34,45 @@ public abstract class BufferIterator { */ public abstract void skip(int byteCount); + /** + * Returns the current position of the iterator within the buffer. + */ + public abstract int pos(); + /** * Copies {@code byteCount} bytes from the current position into {@code dst}, starting at * {@code dstOffset}, and advances the current position {@code byteCount} bytes. + * + * @throws IndexOutOfBoundsException if the read / write would be outside of the buffer / array */ public abstract void readByteArray(byte[] dst, int dstOffset, int byteCount); /** * Returns the byte at the current position, and advances the current position one byte. + * + * @throws IndexOutOfBoundsException if the read would be outside of the buffer */ public abstract byte readByte(); /** * Returns the 32-bit int at the current position, and advances the current position four bytes. + * + * @throws IndexOutOfBoundsException if the read would be outside of the buffer */ public abstract int readInt(); /** * Copies {@code intCount} 32-bit ints from the current position into {@code dst}, starting at * {@code dstOffset}, and advances the current position {@code 4 * intCount} bytes. + * + * @throws IndexOutOfBoundsException if the read / write would be outside of the buffer / array */ public abstract void readIntArray(int[] dst, int dstOffset, int intCount); /** * Returns the 16-bit short at the current position, and advances the current position two bytes. + * + * @throws IndexOutOfBoundsException if the read would be outside of the buffer */ public abstract short readShort(); } diff --git a/luni/src/main/java/libcore/io/ClassPathURLStreamHandler.java b/luni/src/main/java/libcore/io/ClassPathURLStreamHandler.java index 9f8a84402..117c1f8aa 100644 --- a/luni/src/main/java/libcore/io/ClassPathURLStreamHandler.java +++ b/luni/src/main/java/libcore/io/ClassPathURLStreamHandler.java @@ -103,7 +103,22 @@ static ZipEntry findEntryWithDirectoryFallback(JarFile jarFile, String entryName } private class ClassPathURLConnection extends JarURLConnection { - // The JarFile instance is shared across URLConnections and must not be closed. + // The JarFile instance can be shared across URLConnections and should not be closed when it is: + // + // Sharing occurs if getUseCaches() is true when connect() is called (which can take place + // implicitly). useCachedJarFile records the state of sharing at connect() time. + // useCachedJarFile == true is the common case. If developers call getJarFile().close() when + // sharing is enabled then it will affect other users (current and future) of the shared + // JarFile. + // + // Developers could call ClassLoader.findResource().openConnection() to get a URLConnection and + // then call setUseCaches(false) before connect() to prevent sharing. The developer must then + // call getJarFile().close() or close() on the inputStream from getInputStream() will do it + // automatically. This is likely to be an extremely rare case. + // + // Most developers are not expecting to deal with the lifecycle of the underlying JarFile object + // at all. The presence of the getJarFile() method and setUseCaches() forces us to consider / + // handle it. private JarFile connectionJarFile; private ZipEntry jarEntry; @@ -141,7 +156,7 @@ public JarFile getJarFile() throws IOException { connect(); // We do cache in the surrounding class if useCachedJarFile is true to - // preserve garbage collection semantics to avoid leak warnings. + // preserve garbage collection semantics and to avoid leak warnings. if (useCachedJarFile) { connectionJarFile = jarFile; } else { @@ -163,8 +178,9 @@ public InputStream getInputStream() throws IOException { @Override public void close() throws IOException { super.close(); - // If the jar file is not cached closing the input stream will close the URLConnection and - // any JarFile returned from getJarFile(). + // If the jar file is not cached then closing the input stream will close the + // URLConnection and any JarFile returned from getJarFile(). If the jar file is cached + // we must not close it because it will affect other URLConnections. if (connectionJarFile != null && !useCachedJarFile) { connectionJarFile.close(); closed = true; diff --git a/luni/src/main/java/libcore/io/DropBox.java b/luni/src/main/java/libcore/io/DropBox.java index cf881060a..4180a2aee 100644 --- a/luni/src/main/java/libcore/io/DropBox.java +++ b/luni/src/main/java/libcore/io/DropBox.java @@ -16,6 +16,8 @@ package libcore.io; +import java.util.Base64; + public final class DropBox { /** @@ -54,7 +56,7 @@ public static interface Reporter { private static final class DefaultReporter implements Reporter { public void addData(String tag, byte[] data, int flags) { - System.out.println(tag + ": " + Base64.encode(data)); + System.out.println(tag + ": " + Base64.getEncoder().encodeToString(data)); } public void addText(String tag, String data) { diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java index fbf89398c..55d4d8243 100644 --- a/luni/src/main/java/libcore/io/ForwardingOs.java +++ b/luni/src/main/java/libcore/io/ForwardingOs.java @@ -19,9 +19,12 @@ import android.system.ErrnoException; import android.system.GaiException; import android.system.StructAddrinfo; +import android.system.StructCapUserData; +import android.system.StructCapUserHeader; import android.system.StructFlock; import android.system.StructGroupReq; import android.system.StructGroupSourceReq; +import android.system.StructIfaddrs; import android.system.StructLinger; import android.system.StructPasswd; import android.system.StructPollfd; @@ -55,6 +58,14 @@ public ForwardingOs(Os os) { public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { return os.android_getaddrinfo(node, hints, netId); } public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); } public void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { os.bind(fd, address); } + @Override + public StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException { + return os.capget(hdr); + } + @Override + public void capset(StructCapUserHeader hdr, StructCapUserData[] data) throws ErrnoException { + os.capset(hdr, data); + } public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); } public void chown(String path, int uid, int gid) throws ErrnoException { os.chown(path, uid, gid); } public void close(FileDescriptor fd) throws ErrnoException { os.close(fd); } @@ -96,16 +107,21 @@ public ForwardingOs(Os os) { public StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptUcred(fd, level, option); } public int gettid() { return os.gettid(); } public int getuid() { return os.getuid(); } - public int getxattr(String path, String name, byte[] outValue) throws ErrnoException { return os.getxattr(path, name, outValue); } + public byte[] getxattr(String path, String name) throws ErrnoException { return os.getxattr(path, name); } + public StructIfaddrs[] getifaddrs() throws ErrnoException { return os.getifaddrs(); } public String if_indextoname(int index) { return os.if_indextoname(index); } + public int if_nametoindex(String name) { return os.if_nametoindex(name); } public InetAddress inet_pton(int family, String address) { return os.inet_pton(family, address); } + public int ioctlFlags(FileDescriptor fd, String interfaceName) throws ErrnoException { return os.ioctlFlags(fd, interfaceName); }; public InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException { return os.ioctlInetAddress(fd, cmd, interfaceName); } public int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException { return os.ioctlInt(fd, cmd, arg); } + public int ioctlMTU(FileDescriptor fd, String interfaceName) throws ErrnoException { return os.ioctlMTU(fd, interfaceName); }; public boolean isatty(FileDescriptor fd) { return os.isatty(fd); } public void kill(int pid, int signal) throws ErrnoException { os.kill(pid, signal); } public void lchown(String path, int uid, int gid) throws ErrnoException { os.lchown(path, uid, gid); } public void link(String oldPath, String newPath) throws ErrnoException { os.link(oldPath, newPath); } public void listen(FileDescriptor fd, int backlog) throws ErrnoException { os.listen(fd, backlog); } + public String[] listxattr(String path) throws ErrnoException { return os.listxattr(path); } public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException { return os.lseek(fd, offset, whence); } public StructStat lstat(String path) throws ErrnoException { return os.lstat(path); } public void mincore(long address, long byteCount, byte[] vector) throws ErrnoException { os.mincore(address, byteCount, vector); } diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java index acf9b3973..0c34d5e1b 100644 --- a/luni/src/main/java/libcore/io/IoBridge.java +++ b/luni/src/main/java/libcore/io/IoBridge.java @@ -98,7 +98,12 @@ public static void bind(FileDescriptor fd, InetAddress address, int port) throws try { Libcore.os.bind(fd, address, port); } catch (ErrnoException errnoException) { - throw new BindException(errnoException.getMessage(), errnoException); + if (errnoException.errno == EADDRINUSE || errnoException.errno == EADDRNOTAVAIL || + errnoException.errno == EPERM || errnoException.errno == EACCES) { + throw new BindException(errnoException.getMessage(), errnoException); + } else { + throw new SocketException(errnoException.getMessage(), errnoException); + } } } @@ -123,7 +128,8 @@ public static void connect(FileDescriptor fd, InetAddress inetAddress, int port, try { connectErrno(fd, inetAddress, port, timeoutMs); } catch (ErrnoException errnoException) { - throw new ConnectException(connectDetail(inetAddress, port, timeoutMs, errnoException), errnoException); + throw new ConnectException(connectDetail(fd, inetAddress, port, timeoutMs, + errnoException), errnoException); } catch (SocketException ex) { throw ex; // We don't want to doubly wrap these. } catch (SocketTimeoutException ex) { @@ -169,21 +175,44 @@ private static void connectErrno(FileDescriptor fd, InetAddress inetAddress, int remainingTimeoutMs = (int) TimeUnit.NANOSECONDS.toMillis(finishTimeNanos - System.nanoTime()); if (remainingTimeoutMs <= 0) { - throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null)); + throw new SocketTimeoutException(connectDetail(fd, inetAddress, port, timeoutMs, + null)); } } while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs)); IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking. } - private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) { - String detail = "failed to connect to " + inetAddress + " (port " + port + ")"; + private static String connectDetail(FileDescriptor fd, InetAddress inetAddress, int port, + int timeoutMs, Exception cause) { + // Figure out source address from fd. + InetSocketAddress localAddress = null; + try { + localAddress = getLocalInetSocketAddress(fd); + } catch (SocketException ignored) { } + + StringBuilder sb = new StringBuilder("failed to connect") + .append(" to ") + .append(inetAddress) + .append(" (port ") + .append(port) + .append(")"); + if (localAddress != null) { + sb.append(" from ") + .append(localAddress.getAddress()) + .append(" (port ") + .append(localAddress.getPort()) + .append(")"); + } if (timeoutMs > 0) { - detail += " after " + timeoutMs + "ms"; + sb.append(" after ") + .append(timeoutMs) + .append("ms"); } if (cause != null) { - detail += ": " + cause.getMessage(); + sb.append(": ") + .append(cause.getMessage()); } - return detail; + return sb.toString(); } /** @@ -230,7 +259,7 @@ public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, in } cause = errnoException; } - String detail = connectDetail(inetAddress, port, timeoutMs, cause); + String detail = connectDetail(fd, inetAddress, port, timeoutMs, cause); if (cause.errno == ETIMEDOUT) { throw new SocketTimeoutException(detail, cause); } @@ -245,6 +274,7 @@ public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, in public static final int JAVA_MCAST_BLOCK_SOURCE = 23; public static final int JAVA_MCAST_UNBLOCK_SOURCE = 24; public static final int JAVA_IP_MULTICAST_TTL = 17; + public static final int JAVA_IP_TTL = 25; /** * java.net has its own socket options similar to the underlying Unix ones. We paper over the @@ -261,19 +291,22 @@ public static Object getSocketOption(FileDescriptor fd, int option) throws Socke private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws ErrnoException, SocketException { switch (option) { case SocketOptions.IP_MULTICAST_IF: - // This is IPv4-only. - return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF); case SocketOptions.IP_MULTICAST_IF2: - // This is IPv6-only. return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF); case SocketOptions.IP_MULTICAST_LOOP: // Since setting this from java.net always sets IPv4 and IPv6 to the same value, // it doesn't matter which we return. - return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP)); + // NOTE: getsockopt's return value means "isEnabled", while OpenJDK code java.net + // requires a value that means "isDisabled" so we NEGATE the system call value here. + return !booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP)); case IoBridge.JAVA_IP_MULTICAST_TTL: // Since setting this from java.net always sets IPv4 and IPv6 to the same value, // it doesn't matter which we return. return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); + case IoBridge.JAVA_IP_TTL: + // Since setting this from java.net always sets IPv4 and IPv6 to the same value, + // it doesn't matter which we return. + return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS); case SocketOptions.IP_TOS: // Since setting this from java.net always sets IPv4 and IPv6 to the same value, // it doesn't matter which we return. @@ -300,6 +333,8 @@ private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis(); case SocketOptions.TCP_NODELAY: return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY)); + case SocketOptions.SO_BINDADDR: + return ((InetSocketAddress) Libcore.os.getsockname(fd)).getAddress(); default: throw new SocketException("Unknown socket option: " + option); } @@ -328,7 +363,15 @@ public static void setSocketOption(FileDescriptor fd, int option, Object value) private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws ErrnoException, SocketException { switch (option) { case SocketOptions.IP_MULTICAST_IF: - throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android"); + NetworkInterface nif = NetworkInterface.getByInetAddress((InetAddress) value); + if (nif == null) { + throw new SocketException( + "bad argument for IP_MULTICAST_IF : address not bound to any interface"); + } + // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int. + Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, nif.getIndex()); + Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, nif.getIndex()); + return; case SocketOptions.IP_MULTICAST_IF2: // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int. Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value); @@ -336,8 +379,11 @@ private static void setSocketOptionErrno(FileDescriptor fd, int option, Object v return; case SocketOptions.IP_MULTICAST_LOOP: // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte. - Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value)); - Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value)); + // NOTE: setsockopt's arguement value means "isEnabled", while OpenJDK code java.net + // uses a value that means "isDisabled" so we NEGATE the system call value here. + int enable = booleanToInt(!((Boolean) value)); + Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, enable); + Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, enable); return; case IoBridge.JAVA_IP_MULTICAST_TTL: // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int, @@ -345,6 +391,10 @@ private static void setSocketOptionErrno(FileDescriptor fd, int option, Object v Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value); Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value); return; + case IoBridge.JAVA_IP_TTL: + Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TTL, (Integer) value); + Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (Integer) value); + return; case SocketOptions.IP_TOS: Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value); Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value); @@ -530,10 +580,11 @@ public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAd return result; } - private static int maybeThrowAfterSendto(boolean isDatagram, ErrnoException errnoException) throws SocketException { + private static int maybeThrowAfterSendto(boolean isDatagram, ErrnoException errnoException) + throws IOException { if (isDatagram) { - if (errnoException.errno == ECONNRESET || errnoException.errno == ECONNREFUSED) { - return 0; + if (errnoException.errno == ECONNREFUSED) { + throw new PortUnreachableException("ICMP Port Unreachable"); } } else { if (errnoException.errno == EAGAIN) { @@ -542,15 +593,15 @@ private static int maybeThrowAfterSendto(boolean isDatagram, ErrnoException errn return 0; } } - throw errnoException.rethrowAsSocketException(); + throw errnoException.rethrowAsIOException(); } public static int recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, DatagramPacket packet, boolean isConnected) throws IOException { int result; try { - InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null; + InetSocketAddress srcAddress = packet != null ? new InetSocketAddress() : null; result = Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); - result = postRecvfrom(isRead, packet, isConnected, srcAddress, result); + result = postRecvfrom(isRead, packet, srcAddress, result); } catch (ErrnoException errnoException) { result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException); } @@ -560,24 +611,26 @@ public static int recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int public static int recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected) throws IOException { int result; try { - InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null; + InetSocketAddress srcAddress = packet != null ? new InetSocketAddress() : null; result = Libcore.os.recvfrom(fd, buffer, flags, srcAddress); - result = postRecvfrom(isRead, packet, isConnected, srcAddress, result); + result = postRecvfrom(isRead, packet, srcAddress, result); } catch (ErrnoException errnoException) { result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException); } return result; } - private static int postRecvfrom(boolean isRead, DatagramPacket packet, boolean isConnected, InetSocketAddress srcAddress, int byteCount) { + private static int postRecvfrom(boolean isRead, DatagramPacket packet, InetSocketAddress srcAddress, int byteCount) { if (isRead && byteCount == 0) { return -1; } if (packet != null) { packet.setReceivedLength(byteCount); - if (!isConnected) { + packet.setPort(srcAddress.getPort()); + + // packet.address should only be changed when it is different from srcAddress. + if (!srcAddress.getAddress().equals(packet.getAddress())) { packet.setAddress(srcAddress.getAddress()); - packet.setPort(srcAddress.getPort()); } } return byteCount; @@ -592,7 +645,7 @@ private static int maybeThrowAfterRecvfrom(boolean isRead, boolean isConnected, } } else { if (isConnected && errnoException.errno == ECONNREFUSED) { - throw new PortUnreachableException("", errnoException); + throw new PortUnreachableException("ICMP Port Unreachable", errnoException); } else if (errnoException.errno == EAGAIN) { throw new SocketTimeoutException(errnoException); } else { @@ -601,21 +654,10 @@ private static int maybeThrowAfterRecvfrom(boolean isRead, boolean isConnected, } } - public static FileDescriptor socket(boolean stream) throws SocketException { + public static FileDescriptor socket(int domain, int type, int protocol) throws SocketException { FileDescriptor fd; try { - fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0); - - // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults - // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which - // would be correct for the *unicast* hop limit). - // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to - // have been applied as a result of that discussion. If that bug is ever fixed, we can - // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets. - // (IPv4 is already correct.) - if (!stream) { - Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1); - } + fd = Libcore.os.socket(domain, type, protocol); return fd; } catch (ErrnoException errnoException) { @@ -623,21 +665,32 @@ public static FileDescriptor socket(boolean stream) throws SocketException { } } - public static InetAddress getSocketLocalAddress(FileDescriptor fd) throws SocketException { + /** + * Wait for some event on a file descriptor, blocks until the event happened or timeout period + * passed. See poll(2) and @link{android.system.Os.Poll}. + * + * @throws SocketException if poll(2) fails. + * @throws SocketTimeoutException if the event has not happened before timeout period has passed. + */ + public static void poll(FileDescriptor fd, int events, int timeout) + throws SocketException, SocketTimeoutException { + StructPollfd[] pollFds = new StructPollfd[]{ new StructPollfd() }; + pollFds[0].fd = fd; + pollFds[0].events = (short) events; + try { - SocketAddress sa = Libcore.os.getsockname(fd); - InetSocketAddress isa = (InetSocketAddress) sa; - return isa.getAddress(); - } catch (ErrnoException errnoException) { - throw errnoException.rethrowAsSocketException(); + int ret = android.system.Os.poll(pollFds, timeout); + if (ret == 0) { + throw new SocketTimeoutException("Poll timed out"); + } + } catch (ErrnoException e) { + e.rethrowAsSocketException(); } } - public static int getSocketLocalPort(FileDescriptor fd) throws SocketException { + public static InetSocketAddress getLocalInetSocketAddress(FileDescriptor fd) throws SocketException { try { - SocketAddress sa = Libcore.os.getsockname(fd); - InetSocketAddress isa = (InetSocketAddress) sa; - return isa.getPort(); + return (InetSocketAddress) Libcore.os.getsockname(fd); } catch (ErrnoException errnoException) { throw errnoException.rethrowAsSocketException(); } diff --git a/luni/src/main/java/libcore/io/IoTracker.java b/luni/src/main/java/libcore/io/IoTracker.java new file mode 100644 index 000000000..4623b6a78 --- /dev/null +++ b/luni/src/main/java/libcore/io/IoTracker.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.io; + +import dalvik.system.BlockGuard; + +/** + * Used to detect unbuffered I/O. + * @hide + */ +public final class IoTracker { + private int opCount; + private int totalByteCount; + private boolean isOpen = true; + private Mode mode = Mode.READ; + + public void trackIo(int byteCount) { + ++opCount; + totalByteCount += byteCount; + if (isOpen && opCount > 10 && totalByteCount < 10*512) { + BlockGuard.getThreadPolicy().onUnbufferedIO(); + isOpen = false; + } + } + + public void trackIo(int byteCount, Mode mode) { + if (this.mode != mode) { + reset(); + this.mode = mode; + } + trackIo(byteCount); + } + + /** + * Resets the state of the IoTracker, except {@link #isOpen} as it is not required to notify + * again and again about the same stream. + * This is primarily used by RandomAccessFile to consider a case when {@link + * java.io.RandomAccessFile#seek seek} is called. + */ + public void reset() { + opCount = 0; + totalByteCount = 0; + } + + public enum Mode { + READ, + WRITE + } +} diff --git a/luni/src/main/java/libcore/io/Libcore.java b/luni/src/main/java/libcore/io/Libcore.java index 5f57f91bb..cbc5a55fc 100644 --- a/luni/src/main/java/libcore/io/Libcore.java +++ b/luni/src/main/java/libcore/io/Libcore.java @@ -19,5 +19,15 @@ public final class Libcore { private Libcore() { } - public static Os os = new BlockGuardOs(new Posix()); + /** + * Direct access to syscalls. Code should strongly prefer using {@link #os} + * unless it has a strong reason to bypass the helpful checks/guards that it + * provides. + */ + public static Os rawOs = new Linux(); + + /** + * Access to syscalls with helpful checks/guards. + */ + public static Os os = new BlockGuardOs(rawOs); } diff --git a/luni/src/main/java/libcore/io/Linux.java b/luni/src/main/java/libcore/io/Linux.java new file mode 100644 index 000000000..09adb09c3 --- /dev/null +++ b/luni/src/main/java/libcore/io/Linux.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2011 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. + */ + +package libcore.io; + +import android.system.ErrnoException; +import android.system.GaiException; +import android.system.StructAddrinfo; +import android.system.StructCapUserData; +import android.system.StructCapUserHeader; +import android.system.StructFlock; +import android.system.StructGroupReq; +import android.system.StructGroupSourceReq; +import android.system.StructIfaddrs; +import android.system.StructLinger; +import android.system.StructPasswd; +import android.system.StructPollfd; +import android.system.StructStat; +import android.system.StructStatVfs; +import android.system.StructTimeval; +import android.system.StructUcred; +import android.system.StructUtsname; +import android.util.MutableInt; +import android.util.MutableLong; +import java.io.FileDescriptor; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.NioUtils; + +public final class Linux implements Os { + Linux() { } + + public native FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException; + public native boolean access(String path, int mode) throws ErrnoException; + public native InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException; + public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; + public native void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException; + @Override + public native StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException; + @Override + public native void capset(StructCapUserHeader hdr, StructCapUserData[] data) + throws ErrnoException; + public native void chmod(String path, int mode) throws ErrnoException; + public native void chown(String path, int uid, int gid) throws ErrnoException; + public native void close(FileDescriptor fd) throws ErrnoException; + public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; + public native void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException; + public native FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException; + public native FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException; + public native String[] environ(); + public native void execv(String filename, String[] argv) throws ErrnoException; + public native void execve(String filename, String[] argv, String[] envp) throws ErrnoException; + public native void fchmod(FileDescriptor fd, int mode) throws ErrnoException; + public native void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException; + public native int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException, InterruptedIOException; + public native int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException; + public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException; + public native void fdatasync(FileDescriptor fd) throws ErrnoException; + public native StructStat fstat(FileDescriptor fd) throws ErrnoException; + public native StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException; + public native void fsync(FileDescriptor fd) throws ErrnoException; + public native void ftruncate(FileDescriptor fd, long length) throws ErrnoException; + public native String gai_strerror(int error); + public native int getegid(); + public native int geteuid(); + public native int getgid(); + public native String getenv(String name); + public native String getnameinfo(InetAddress address, int flags) throws GaiException; + public native SocketAddress getpeername(FileDescriptor fd) throws ErrnoException; + public native int getpgid(int pid); + public native int getpid(); + public native int getppid(); + public native StructPasswd getpwnam(String name) throws ErrnoException; + public native StructPasswd getpwuid(int uid) throws ErrnoException; + public native SocketAddress getsockname(FileDescriptor fd) throws ErrnoException; + public native int getsockoptByte(FileDescriptor fd, int level, int option) throws ErrnoException; + public native InetAddress getsockoptInAddr(FileDescriptor fd, int level, int option) throws ErrnoException; + public native int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException; + public native StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException; + public native StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException; + public native StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException; + public native int gettid(); + public native int getuid(); + public native byte[] getxattr(String path, String name) throws ErrnoException; + public native StructIfaddrs[] getifaddrs() throws ErrnoException; + public native String if_indextoname(int index); + public native int if_nametoindex(String name); + public native InetAddress inet_pton(int family, String address); + public native int ioctlFlags(FileDescriptor fd, String interfaceName) throws ErrnoException; + public native InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException; + public native int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException; + public native int ioctlMTU(FileDescriptor fd, String interfaceName) throws ErrnoException; + public native boolean isatty(FileDescriptor fd); + public native void kill(int pid, int signal) throws ErrnoException; + public native void lchown(String path, int uid, int gid) throws ErrnoException; + public native void link(String oldPath, String newPath) throws ErrnoException; + public native void listen(FileDescriptor fd, int backlog) throws ErrnoException; + public native String[] listxattr(String path) throws ErrnoException; + public native long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException; + public native StructStat lstat(String path) throws ErrnoException; + public native void mincore(long address, long byteCount, byte[] vector) throws ErrnoException; + public native void mkdir(String path, int mode) throws ErrnoException; + public native void mkfifo(String path, int mode) throws ErrnoException; + public native void mlock(long address, long byteCount) throws ErrnoException; + public native long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException; + public native void msync(long address, long byteCount, int flags) throws ErrnoException; + public native void munlock(long address, long byteCount) throws ErrnoException; + public native void munmap(long address, long byteCount) throws ErrnoException; + public native FileDescriptor open(String path, int flags, int mode) throws ErrnoException; + public native FileDescriptor[] pipe2(int flags) throws ErrnoException; + public native int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException; + public native void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException; + public native int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException; + public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { + final int bytesRead; + final int position = buffer.position(); + + if (buffer.isDirect()) { + bytesRead = preadBytes(fd, buffer, position, buffer.remaining(), offset); + } else { + bytesRead = preadBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining(), offset); + } + + maybeUpdateBufferPosition(buffer, position, bytesRead); + return bytesRead; + } + public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { + // This indirection isn't strictly necessary, but ensures that our public interface is type safe. + return preadBytes(fd, bytes, byteOffset, byteCount, offset); + } + private native int preadBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException; + public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { + final int bytesWritten; + final int position = buffer.position(); + + if (buffer.isDirect()) { + bytesWritten = pwriteBytes(fd, buffer, position, buffer.remaining(), offset); + } else { + bytesWritten = pwriteBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining(), offset); + } + + maybeUpdateBufferPosition(buffer, position, bytesWritten); + return bytesWritten; + } + public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { + // This indirection isn't strictly necessary, but ensures that our public interface is type safe. + return pwriteBytes(fd, bytes, byteOffset, byteCount, offset); + } + private native int pwriteBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException; + public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { + final int bytesRead; + final int position = buffer.position(); + + if (buffer.isDirect()) { + bytesRead = readBytes(fd, buffer, position, buffer.remaining()); + } else { + bytesRead = readBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining()); + } + + maybeUpdateBufferPosition(buffer, position, bytesRead); + return bytesRead; + } + public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { + // This indirection isn't strictly necessary, but ensures that our public interface is type safe. + return readBytes(fd, bytes, byteOffset, byteCount); + } + private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException; + public native String readlink(String path) throws ErrnoException; + public native String realpath(String path) throws ErrnoException; + public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException; + public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { + final int bytesReceived; + final int position = buffer.position(); + + if (buffer.isDirect()) { + bytesReceived = recvfromBytes(fd, buffer, position, buffer.remaining(), flags, srcAddress); + } else { + bytesReceived = recvfromBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining(), flags, srcAddress); + } + + maybeUpdateBufferPosition(buffer, position, bytesReceived); + return bytesReceived; + } + public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { + // This indirection isn't strictly necessary, but ensures that our public interface is type safe. + return recvfromBytes(fd, bytes, byteOffset, byteCount, flags, srcAddress); + } + private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException; + public native void remove(String path) throws ErrnoException; + public native void removexattr(String path, String name) throws ErrnoException; + public native void rename(String oldPath, String newPath) throws ErrnoException; + public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException; + public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { + final int bytesSent; + final int position = buffer.position(); + + if (buffer.isDirect()) { + bytesSent = sendtoBytes(fd, buffer, position, buffer.remaining(), flags, inetAddress, port); + } else { + bytesSent = sendtoBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining(), flags, inetAddress, port); + } + + maybeUpdateBufferPosition(buffer, position, bytesSent); + return bytesSent; + } + public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { + // This indirection isn't strictly necessary, but ensures that our public interface is type safe. + return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); + } + public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException { + return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, address); + } + private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException; + private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException; + public native void setegid(int egid) throws ErrnoException; + public native void setenv(String name, String value, boolean overwrite) throws ErrnoException; + public native void seteuid(int euid) throws ErrnoException; + public native void setgid(int gid) throws ErrnoException; + public native void setpgid(int pid, int pgid) throws ErrnoException; + public native void setregid(int rgid, int egid) throws ErrnoException; + public native void setreuid(int ruid, int euid) throws ErrnoException; + public native int setsid() throws ErrnoException; + public native void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException; + public native void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException; + public native void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException; + public native void setsockoptIpMreqn(FileDescriptor fd, int level, int option, int value) throws ErrnoException; + public native void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException; + public native void setsockoptGroupSourceReq(FileDescriptor fd, int level, int option, StructGroupSourceReq value) throws ErrnoException; + public native void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException; + public native void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException; + public native void setuid(int uid) throws ErrnoException; + public native void setxattr(String path, String name, byte[] value, int flags) throws ErrnoException; + public native void shutdown(FileDescriptor fd, int how) throws ErrnoException; + public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException; + public native void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException; + public native StructStat stat(String path) throws ErrnoException; + public native StructStatVfs statvfs(String path) throws ErrnoException; + public native String strerror(int errno); + public native String strsignal(int signal); + public native void symlink(String oldPath, String newPath) throws ErrnoException; + public native long sysconf(int name); + public native void tcdrain(FileDescriptor fd) throws ErrnoException; + public native void tcsendbreak(FileDescriptor fd, int duration) throws ErrnoException; + public int umask(int mask) { + if ((mask & 0777) != mask) { + throw new IllegalArgumentException("Invalid umask: " + mask); + } + return umaskImpl(mask); + } + private native int umaskImpl(int mask); + public native StructUtsname uname(); + public native void unlink(String pathname) throws ErrnoException; + public native void unsetenv(String name) throws ErrnoException; + public native int waitpid(int pid, MutableInt status, int options) throws ErrnoException; + public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { + final int bytesWritten; + final int position = buffer.position(); + if (buffer.isDirect()) { + bytesWritten = writeBytes(fd, buffer, position, buffer.remaining()); + } else { + bytesWritten = writeBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining()); + } + + maybeUpdateBufferPosition(buffer, position, bytesWritten); + return bytesWritten; + } + public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { + // This indirection isn't strictly necessary, but ensures that our public interface is type safe. + return writeBytes(fd, bytes, byteOffset, byteCount); + } + private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException; + public native int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException; + + private static void maybeUpdateBufferPosition(ByteBuffer buffer, int originalPosition, int bytesReadOrWritten) { + if (bytesReadOrWritten > 0) { + buffer.position(bytesReadOrWritten + originalPosition); + } + } +} diff --git a/luni/src/main/java/libcore/io/Memory.java b/luni/src/main/java/libcore/io/Memory.java index e1484575e..ba7398d77 100644 --- a/luni/src/main/java/libcore/io/Memory.java +++ b/luni/src/main/java/libcore/io/Memory.java @@ -17,6 +17,7 @@ package libcore.io; +import dalvik.annotation.optimization.FastNative; import java.io.FileDescriptor; import java.io.IOException; import java.nio.ByteBuffer; @@ -150,6 +151,7 @@ public static void pokeShort(byte[] dst, int offset, short value, ByteOrder orde */ public static native void memmove(Object dstObject, int dstOffset, Object srcObject, int srcOffset, long byteCount); + @FastNative public static native byte peekByte(long address); public static int peekInt(long address, boolean swap) { @@ -159,6 +161,7 @@ public static int peekInt(long address, boolean swap) { } return result; } + @FastNative private static native int peekIntNative(long address); public static long peekLong(long address, boolean swap) { @@ -168,6 +171,7 @@ public static long peekLong(long address, boolean swap) { } return result; } + @FastNative private static native long peekLongNative(long address); public static short peekShort(long address, boolean swap) { @@ -177,6 +181,7 @@ public static short peekShort(long address, boolean swap) { } return result; } + @FastNative private static native short peekShortNative(long address); public static native void peekByteArray(long address, byte[] dst, int dstOffset, int byteCount); @@ -187,6 +192,7 @@ public static short peekShort(long address, boolean swap) { public static native void peekLongArray(long address, long[] dst, int dstOffset, int longCount, boolean swap); public static native void peekShortArray(long address, short[] dst, int dstOffset, int shortCount, boolean swap); + @FastNative public static native void pokeByte(long address, byte value); public static void pokeInt(long address, int value, boolean swap) { @@ -195,6 +201,7 @@ public static void pokeInt(long address, int value, boolean swap) { } pokeIntNative(address, value); } + @FastNative private static native void pokeIntNative(long address, int value); public static void pokeLong(long address, long value, boolean swap) { @@ -203,6 +210,7 @@ public static void pokeLong(long address, long value, boolean swap) { } pokeLongNative(address, value); } + @FastNative private static native void pokeLongNative(long address, long value); public static void pokeShort(long address, short value, boolean swap) { @@ -211,6 +219,7 @@ public static void pokeShort(long address, short value, boolean swap) { } pokeShortNative(address, value); } + @FastNative private static native void pokeShortNative(long address, short value); public static native void pokeByteArray(long address, byte[] src, int offset, int count); diff --git a/luni/src/main/java/libcore/io/MemoryMappedFile.java b/luni/src/main/java/libcore/io/MemoryMappedFile.java index b4cd8fc50..4c736833a 100644 --- a/luni/src/main/java/libcore/io/MemoryMappedFile.java +++ b/luni/src/main/java/libcore/io/MemoryMappedFile.java @@ -28,20 +28,23 @@ import static android.system.OsConstants.*; /** - * A memory-mapped file. Use {@link #mmap} to map a file, {@link #close} to unmap a file, + * A memory-mapped file. Use {@link #mmapRO} to map a file, {@link #close} to unmap a file, * and either {@link #bigEndianIterator} or {@link #littleEndianIterator} to get a seekable - * {@link BufferIterator} over the mapped data. + * {@link BufferIterator} over the mapped data. This class is not thread safe. */ public final class MemoryMappedFile implements AutoCloseable { - private long address; - private final long size; + private boolean closed; + private final long address; + private final int size; - /** - * Use this if you've called {@code mmap} yourself. - */ + /** Public for layoutlib only. */ public MemoryMappedFile(long address, long size) { this.address = address; - this.size = size; + // For simplicity when bounds checking, only sizes up to Integer.MAX_VALUE are supported. + if (size < 0 || size > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Unsupported file size=" + size); + } + this.size = (int) size; } /** @@ -49,10 +52,13 @@ public MemoryMappedFile(long address, long size) { */ public static MemoryMappedFile mmapRO(String path) throws ErrnoException { FileDescriptor fd = Libcore.os.open(path, O_RDONLY, 0); - long size = Libcore.os.fstat(fd).st_size; - long address = Libcore.os.mmap(0L, size, PROT_READ, MAP_SHARED, fd, 0); - Libcore.os.close(fd); - return new MemoryMappedFile(address, size); + try { + long size = Libcore.os.fstat(fd).st_size; + long address = Libcore.os.mmap(0L, size, PROT_READ, MAP_SHARED, fd, 0); + return new MemoryMappedFile(address, size); + } finally { + Libcore.os.close(fd); + } } /** @@ -63,31 +69,45 @@ public static MemoryMappedFile mmapRO(String path) throws ErrnoException { * Calling this method invalidates any iterators over this {@code MemoryMappedFile}. It is an * error to use such an iterator after calling {@code close}. */ - public synchronized void close() throws ErrnoException { - if (address != 0) { + public void close() throws ErrnoException { + if (!closed) { + closed = true; Libcore.os.munmap(address, size); - address = 0; } } + public boolean isClosed() { + return closed; + } + /** * Returns a new iterator that treats the mapped data as big-endian. */ public BufferIterator bigEndianIterator() { - return new NioBufferIterator(address, (int) size, ByteOrder.nativeOrder() != ByteOrder.BIG_ENDIAN); + return new NioBufferIterator( + this, address, size, ByteOrder.nativeOrder() != ByteOrder.BIG_ENDIAN); } /** * Returns a new iterator that treats the mapped data as little-endian. */ public BufferIterator littleEndianIterator() { - return new NioBufferIterator(address, (int) size, ByteOrder.nativeOrder() != ByteOrder.LITTLE_ENDIAN); + return new NioBufferIterator( + this, this.address, this.size, ByteOrder.nativeOrder() != ByteOrder.LITTLE_ENDIAN); + } + + /** Throws {@link IllegalStateException} if the file is closed. */ + void checkNotClosed() { + if (closed) { + throw new IllegalStateException("MemoryMappedFile is closed"); + } } /** * Returns the size in bytes of the memory-mapped region. */ - public long size() { + public int size() { + checkNotClosed(); return size; } } diff --git a/luni/src/main/java/libcore/io/NioBufferIterator.java b/luni/src/main/java/libcore/io/NioBufferIterator.java index 3dd05a5a5..0f3f920cb 100644 --- a/luni/src/main/java/libcore/io/NioBufferIterator.java +++ b/luni/src/main/java/libcore/io/NioBufferIterator.java @@ -16,24 +16,37 @@ package libcore.io; -import libcore.io.Memory; - /** * Iterates over big- or little-endian bytes on the native heap. * See {@link MemoryMappedFile#bigEndianIterator} and {@link MemoryMappedFile#littleEndianIterator}. * - * @hide don't make this public without adding bounds checking. + * @hide */ public final class NioBufferIterator extends BufferIterator { + + private final MemoryMappedFile file; private final long address; - private final int size; + private final int length; private final boolean swap; private int position; - NioBufferIterator(long address, int size, boolean swap) { + NioBufferIterator(MemoryMappedFile file, long address, int length, boolean swap) { + file.checkNotClosed(); + + this.file = file; this.address = address; - this.size = size; + + if (length < 0) { + throw new IllegalArgumentException("length < 0"); + } + final long MAX_VALID_ADDRESS = -1; + if (Long.compareUnsigned(address, MAX_VALID_ADDRESS - length) > 0) { + throw new IllegalArgumentException( + "length " + length + " would overflow 64-bit address space"); + } + this.length = length; + this.swap = swap; } @@ -45,31 +58,78 @@ public void skip(int byteCount) { position += byteCount; } + @Override + public int pos() { + return position; + } + public void readByteArray(byte[] dst, int dstOffset, int byteCount) { + checkDstBounds(dstOffset, dst.length, byteCount); + file.checkNotClosed(); + checkReadBounds(position, length, byteCount); Memory.peekByteArray(address + position, dst, dstOffset, byteCount); position += byteCount; } public byte readByte() { + file.checkNotClosed(); + checkReadBounds(position, length, 1); byte result = Memory.peekByte(address + position); ++position; return result; } public int readInt() { + file.checkNotClosed(); + checkReadBounds(position, length, SizeOf.INT); int result = Memory.peekInt(address + position, swap); position += SizeOf.INT; return result; } public void readIntArray(int[] dst, int dstOffset, int intCount) { + checkDstBounds(dstOffset, dst.length, intCount); + file.checkNotClosed(); + final int byteCount = SizeOf.INT * intCount; + checkReadBounds(position, length, byteCount); Memory.peekIntArray(address + position, dst, dstOffset, intCount, swap); - position += SizeOf.INT * intCount; + position += byteCount; } public short readShort() { + file.checkNotClosed(); + checkReadBounds(position, length, SizeOf.SHORT); short result = Memory.peekShort(address + position, swap); position += SizeOf.SHORT; return result; } + + private static void checkReadBounds(int position, int length, int byteCount) { + if (position < 0 || byteCount < 0) { + throw new IndexOutOfBoundsException( + "Invalid read args: position=" + position + ", byteCount=" + byteCount); + } + // Use of int here relies on length being an int <= Integer.MAX_VALUE. + final int finalReadPos = position + byteCount; + if (finalReadPos < 0 || finalReadPos > length) { + throw new IndexOutOfBoundsException( + "Read outside range: position=" + position + ", byteCount=" + byteCount + + ", length=" + length); + } + } + + private static void checkDstBounds(int dstOffset, int dstLength, int count) { + if (dstOffset < 0 || count < 0) { + throw new IndexOutOfBoundsException( + "Invalid dst args: offset=" + dstLength + ", count=" + count); + } + // Use of int here relies on dstLength being an int <= Integer.MAX_VALUE, which it has to + // be because it's an array length. + final int targetPos = dstOffset + count; + if (targetPos < 0 || targetPos > dstLength) { + throw new IndexOutOfBoundsException( + "Write outside range: dst.length=" + dstLength + ", offset=" + + dstOffset + ", count=" + count); + } + } } diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java index 006a29eb7..20a84bd2a 100644 --- a/luni/src/main/java/libcore/io/Os.java +++ b/luni/src/main/java/libcore/io/Os.java @@ -19,9 +19,12 @@ import android.system.ErrnoException; import android.system.GaiException; import android.system.StructAddrinfo; +import android.system.StructCapUserData; +import android.system.StructCapUserHeader; import android.system.StructFlock; import android.system.StructGroupReq; import android.system.StructGroupSourceReq; +import android.system.StructIfaddrs; import android.system.StructLinger; import android.system.StructPasswd; import android.system.StructPollfd; @@ -46,6 +49,8 @@ public interface Os { public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException; public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; public void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException; + public StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException; + public void capset(StructCapUserHeader hdr, StructCapUserData[] data) throws ErrnoException; public void chmod(String path, int mode) throws ErrnoException; public void chown(String path, int uid, int gid) throws ErrnoException; public void close(FileDescriptor fd) throws ErrnoException; @@ -88,16 +93,21 @@ public interface Os { public StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException; public int gettid(); public int getuid(); - public int getxattr(String path, String name, byte[] outValue) throws ErrnoException; + public byte[] getxattr(String path, String name) throws ErrnoException; + public StructIfaddrs[] getifaddrs() throws ErrnoException; public String if_indextoname(int index); + public int if_nametoindex(String name); public InetAddress inet_pton(int family, String address); + public int ioctlFlags(FileDescriptor fd, String interfaceName) throws ErrnoException; public InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException; public int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException; + public int ioctlMTU(FileDescriptor fd, String interfaceName) throws ErrnoException; public boolean isatty(FileDescriptor fd); public void kill(int pid, int signal) throws ErrnoException; public void lchown(String path, int uid, int gid) throws ErrnoException; public void link(String oldPath, String newPath) throws ErrnoException; public void listen(FileDescriptor fd, int backlog) throws ErrnoException; + public String[] listxattr(String path) throws ErrnoException; public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException; public StructStat lstat(String path) throws ErrnoException; public void mincore(long address, long byteCount, byte[] vector) throws ErrnoException; diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java deleted file mode 100644 index a341641a5..000000000 --- a/luni/src/main/java/libcore/io/Posix.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package libcore.io; - -import android.system.ErrnoException; -import android.system.GaiException; -import android.system.StructAddrinfo; -import android.system.StructFlock; -import android.system.StructGroupReq; -import android.system.StructGroupSourceReq; -import android.system.StructLinger; -import android.system.StructPasswd; -import android.system.StructPollfd; -import android.system.StructStat; -import android.system.StructStatVfs; -import android.system.StructTimeval; -import android.system.StructUcred; -import android.system.StructUtsname; -import android.util.MutableInt; -import android.util.MutableLong; -import java.io.FileDescriptor; -import java.io.InterruptedIOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.nio.NioUtils; - -public final class Posix implements Os { - Posix() { } - - public native FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException; - public native boolean access(String path, int mode) throws ErrnoException; - public native InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException; - public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; - public native void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException; - public native void chmod(String path, int mode) throws ErrnoException; - public native void chown(String path, int uid, int gid) throws ErrnoException; - public native void close(FileDescriptor fd) throws ErrnoException; - public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; - public native void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException; - public native FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException; - public native FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException; - public native String[] environ(); - public native void execv(String filename, String[] argv) throws ErrnoException; - public native void execve(String filename, String[] argv, String[] envp) throws ErrnoException; - public native void fchmod(FileDescriptor fd, int mode) throws ErrnoException; - public native void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException; - public native int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException, InterruptedIOException; - public native int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException; - public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException; - public native void fdatasync(FileDescriptor fd) throws ErrnoException; - public native StructStat fstat(FileDescriptor fd) throws ErrnoException; - public native StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException; - public native void fsync(FileDescriptor fd) throws ErrnoException; - public native void ftruncate(FileDescriptor fd, long length) throws ErrnoException; - public native String gai_strerror(int error); - public native int getegid(); - public native int geteuid(); - public native int getgid(); - public native String getenv(String name); - public native String getnameinfo(InetAddress address, int flags) throws GaiException; - public native SocketAddress getpeername(FileDescriptor fd) throws ErrnoException; - public native int getpgid(int pid); - public native int getpid(); - public native int getppid(); - public native StructPasswd getpwnam(String name) throws ErrnoException; - public native StructPasswd getpwuid(int uid) throws ErrnoException; - public native SocketAddress getsockname(FileDescriptor fd) throws ErrnoException; - public native int getsockoptByte(FileDescriptor fd, int level, int option) throws ErrnoException; - public native InetAddress getsockoptInAddr(FileDescriptor fd, int level, int option) throws ErrnoException; - public native int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException; - public native StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException; - public native StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException; - public native StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException; - public native int gettid(); - public native int getuid(); - public native int getxattr(String path, String name, byte[] outValue) throws ErrnoException; - public native String if_indextoname(int index); - public native InetAddress inet_pton(int family, String address); - public native InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException; - public native int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException; - public native boolean isatty(FileDescriptor fd); - public native void kill(int pid, int signal) throws ErrnoException; - public native void lchown(String path, int uid, int gid) throws ErrnoException; - public native void link(String oldPath, String newPath) throws ErrnoException; - public native void listen(FileDescriptor fd, int backlog) throws ErrnoException; - public native long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException; - public native StructStat lstat(String path) throws ErrnoException; - public native void mincore(long address, long byteCount, byte[] vector) throws ErrnoException; - public native void mkdir(String path, int mode) throws ErrnoException; - public native void mkfifo(String path, int mode) throws ErrnoException; - public native void mlock(long address, long byteCount) throws ErrnoException; - public native long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException; - public native void msync(long address, long byteCount, int flags) throws ErrnoException; - public native void munlock(long address, long byteCount) throws ErrnoException; - public native void munmap(long address, long byteCount) throws ErrnoException; - public native FileDescriptor open(String path, int flags, int mode) throws ErrnoException; - public native FileDescriptor[] pipe2(int flags) throws ErrnoException; - public native int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException; - public native void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException; - public native int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException; - public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { - final int bytesRead; - final int position = buffer.position(); - - if (buffer.isDirect()) { - bytesRead = preadBytes(fd, buffer, position, buffer.remaining(), offset); - } else { - bytesRead = preadBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining(), offset); - } - - maybeUpdateBufferPosition(buffer, position, bytesRead); - return bytesRead; - } - public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { - // This indirection isn't strictly necessary, but ensures that our public interface is type safe. - return preadBytes(fd, bytes, byteOffset, byteCount, offset); - } - private native int preadBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException; - public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { - final int bytesWritten; - final int position = buffer.position(); - - if (buffer.isDirect()) { - bytesWritten = pwriteBytes(fd, buffer, position, buffer.remaining(), offset); - } else { - bytesWritten = pwriteBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining(), offset); - } - - maybeUpdateBufferPosition(buffer, position, bytesWritten); - return bytesWritten; - } - public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { - // This indirection isn't strictly necessary, but ensures that our public interface is type safe. - return pwriteBytes(fd, bytes, byteOffset, byteCount, offset); - } - private native int pwriteBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException; - public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { - final int bytesRead; - final int position = buffer.position(); - - if (buffer.isDirect()) { - bytesRead = readBytes(fd, buffer, position, buffer.remaining()); - } else { - bytesRead = readBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining()); - } - - maybeUpdateBufferPosition(buffer, position, bytesRead); - return bytesRead; - } - public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { - // This indirection isn't strictly necessary, but ensures that our public interface is type safe. - return readBytes(fd, bytes, byteOffset, byteCount); - } - private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException; - public native String readlink(String path) throws ErrnoException; - public native String realpath(String path) throws ErrnoException; - public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException; - public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { - final int bytesReceived; - final int position = buffer.position(); - - if (buffer.isDirect()) { - bytesReceived = recvfromBytes(fd, buffer, position, buffer.remaining(), flags, srcAddress); - } else { - bytesReceived = recvfromBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining(), flags, srcAddress); - } - - maybeUpdateBufferPosition(buffer, position, bytesReceived); - return bytesReceived; - } - public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { - // This indirection isn't strictly necessary, but ensures that our public interface is type safe. - return recvfromBytes(fd, bytes, byteOffset, byteCount, flags, srcAddress); - } - private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException; - public native void remove(String path) throws ErrnoException; - public native void removexattr(String path, String name) throws ErrnoException; - public native void rename(String oldPath, String newPath) throws ErrnoException; - public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException; - public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { - final int bytesSent; - final int position = buffer.position(); - - if (buffer.isDirect()) { - bytesSent = sendtoBytes(fd, buffer, position, buffer.remaining(), flags, inetAddress, port); - } else { - bytesSent = sendtoBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining(), flags, inetAddress, port); - } - - maybeUpdateBufferPosition(buffer, position, bytesSent); - return bytesSent; - } - public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { - // This indirection isn't strictly necessary, but ensures that our public interface is type safe. - return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); - } - public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException { - return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, address); - } - private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException; - private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException; - public native void setegid(int egid) throws ErrnoException; - public native void setenv(String name, String value, boolean overwrite) throws ErrnoException; - public native void seteuid(int euid) throws ErrnoException; - public native void setgid(int gid) throws ErrnoException; - public native void setpgid(int pid, int pgid) throws ErrnoException; - public native void setregid(int rgid, int egid) throws ErrnoException; - public native void setreuid(int ruid, int euid) throws ErrnoException; - public native int setsid() throws ErrnoException; - public native void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException; - public native void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException; - public native void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException; - public native void setsockoptIpMreqn(FileDescriptor fd, int level, int option, int value) throws ErrnoException; - public native void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException; - public native void setsockoptGroupSourceReq(FileDescriptor fd, int level, int option, StructGroupSourceReq value) throws ErrnoException; - public native void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException; - public native void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException; - public native void setuid(int uid) throws ErrnoException; - public native void setxattr(String path, String name, byte[] value, int flags) throws ErrnoException; - public native void shutdown(FileDescriptor fd, int how) throws ErrnoException; - public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException; - public native void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException; - public native StructStat stat(String path) throws ErrnoException; - public native StructStatVfs statvfs(String path) throws ErrnoException; - public native String strerror(int errno); - public native String strsignal(int signal); - public native void symlink(String oldPath, String newPath) throws ErrnoException; - public native long sysconf(int name); - public native void tcdrain(FileDescriptor fd) throws ErrnoException; - public native void tcsendbreak(FileDescriptor fd, int duration) throws ErrnoException; - public int umask(int mask) { - if ((mask & 0777) != mask) { - throw new IllegalArgumentException("Invalid umask: " + mask); - } - return umaskImpl(mask); - } - private native int umaskImpl(int mask); - public native StructUtsname uname(); - public native void unlink(String pathname) throws ErrnoException; - public native void unsetenv(String name) throws ErrnoException; - public native int waitpid(int pid, MutableInt status, int options) throws ErrnoException; - public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { - final int bytesWritten; - final int position = buffer.position(); - if (buffer.isDirect()) { - bytesWritten = writeBytes(fd, buffer, position, buffer.remaining()); - } else { - bytesWritten = writeBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + position, buffer.remaining()); - } - - maybeUpdateBufferPosition(buffer, position, bytesWritten); - return bytesWritten; - } - public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { - // This indirection isn't strictly necessary, but ensures that our public interface is type safe. - return writeBytes(fd, bytes, byteOffset, byteCount); - } - private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException; - public native int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException; - - private static void maybeUpdateBufferPosition(ByteBuffer buffer, int originalPosition, int bytesReadOrWritten) { - if (bytesReadOrWritten > 0) { - buffer.position(bytesReadOrWritten + originalPosition); - } - } -} diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java index 3b59b87dc..b746273d4 100644 --- a/luni/src/main/java/libcore/net/MimeUtils.java +++ b/luni/src/main/java/libcore/net/MimeUtils.java @@ -17,6 +17,7 @@ package libcore.net; import java.util.HashMap; +import java.util.Locale; import java.util.Map; /** @@ -210,6 +211,12 @@ public final class MimeUtils { add("application/x-xcf", "xcf"); add("application/x-xfig", "fig"); add("application/xhtml+xml", "xhtml"); + // Video mime types for 3GPP first so they'll be default for guessMimeTypeFromExtension + // See RFC 3839 for 3GPP and RFC 4393 for 3GPP2 + add("video/3gpp", "3gpp"); + add("video/3gpp", "3gp"); + add("video/3gpp2", "3gpp2"); + add("video/3gpp2", "3g2"); add("audio/3gpp", "3gpp"); add("audio/aac", "aac"); add("audio/aac-adts", "aac"); @@ -353,10 +360,6 @@ public final class MimeUtils { add("text/x-tex", "cls"); add("text/x-vcalendar", "vcs"); add("text/x-vcard", "vcf"); - add("video/3gpp", "3gpp"); - add("video/3gpp", "3gp"); - add("video/3gpp2", "3gpp2"); - add("video/3gpp2", "3g2"); add("video/avi", "avi"); add("video/dl", "dl"); add("video/dv", "dif"); @@ -407,52 +410,52 @@ private MimeUtils() { } /** - * Returns true if the given MIME type has an entry in the map. + * Returns true if the given case insensitive MIME type has an entry in the map. * @param mimeType A MIME type (i.e. text/plain) - * @return True iff there is a mimeType entry in the map. + * @return True if a extension has been registered for + * the given case insensitive MIME type. */ public static boolean hasMimeType(String mimeType) { - if (mimeType == null || mimeType.isEmpty()) { - return false; - } - return mimeTypeToExtensionMap.containsKey(mimeType); + return (guessExtensionFromMimeType(mimeType) != null); } /** - * Returns the MIME type for the given extension. + * Returns the MIME type for the given case insensitive file extension. * @param extension A file extension without the leading '.' - * @return The MIME type for the given extension or null iff there is none. + * @return The MIME type has been registered for + * the given case insensitive file extension or null if there is none. */ public static String guessMimeTypeFromExtension(String extension) { if (extension == null || extension.isEmpty()) { return null; } + extension = extension.toLowerCase(Locale.US); return extensionToMimeTypeMap.get(extension); } /** - * Returns true if the given extension has a registered MIME type. + * Returns true if the given case insensitive extension has a registered MIME type. * @param extension A file extension without the leading '.' - * @return True iff there is an extension entry in the map. + * @return True if a MIME type has been registered for + * the given case insensitive file extension. */ public static boolean hasExtension(String extension) { - if (extension == null || extension.isEmpty()) { - return false; - } - return extensionToMimeTypeMap.containsKey(extension); + return (guessMimeTypeFromExtension(extension) != null); } /** - * Returns the registered extension for the given MIME type. Note that some + * Returns the registered extension for the given case insensitive MIME type. Note that some * MIME types map to multiple extensions. This call will return the most * common extension for the given MIME type. * @param mimeType A MIME type (i.e. text/plain) - * @return The extension for the given MIME type or null iff there is none. + * @return The extension has been registered for + * the given case insensitive MIME type or null if there is none. */ public static String guessExtensionFromMimeType(String mimeType) { if (mimeType == null || mimeType.isEmpty()) { return null; } + mimeType = mimeType.toLowerCase(Locale.US); return mimeTypeToExtensionMap.get(mimeType); } } diff --git a/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java b/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java index 56b1b6a87..d9c87a417 100644 --- a/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java +++ b/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java @@ -71,6 +71,14 @@ public static void setInstance(NetworkSecurityPolicy policy) { */ public abstract boolean isCleartextTrafficPermitted(String hostname); + /** + * Returns {@code true} if Certificate Transparency information is required to be presented by + * the server and verified by the client in TLS connections to {@code hostname}. + * + *

See RFC6962 section 3.3 for more details. + */ + public abstract boolean isCertificateTransparencyVerificationRequired(String hostname); + public static final class DefaultNetworkSecurityPolicy extends NetworkSecurityPolicy { @Override public boolean isCleartextTrafficPermitted() { @@ -81,5 +89,10 @@ public boolean isCleartextTrafficPermitted() { public boolean isCleartextTrafficPermitted(String hostname) { return isCleartextTrafficPermitted(); } + + @Override + public boolean isCertificateTransparencyVerificationRequired(String hostname) { + return false; + } } } diff --git a/luni/src/main/java/libcore/reflect/AnnotatedElements.java b/luni/src/main/java/libcore/reflect/AnnotatedElements.java index 2fe2d2b25..2b4fb5ea2 100644 --- a/luni/src/main/java/libcore/reflect/AnnotatedElements.java +++ b/luni/src/main/java/libcore/reflect/AnnotatedElements.java @@ -32,45 +32,15 @@ */ public final class AnnotatedElements { /** - * Default implementation for {@link AnnotatedElement#getDeclaredAnnotation}. - * - * @return Directly present annotation of type {@code annotationClass} for {@code element}, - * or {@code null} if none was found. - */ - public static T getDeclaredAnnotation(AnnotatedElement element, - Class annotationClass) { - if (annotationClass == null) { - throw new NullPointerException("annotationClass"); - } - - Annotation[] annotations = element.getDeclaredAnnotations(); - - // Safeguard: getDeclaredAnnotations should never return null. - if (annotations == null) { - return null; - } - - // The annotation might be directly present: - // Return the first (and only) annotation whose class matches annotationClass. - for (int i = 0; i < annotations.length; ++i) { - if (annotationClass.isInstance(annotations[i])) { - return (T)annotations[i]; // Safe because of above guard. - } - } - - // The annotation was *not* directly present: - // If the array was empty, or we found no matches, return null. - return null; - } - - /** - * Default implementation for {@link AnnotatedElement#getDeclaredAnnotationsByType}. + * Default implementation of {@link AnnotatedElement#getDeclaredAnnotationsByType}, and + * {@link AnnotatedElement#getAnnotationsByType} for elements that do not support annotation + * inheritance. * * @return Directly/indirectly present list of annotations of type {@code annotationClass} for * {@code element}, or an empty array if none were found. */ - public static T[] getDeclaredAnnotationsByType(AnnotatedElement element, - Class annotationClass) { + public static T[] getDirectOrIndirectAnnotationsByType( + AnnotatedElement element, Class annotationClass) { if (annotationClass == null) { throw new NullPointerException("annotationClass"); } @@ -182,37 +152,7 @@ private static void insertAnnotationValues(Annotation ann return (repeatableAnnotation == null) ? null : repeatableAnnotation.value(); } - /** - * Default implementation of {@link AnnotatedElement#getAnnotationsByType}. - * - *

- * This method does not handle inherited annotations and is - * intended for use for {@code Method}, {@code Field}, {@code Package}. - * The {@link Class#getAnnotationsByType} is implemented explicitly. - *

- * - * @return Associated annotations of type {@code annotationClass} for {@code element}. - */ - public static T[] getAnnotationsByType(AnnotatedElement element, - Class annotationClass) { - if (annotationClass == null) { - throw new NullPointerException("annotationClass"); - } - - // Find any associated annotations [directly or repeatably (indirectly) present on this class]. - T[] annotations = element.getDeclaredAnnotationsByType(annotationClass); - if (annotations == null) { - throw new AssertionError("annotations must not be null"); // Internal error. - } - - // If nothing was found, we would look for associated annotations recursively up to the root - // class. However this can only happen if AnnotatedElement is a Class, which is handled - // in the Class override of this method. - return annotations; - } - private AnnotatedElements() { - throw new AssertionError("Instances of AnnotatedElements not allowed"); } } diff --git a/luni/src/main/java/libcore/util/CharsetUtils.java b/luni/src/main/java/libcore/util/CharsetUtils.java index 5163dbabb..bab6f53b2 100644 --- a/luni/src/main/java/libcore/util/CharsetUtils.java +++ b/luni/src/main/java/libcore/util/CharsetUtils.java @@ -16,6 +16,8 @@ package libcore.util; +import dalvik.annotation.optimization.FastNative; + /** * Various special-case charset conversions (for performance). * @@ -26,18 +28,21 @@ public final class CharsetUtils { * Returns a new byte array containing the bytes corresponding to the characters in the given * string, encoded in US-ASCII. Unrepresentable characters are replaced by (byte) '?'. */ + @FastNative public static native byte[] toAsciiBytes(String s, int offset, int length); /** * Returns a new byte array containing the bytes corresponding to the characters in the given * string, encoded in ISO-8859-1. Unrepresentable characters are replaced by (byte) '?'. */ + @FastNative public static native byte[] toIsoLatin1Bytes(String s, int offset, int length); /** * Returns a new byte array containing the bytes corresponding to the characters in the given * string, encoded in UTF-8. All characters are representable in UTF-8. */ + @FastNative public static native byte[] toUtf8Bytes(String s, int offset, int length); /** @@ -64,6 +69,7 @@ public static byte[] toBigEndianUtf16Bytes(String s, int offset, int length) { * value[i] = (ch <= 0x7f) ? ch : REPLACEMENT_CHAR; * } */ + @FastNative public static native void asciiBytesToChars(byte[] bytes, int offset, int length, char[] chars); /** @@ -73,6 +79,7 @@ public static byte[] toBigEndianUtf16Bytes(String s, int offset, int length) { * value[i] = (char) (data[start++] & 0xff); * } */ + @FastNative public static native void isoLatin1BytesToChars(byte[] bytes, int offset, int length, char[] chars); private CharsetUtils() { diff --git a/luni/src/main/java/libcore/util/TimeZoneDataFiles.java b/luni/src/main/java/libcore/util/TimeZoneDataFiles.java new file mode 100644 index 000000000..83613391a --- /dev/null +++ b/luni/src/main/java/libcore/util/TimeZoneDataFiles.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 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. + */ + +package libcore.util; + +/** + * Utility methods associated with finding updateable time zone data files. + */ +public final class TimeZoneDataFiles { + + private static final String ANDROID_ROOT_ENV = "ANDROID_ROOT"; + private static final String ANDROID_DATA_ENV = "ANDROID_DATA"; + + private TimeZoneDataFiles() {} + + // VisibleForTesting + public static String[] getTimeZoneFilePaths(String fileName) { + return new String[] { + getDataTimeZoneFile(fileName), + getSystemTimeZoneFile(fileName) + }; + } + + private static String getDataTimeZoneFile(String fileName) { + return System.getenv(ANDROID_DATA_ENV) + "/misc/zoneinfo/current/" + fileName; + } + + // VisibleForTesting + public static String getSystemTimeZoneFile(String fileName) { + return System.getenv(ANDROID_ROOT_ENV) + "/usr/share/zoneinfo/" + fileName; + } + + public static String generateIcuDataPath() { + StringBuilder icuDataPathBuilder = new StringBuilder(); + // ICU should first look in ANDROID_DATA. This is used for (optional) timezone data. + String dataIcuDataPath = getEnvironmentPath(ANDROID_DATA_ENV, "/misc/zoneinfo/current/icu"); + if (dataIcuDataPath != null) { + icuDataPathBuilder.append(dataIcuDataPath); + } + + // ICU should always look in ANDROID_ROOT. + String systemIcuDataPath = getEnvironmentPath(ANDROID_ROOT_ENV, "/usr/icu"); + if (systemIcuDataPath != null) { + if (icuDataPathBuilder.length() > 0) { + icuDataPathBuilder.append(":"); + } + icuDataPathBuilder.append(systemIcuDataPath); + } + return icuDataPathBuilder.toString(); + } + + /** + * Creates a path by combining the value of an environment variable with a relative path. + * Returns {@code null} if the environment variable is not set. + */ + private static String getEnvironmentPath(String environmentVariable, String path) { + String variable = System.getenv(environmentVariable); + if (variable == null) { + return null; + } + return variable + path; + } +} diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java index f9942218a..bbaf0f9c6 100644 --- a/luni/src/main/java/libcore/util/ZoneInfo.java +++ b/luni/src/main/java/libcore/util/ZoneInfo.java @@ -43,7 +43,7 @@ * reading the index and creating a {@link BufferIterator} that provides access to an entry for a * specific file. This class is responsible for reading the data from that {@link BufferIterator} * and storing it a representation to support the {@link TimeZone} and {@link GregorianCalendar} - * implementations. See {@link ZoneInfo#makeTimeZone(String, BufferIterator)}. + * implementations. See {@link ZoneInfo#readTimeZone(String, BufferIterator, long)}. * *

The main difference between {@code tzfile} and the compacted form is that the * {@code struct ttinfo} only uses a single byte for {@code tt_isdst} and {@code tt_abbrind}. @@ -115,8 +115,8 @@ public final class ZoneInfo extends TimeZone { * in the offset from UTC or a change in the DST. * *

These times are pre-calculated externally from a set of rules (both historical and - * future) and stored in a file from which {@link ZoneInfo#makeTimeZone(String, BufferIterator)} - * reads the data. That is quite different to {@link java.util.SimpleTimeZone}, which has + * future) and stored in a file from which {@link ZoneInfo#readTimeZone(String, BufferIterator, + * long)} reads the data. That is quite different to {@link java.util.SimpleTimeZone}, which has * essentially human readable rules (e.g. DST starts at 01:00 on the first Sunday in March and * ends at 01:00 on the last Sunday in October) that can be used to determine the DST transition * times across a number of years @@ -178,19 +178,14 @@ public final class ZoneInfo extends TimeZone { */ private final byte[] mIsDsts; - public static ZoneInfo makeTimeZone(String id, BufferIterator it) { - return makeTimeZone(id, it, System.currentTimeMillis()); - } - - /** - * Visible for testing. - */ - public static ZoneInfo makeTimeZone(String id, BufferIterator it, long currentTimeMillis) { + public static ZoneInfo readTimeZone(String id, BufferIterator it, long currentTimeMillis) + throws IOException { // Variable names beginning tzh_ correspond to those in "tzfile.h". // Check tzh_magic. - if (it.readInt() != 0x545a6966) { // "TZif" - return null; + int tzh_magic = it.readInt(); + if (tzh_magic != 0x545a6966) { // "TZif" + throw new IOException("Timezone id=" + id + " has an invalid header=" + tzh_magic); } // Skip the uninteresting part of the header. @@ -198,9 +193,22 @@ public static ZoneInfo makeTimeZone(String id, BufferIterator it, long currentTi // Read the sizes of the arrays we're about to read. int tzh_timecnt = it.readInt(); + // Arbitrary ceiling to prevent allocating memory for corrupt data. + // 2 per year with 2^32 seconds would give ~272 transitions. + final int MAX_TRANSITIONS = 2000; + if (tzh_timecnt < 0 || tzh_timecnt > MAX_TRANSITIONS) { + throw new IOException( + "Timezone id=" + id + " has an invalid number of transitions=" + tzh_timecnt); + } + int tzh_typecnt = it.readInt(); - if (tzh_typecnt > 256) { - throw new IllegalStateException(id + " has more than 256 different types"); + final int MAX_TYPES = 256; + if (tzh_typecnt < 1) { + throw new IOException("ZoneInfo requires at least one type " + + "to be provided for each timezone but could not find one for '" + id + "'"); + } else if (tzh_typecnt > MAX_TYPES) { + throw new IOException( + "Timezone with id " + id + " has too many types=" + tzh_typecnt); } it.skip(4); // Skip tzh_charcnt. @@ -217,20 +225,32 @@ public static ZoneInfo makeTimeZone(String id, BufferIterator it, long currentTi long[] transitions64 = new long[tzh_timecnt]; for (int i = 0; i < tzh_timecnt; ++i) { transitions64[i] = transitions32[i]; + if (i > 0 && transitions64[i] <= transitions64[i - 1]) { + throw new IOException( + id + " transition at " + i + " is not sorted correctly, is " + + transitions64[i] + ", previous is " + transitions64[i - 1]); + } } byte[] type = new byte[tzh_timecnt]; it.readByteArray(type, 0, type.length); + for (int i = 0; i < type.length; i++) { + int typeIndex = type[i] & 0xff; + if (typeIndex >= tzh_typecnt) { + throw new IOException( + id + " type at " + i + " is not < " + tzh_typecnt + ", is " + typeIndex); + } + } int[] gmtOffsets = new int[tzh_typecnt]; byte[] isDsts = new byte[tzh_typecnt]; for (int i = 0; i < tzh_typecnt; ++i) { gmtOffsets[i] = it.readInt(); - byte b = it.readByte(); - if (b != 0 && b != 1) { - throw new IllegalStateException(id + " dst at " + i + " is not 0 or 1, is " + b); + byte isDst = it.readByte(); + if (isDst != 0 && isDst != 1) { + throw new IOException(id + " dst at " + i + " is not 0 or 1, is " + isDst); } - isDsts[i] = b; + isDsts[i] = isDst; // We skip the abbreviation index. This would let us provide historically-accurate // time zone abbreviations (such as "AHST", "YST", and "AKST" for standard time in // America/Anchorage in 1982, 1983, and 1984 respectively). ICU only knows the current @@ -247,7 +267,7 @@ public static ZoneInfo makeTimeZone(String id, BufferIterator it, long currentTi private ZoneInfo(String name, long[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts, long currentTimeMillis) { if (gmtOffsets.length == 0) { - throw new IllegalStateException("ZoneInfo requires at least one offset " + throw new IllegalArgumentException("ZoneInfo requires at least one offset " + "to be provided for each timezone but could not find one for '" + name + "'"); } mTransitions = transitions; diff --git a/luni/src/main/java/libcore/util/ZoneInfoDB.java b/luni/src/main/java/libcore/util/ZoneInfoDB.java index 916ba290f..acb9c1230 100644 --- a/luni/src/main/java/libcore/util/ZoneInfoDB.java +++ b/luni/src/main/java/libcore/util/ZoneInfoDB.java @@ -17,6 +17,9 @@ package libcore.util; import android.system.ErrnoException; + +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -34,11 +37,30 @@ * @hide - used to implement TimeZone */ public final class ZoneInfoDB { + + // VisibleForTesting + public static final String TZDATA_FILE = "tzdata"; + private static final TzData DATA = - new TzData(System.getenv("ANDROID_DATA") + "/misc/zoneinfo/current/tzdata", - System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata"); + TzData.loadTzDataWithFallback(TimeZoneDataFiles.getTimeZoneFilePaths(TZDATA_FILE)); public static class TzData { + + // The database reserves 40 bytes for each id. + private static final int SIZEOF_TZNAME = 40; + + // The database uses 32-bit (4 byte) integers. + private static final int SIZEOF_TZINT = 4; + + // Each index entry takes up this number of bytes. + public static final int SIZEOF_INDEX_ENTRY = SIZEOF_TZNAME + 3 * SIZEOF_TZINT; + + /** + * {@code true} if {@link #close()} has been called meaning the instance cannot provide any + * data. + */ + private boolean closed; + /** * Rather than open, read, and close the big data file each time we look up a time zone, * we map the big data file during startup, and then just use the MemoryMappedFile. @@ -71,47 +93,85 @@ public static class TzData { new BasicLruCache(CACHE_SIZE) { @Override protected ZoneInfo create(String id) { - BufferIterator it = getBufferIterator(id); - if (it == null) { - return null; + try { + return makeTimeZoneUncached(id); + } catch (IOException e) { + throw new IllegalStateException("Unable to load timezone for ID=" + id, e); } - - return ZoneInfo.makeTimeZone(id, it); } }; - public TzData(String... paths) { + /** + * Loads the data at the specified paths in order, returning the first valid one as a + * {@link TzData} object. If there is no valid one found a basic fallback instance is created + * containing just GMT. + */ + public static TzData loadTzDataWithFallback(String... paths) { for (String path : paths) { - if (loadData(path)) { - return; + TzData tzData = new TzData(); + if (tzData.loadData(path)) { + return tzData; } } // We didn't find any usable tzdata on disk, so let's just hard-code knowledge of "GMT". // This is actually implemented in TimeZone itself, so if this is the only time zone // we report, we won't be asked any more questions. - System.logE("Couldn't find any tzdata!"); - version = "missing"; - zoneTab = "# Emergency fallback data.\n"; - ids = new String[] { "GMT" }; - byteOffsets = rawUtcOffsetsCache = new int[1]; + System.logE("Couldn't find any " + TZDATA_FILE + " file!"); + return TzData.createFallback(); + } + + /** + * Loads the data at the specified path and returns the {@link TzData} object if it is valid, + * otherwise {@code null}. + */ + public static TzData loadTzData(String path) { + TzData tzData = new TzData(); + if (tzData.loadData(path)) { + return tzData; + } + return null; + } + + private static TzData createFallback() { + TzData tzData = new TzData(); + tzData.populateFallback(); + return tzData; + } + + private TzData() { } /** * Visible for testing. */ public BufferIterator getBufferIterator(String id) { + checkNotClosed(); + // Work out where in the big data file this time zone is. int index = Arrays.binarySearch(ids, id); if (index < 0) { return null; } + int byteOffset = byteOffsets[index]; BufferIterator it = mappedFile.bigEndianIterator(); - it.skip(byteOffsets[index]); + it.skip(byteOffset); return it; } + private void populateFallback() { + version = "missing"; + zoneTab = "# Emergency fallback data.\n"; + ids = new String[] { "GMT" }; + byteOffsets = rawUtcOffsetsCache = new int[1]; + } + + /** + * Loads the data file at the specified path. If the data is valid {@code true} will be + * returned and the {@link TzData} instance can be used. If {@code false} is returned then the + * TzData instance is left in a closed state and must be discarded. + */ private boolean loadData(String path) { try { mappedFile = MemoryMappedFile.mmapRO(path); @@ -122,34 +182,56 @@ private boolean loadData(String path) { readHeader(); return true; } catch (Exception ex) { + close(); + // Something's wrong with the file. // Log the problem and return false so we try the next choice. - System.logE("tzdata file \"" + path + "\" was present but invalid!", ex); + System.logE(TZDATA_FILE + " file \"" + path + "\" was present but invalid!", ex); return false; } } - private void readHeader() { + private void readHeader() throws IOException { // byte[12] tzdata_version -- "tzdata2012f\0" // int index_offset // int data_offset // int zonetab_offset BufferIterator it = mappedFile.bigEndianIterator(); - byte[] tzdata_version = new byte[12]; - it.readByteArray(tzdata_version, 0, tzdata_version.length); - String magic = new String(tzdata_version, 0, 6, StandardCharsets.US_ASCII); - if (!magic.equals("tzdata") || tzdata_version[11] != 0) { - throw new RuntimeException("bad tzdata magic: " + Arrays.toString(tzdata_version)); - } - version = new String(tzdata_version, 6, 5, StandardCharsets.US_ASCII); + try { + byte[] tzdata_version = new byte[12]; + it.readByteArray(tzdata_version, 0, tzdata_version.length); + String magic = new String(tzdata_version, 0, 6, StandardCharsets.US_ASCII); + if (!magic.equals("tzdata") || tzdata_version[11] != 0) { + throw new IOException("bad tzdata magic: " + Arrays.toString(tzdata_version)); + } + version = new String(tzdata_version, 6, 5, StandardCharsets.US_ASCII); + + final int fileSize = mappedFile.size(); + int index_offset = it.readInt(); + validateOffset(index_offset, fileSize); + int data_offset = it.readInt(); + validateOffset(data_offset, fileSize); + int zonetab_offset = it.readInt(); + validateOffset(zonetab_offset, fileSize); + + if (index_offset >= data_offset || data_offset >= zonetab_offset) { + throw new IOException("Invalid offset: index_offset=" + index_offset + + ", data_offset=" + data_offset + ", zonetab_offset=" + zonetab_offset + + ", fileSize=" + fileSize); + } - int index_offset = it.readInt(); - int data_offset = it.readInt(); - int zonetab_offset = it.readInt(); + readIndex(it, index_offset, data_offset); + readZoneTab(it, zonetab_offset, fileSize - zonetab_offset); + } catch (IndexOutOfBoundsException e) { + throw new IOException("Invalid read from data file", e); + } + } - readIndex(it, index_offset, data_offset); - readZoneTab(it, zonetab_offset, (int) mappedFile.size() - zonetab_offset); + private static void validateOffset(int offset, int size) throws IOException { + if (offset < 0 || offset >= size) { + throw new IOException("Invalid offset=" + offset + ", size=" + size); + } } private void readZoneTab(BufferIterator it, int zoneTabOffset, int zoneTabSize) { @@ -159,62 +241,79 @@ private void readZoneTab(BufferIterator it, int zoneTabOffset, int zoneTabSize) zoneTab = new String(bytes, 0, bytes.length, StandardCharsets.US_ASCII); } - private void readIndex(BufferIterator it, int indexOffset, int dataOffset) { + private void readIndex(BufferIterator it, int indexOffset, int dataOffset) throws IOException { it.seek(indexOffset); - // The database reserves 40 bytes for each id. - final int SIZEOF_TZNAME = 40; - // The database uses 32-bit (4 byte) integers. - final int SIZEOF_TZINT = 4; - byte[] idBytes = new byte[SIZEOF_TZNAME]; int indexSize = (dataOffset - indexOffset); - int entryCount = indexSize / (SIZEOF_TZNAME + 3*SIZEOF_TZINT); - - char[] idChars = new char[entryCount * SIZEOF_TZNAME]; - int[] idEnd = new int[entryCount]; - int idOffset = 0; + if (indexSize % SIZEOF_INDEX_ENTRY != 0) { + throw new IOException("Index size is not divisible by " + SIZEOF_INDEX_ENTRY + + ", indexSize=" + indexSize); + } + int entryCount = indexSize / SIZEOF_INDEX_ENTRY; byteOffsets = new int[entryCount]; + ids = new String[entryCount]; for (int i = 0; i < entryCount; i++) { + // Read the fixed length timezone ID. it.readByteArray(idBytes, 0, idBytes.length); + // Read the offset into the file where the data for ID can be found. byteOffsets[i] = it.readInt(); - byteOffsets[i] += dataOffset; // TODO: change the file format so this is included. + byteOffsets[i] += dataOffset; int length = it.readInt(); if (length < 44) { - throw new AssertionError("length in index file < sizeof(tzhead)"); + throw new IOException("length in index file < sizeof(tzhead)"); } it.skip(4); // Skip the unused 4 bytes that used to be the raw offset. - // Don't include null chars in the String - int len = idBytes.length; - for (int j = 0; j < len; j++) { - if (idBytes[j] == 0) { - break; + // Calculate the true length of the ID. + int len = 0; + while (idBytes[len] != 0 && len < idBytes.length) { + len++; + } + if (len == 0) { + throw new IOException("Invalid ID at index=" + i); + } + ids[i] = new String(idBytes, 0, len, StandardCharsets.US_ASCII); + if (i > 0) { + if (ids[i].compareTo(ids[i - 1]) <= 0) { + throw new IOException("Index not sorted or contains multiple entries with the same ID" + + ", index=" + i + ", ids[i]=" + ids[i] + ", ids[i - 1]=" + ids[i - 1]); } - idChars[idOffset++] = (char) (idBytes[j] & 0xFF); } + } + } - idEnd[i] = idOffset; + public void validate() throws IOException { + checkNotClosed(); + // Validate the data in the tzdata file by loading each and every zone. + for (String id : getAvailableIDs()) { + ZoneInfo zoneInfo = makeTimeZoneUncached(id); + if (zoneInfo == null) { + throw new IOException("Unable to find data for ID=" + id); + } } + } - // We create one string containing all the ids, and then break that into substrings. - // This way, all ids share a single char[] on the heap. - String allIds = new String(idChars, 0, idOffset); - ids = new String[entryCount]; - for (int i = 0; i < entryCount; i++) { - ids[i] = allIds.substring(i == 0 ? 0 : idEnd[i - 1], idEnd[i]); + ZoneInfo makeTimeZoneUncached(String id) throws IOException { + BufferIterator it = getBufferIterator(id); + if (it == null) { + return null; } + + return ZoneInfo.readTimeZone(id, it, System.currentTimeMillis()); } public String[] getAvailableIDs() { + checkNotClosed(); return ids.clone(); } public String[] getAvailableIDs(int rawUtcOffset) { + checkNotClosed(); List matches = new ArrayList(); int[] rawUtcOffsets = getRawUtcOffsets(); for (int i = 0; i < rawUtcOffsets.length; ++i) { @@ -242,28 +341,83 @@ private synchronized int[] getRawUtcOffsets() { } public String getVersion() { + checkNotClosed(); return version; } public String getZoneTab() { + checkNotClosed(); return zoneTab; } public ZoneInfo makeTimeZone(String id) throws IOException { + checkNotClosed(); ZoneInfo zoneInfo = cache.get(id); // The object from the cache is cloned because TimeZone / ZoneInfo are mutable. return zoneInfo == null ? null : (ZoneInfo) zoneInfo.clone(); } public boolean hasTimeZone(String id) throws IOException { + checkNotClosed(); return cache.get(id) != null; } + public void close() { + if (!closed) { + closed = true; + + // Clear state that takes up appreciable heap. + ids = null; + byteOffsets = null; + rawUtcOffsetsCache = null; + mappedFile = null; + cache.evictAll(); + + // Remove the mapped file (if needed). + if (mappedFile != null) { + try { + mappedFile.close(); + } catch (ErrnoException ignored) { + } + } + } + } + + private void checkNotClosed() throws IllegalStateException { + if (closed) { + throw new IllegalStateException("TzData is closed"); + } + } + @Override protected void finalize() throws Throwable { - if (mappedFile != null) { - mappedFile.close(); + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Returns the String describing the IANA version of the rules contained in the specified TzData + * file. This method just reads the header of the file, and so is less expensive than mapping + * the whole file into memory (and provides no guarantees about validity). + */ + public static String getRulesVersion(File tzDataFile) throws IOException { + try (FileInputStream is = new FileInputStream(tzDataFile)) { + + final int bytesToRead = 12; + byte[] tzdataVersion = new byte[bytesToRead]; + int bytesRead = is.read(tzdataVersion, 0, bytesToRead); + if (bytesRead != bytesToRead) { + throw new IOException("File too short: only able to read " + bytesRead + " bytes."); + } + + String magic = new String(tzdataVersion, 0, 6, StandardCharsets.US_ASCII); + if (!magic.equals("tzdata") || tzdataVersion[11] != 0) { + throw new IOException("bad tzdata magic: " + Arrays.toString(tzdataVersion)); + } + return new String(tzdataVersion, 6, 5, StandardCharsets.US_ASCII); } - super.finalize(); } } diff --git a/luni/src/main/java/org/apache/harmony/xml/dom/DOMConfigurationImpl.java b/luni/src/main/java/org/apache/harmony/xml/dom/DOMConfigurationImpl.java index 672477651..0eda8f04b 100644 --- a/luni/src/main/java/org/apache/harmony/xml/dom/DOMConfigurationImpl.java +++ b/luni/src/main/java/org/apache/harmony/xml/dom/DOMConfigurationImpl.java @@ -357,6 +357,10 @@ public Object getParameter(String name) throws DOMException { } public DOMStringList getParameterNames() { + return internalGetParameterNames(); + } + + private static DOMStringList internalGetParameterNames() { final String[] result = PARAMETERS.keySet().toArray(new String[PARAMETERS.size()]); return new DOMStringList() { public String item(int index) { diff --git a/luni/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java b/luni/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java index e1b62fa0c..e4002eaa1 100644 --- a/luni/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java +++ b/luni/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java @@ -56,7 +56,6 @@ public final class DocumentImpl extends InnerNodeImpl implements Document { */ private String documentUri; private String inputEncoding; - private String xmlEncoding; private String xmlVersion = "1.0"; private boolean xmlStandalone = false; private boolean strictErrorChecking = true; @@ -437,7 +436,7 @@ public String getInputEncoding() { } public String getXmlEncoding() { - return xmlEncoding; + return null; } public boolean getXmlStandalone() { diff --git a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java index 040a0128d..4f54fb55c 100644 --- a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java +++ b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java @@ -129,11 +129,12 @@ public Document parse(InputSource source) throws SAXException, IOException { parser.require(XmlPullParser.END_DOCUMENT, null, null); } catch (XmlPullParserException ex) { - if (ex.getDetail() instanceof IOException) { - throw (IOException) ex.getDetail(); + Throwable detail = ex.getDetail(); + if (detail instanceof IOException) { + throw (IOException) detail; } - if (ex.getDetail() instanceof RuntimeException) { - throw (RuntimeException) ex.getDetail(); + if (detail instanceof RuntimeException) { + throw (RuntimeException) detail; } LocatorImpl locator = new LocatorImpl(); diff --git a/luni/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java b/luni/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java index c4ff069b2..39dd367fb 100644 --- a/luni/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java +++ b/luni/src/main/java/org/xml/sax/helpers/XMLReaderFactory.java @@ -126,9 +126,12 @@ public static XMLReader createXMLReader () in = loader.getResourceAsStream (service); if (in != null) { - reader = new BufferedReader (new InputStreamReader (in, StandardCharsets.UTF_8)); - className = reader.readLine (); - in.close (); + try { + reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + className = reader.readLine(); + } finally { + in.close(); // may throw IOException + } } } catch (Exception e) { } diff --git a/luni/src/main/native/ExecStrings.cpp b/luni/src/main/native/ExecStrings.cpp index a6a62e21b..9a408fd65 100644 --- a/luni/src/main/native/ExecStrings.cpp +++ b/luni/src/main/native/ExecStrings.cpp @@ -20,7 +20,8 @@ #include -#include "cutils/log.h" +#include + #include "ScopedLocalRef.h" ExecStrings::ExecStrings(JNIEnv* env, jobjectArray java_string_array) diff --git a/luni/src/main/native/IcuUtilities.cpp b/luni/src/main/native/IcuUtilities.cpp index 98648a5f3..6b29e670c 100644 --- a/luni/src/main/native/IcuUtilities.cpp +++ b/luni/src/main/native/IcuUtilities.cpp @@ -16,16 +16,17 @@ #define LOG_TAG "IcuUtilities" +#include + #include "IcuUtilities.h" #include "JniConstants.h" #include "JniException.h" #include "ScopedLocalRef.h" #include "ScopedUtfChars.h" -#include "cutils/log.h" #include "unicode/strenum.h" -#include "unicode/uloc.h" #include "unicode/ustring.h" +#include "unicode/uloc.h" jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, icu::StringEnumeration* se) { if (maybeThrowIcuException(env, provider, status)) { diff --git a/luni/src/main/native/NetFd.h b/luni/src/main/native/NetFd.h index 235b0577a..0397e4d46 100644 --- a/luni/src/main/native/NetFd.h +++ b/luni/src/main/native/NetFd.h @@ -17,6 +17,8 @@ #ifndef NET_FD_H_included #define NET_FD_H_included +#include "JNIHelp.h" + /** * Wraps access to the int inside a java.io.FileDescriptor, taking care of throwing exceptions. */ diff --git a/luni/src/main/native/NetworkUtilities.cpp b/luni/src/main/native/NetworkUtilities.cpp index b285a0133..bf438fac1 100644 --- a/luni/src/main/native/NetworkUtilities.cpp +++ b/luni/src/main/native/NetworkUtilities.cpp @@ -140,8 +140,12 @@ static bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, so jbyte* dst = reinterpret_cast(&sin6.sin6_addr.s6_addr); env->GetByteArrayRegion(addressBytes.get(), 0, 16, dst); // ...and set the scope id... - static jfieldID scopeFid = env->GetFieldID(JniConstants::inet6AddressClass, "scope_id", "I"); - sin6.sin6_scope_id = env->GetIntField(inetAddress, scopeFid); + static jfieldID holder6Fid = env->GetFieldID(JniConstants::inet6AddressClass, + "holder6", + "Ljava/net/Inet6Address$Inet6AddressHolder;"); + ScopedLocalRef holder6(env, env->GetObjectField(inetAddress, holder6Fid)); + static jfieldID scopeFid = env->GetFieldID(JniConstants::inet6AddressHolderClass, "scope_id", "I"); + sin6.sin6_scope_id = env->GetIntField(holder6.get(), scopeFid); sa_len = sizeof(sockaddr_in6); return true; } diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp index b099a4e1c..f642211c4 100644 --- a/luni/src/main/native/Register.cpp +++ b/luni/src/main/native/Register.cpp @@ -16,12 +16,13 @@ #define LOG_TAG "libcore" // We'll be next to "dalvikvm" in the log; make the distinction clear. -#include "cutils/log.h" +#include + +#include "log/log.h" + #include "JniConstants.h" #include "ScopedLocalFrame.h" -#include - // DalvikVM calls this on startup, so we can statically register all our native methods. jint JNI_OnLoad(JavaVM* vm, void*) { JNIEnv* env; @@ -35,6 +36,7 @@ jint JNI_OnLoad(JavaVM* vm, void*) { #define REGISTER(FN) extern void FN(JNIEnv*); FN(env) REGISTER(register_android_system_OsConstants); // REGISTER(register_java_lang_StringToReal); + REGISTER(register_java_lang_invoke_MethodHandle); REGISTER(register_java_math_NativeBN); REGISTER(register_java_util_regex_Matcher); REGISTER(register_java_util_regex_Pattern); @@ -42,8 +44,8 @@ jint JNI_OnLoad(JavaVM* vm, void*) { REGISTER(register_libcore_icu_NativeConverter); REGISTER(register_libcore_icu_TimeZoneNames); REGISTER(register_libcore_io_AsynchronousCloseMonitor); + REGISTER(register_libcore_io_Linux); REGISTER(register_libcore_io_Memory); - REGISTER(register_libcore_io_Posix); REGISTER(register_libcore_util_NativeAllocationRegistry); REGISTER(register_org_apache_harmony_dalvik_NativeTestTarget); REGISTER(register_org_apache_harmony_xml_ExpatParser); @@ -52,3 +54,20 @@ jint JNI_OnLoad(JavaVM* vm, void*) { return JNI_VERSION_1_6; } + +// DalvikVM calls this on shutdown, do any global cleanup here. +// -- Very important if we restart multiple DalvikVMs in the same process to reset the state. +void JNI_OnUnload(JavaVM* vm, void*) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + ALOGE("JavaVM::GetEnv() failed"); + abort(); + } + ALOGV("libjavacore JNI_OnUnload"); + + ScopedLocalFrame localFrame(env); + +#define UNREGISTER(FN) extern void FN(JNIEnv*); FN(env) + UNREGISTER(unregister_libcore_icu_ICU); +#undef UNREGISTER +} diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp index 1293fe776..3ae4af6cf 100644 --- a/luni/src/main/native/android_system_OsConstants.cpp +++ b/luni/src/main/native/android_system_OsConstants.cpp @@ -23,7 +23,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -242,6 +244,10 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) { initConstant(env, c, "F_SETOWN", F_SETOWN); initConstant(env, c, "F_UNLCK", F_UNLCK); initConstant(env, c, "F_WRLCK", F_WRLCK); + initConstant(env, c, "ICMP_ECHO", ICMP_ECHO); + initConstant(env, c, "ICMP_ECHOREPLY", ICMP_ECHOREPLY); + initConstant(env, c, "ICMP6_ECHO_REQUEST", ICMP6_ECHO_REQUEST); + initConstant(env, c, "ICMP6_ECHO_REPLY", ICMP6_ECHO_REPLY); #if defined(IFA_F_DADFAILED) initConstant(env, c, "IFA_F_DADFAILED", IFA_F_DADFAILED); #endif @@ -329,12 +335,16 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) { #endif initConstant(env, c, "IPV6_UNICAST_HOPS", IPV6_UNICAST_HOPS); initConstant(env, c, "IPV6_V6ONLY", IPV6_V6ONLY); + initConstant(env, c, "IP_MULTICAST_ALL", IP_MULTICAST_ALL); initConstant(env, c, "IP_MULTICAST_IF", IP_MULTICAST_IF); initConstant(env, c, "IP_MULTICAST_LOOP", IP_MULTICAST_LOOP); initConstant(env, c, "IP_MULTICAST_TTL", IP_MULTICAST_TTL); initConstant(env, c, "IP_RECVTOS", IP_RECVTOS); initConstant(env, c, "IP_TOS", IP_TOS); initConstant(env, c, "IP_TTL", IP_TTL); +#if defined(_LINUX_CAPABILITY_VERSION_3) + initConstant(env, c, "_LINUX_CAPABILITY_VERSION_3", _LINUX_CAPABILITY_VERSION_3); +#endif initConstant(env, c, "MAP_FIXED", MAP_FIXED); initConstant(env, c, "MAP_POPULATE", MAP_POPULATE); initConstant(env, c, "MAP_PRIVATE", MAP_PRIVATE); @@ -399,6 +409,12 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) { initConstant(env, c, "POLLRDNORM", POLLRDNORM); initConstant(env, c, "POLLWRBAND", POLLWRBAND); initConstant(env, c, "POLLWRNORM", POLLWRNORM); +#if defined(PR_CAP_AMBIENT) + initConstant(env, c, "PR_CAP_AMBIENT", PR_CAP_AMBIENT); +#endif +#if defined(PR_CAP_AMBIENT_RAISE) + initConstant(env, c, "PR_CAP_AMBIENT_RAISE", PR_CAP_AMBIENT_RAISE); +#endif #if defined(PR_GET_DUMPABLE) initConstant(env, c, "PR_GET_DUMPABLE", PR_GET_DUMPABLE); #endif @@ -496,6 +512,9 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) { #endif initConstant(env, c, "SO_BROADCAST", SO_BROADCAST); initConstant(env, c, "SO_DEBUG", SO_DEBUG); +#if defined(SO_DOMAIN) + initConstant(env, c, "SO_DOMAIN", SO_DOMAIN); +#endif initConstant(env, c, "SO_DONTROUTE", SO_DONTROUTE); initConstant(env, c, "SO_ERROR", SO_ERROR); initConstant(env, c, "SO_KEEPALIVE", SO_KEEPALIVE); @@ -506,6 +525,9 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) { #endif #if defined(SO_PEERCRED) initConstant(env, c, "SO_PEERCRED", SO_PEERCRED); +#endif +#if defined(SO_PROTOCOL) + initConstant(env, c, "SO_PROTOCOL", SO_PROTOCOL); #endif initConstant(env, c, "SO_RCVBUF", SO_RCVBUF); initConstant(env, c, "SO_RCVLOWAT", SO_RCVLOWAT); @@ -551,6 +573,9 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) { initConstant(env, c, "S_IXOTH", S_IXOTH); initConstant(env, c, "S_IXUSR", S_IXUSR); initConstant(env, c, "TCP_NODELAY", TCP_NODELAY); +#if defined(TCP_USER_TIMEOUT) + initConstant(env, c, "TCP_USER_TIMEOUT", TCP_USER_TIMEOUT); +#endif initConstant(env, c, "TIOCOUTQ", TIOCOUTQ); // UNIX_PATH_MAX is mentioned in some versions of unix(7), but not actually declared. initConstant(env, c, "UNIX_PATH_MAX", sizeof(sockaddr_un::sun_path)); diff --git a/luni/src/main/native/canonicalize_path.cpp b/luni/src/main/native/canonicalize_path.cpp deleted file mode 100644 index b2a2a01cc..000000000 --- a/luni/src/main/native/canonicalize_path.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2003 Constantin S. Svintsoff - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the authors may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "readlink.h" - -#include - -#include -#include -#include -#include - -/** - * This differs from realpath(3) mainly in its behavior when a path element does not exist or can - * not be searched. realpath(3) treats that as an error and gives up, but we have Java-compatible - * behavior where we just assume the path element was not a symbolic link. This leads to a textual - * treatment of ".." from that point in the path, which may actually lead us back to a path we - * can resolve (as in "/tmp/does-not-exist/../blah.txt" which would be an error for realpath(3) - * but "/tmp/blah.txt" under the traditional Java interpretation). - * - * This implementation also removes all the fixed-length buffers of the C original. - */ -bool canonicalize_path(const char* path, std::string& resolved) { - // 'path' must be an absolute path. - if (path[0] != '/') { - errno = EINVAL; - return false; - } - - resolved = "/"; - if (path[1] == '\0') { - return true; - } - - // Iterate over path components in 'left'. - int symlinkCount = 0; - std::string left(path + 1); - while (!left.empty()) { - // Extract the next path component. - size_t nextSlash = left.find('/'); - std::string nextPathComponent = left.substr(0, nextSlash); - if (nextSlash != std::string::npos) { - left.erase(0, nextSlash + 1); - } else { - left.clear(); - } - if (nextPathComponent.empty()) { - continue; - } else if (nextPathComponent == ".") { - continue; - } else if (nextPathComponent == "..") { - // Strip the last path component except when we have single "/". - if (resolved.size() > 1) { - resolved.erase(resolved.rfind('/')); - } - continue; - } - - // Append the next path component. - if (resolved[resolved.size() - 1] != '/') { - resolved += '/'; - } - resolved += nextPathComponent; - - // See if we've got a symbolic link, and resolve it if so. - struct stat sb; - if (lstat(resolved.c_str(), &sb) == 0 && S_ISLNK(sb.st_mode)) { - if (symlinkCount++ > MAXSYMLINKS) { - errno = ELOOP; - return false; - } - - std::string symlink; - if (!readlink(resolved.c_str(), symlink)) { - return false; - } - if (symlink[0] == '/') { - // The symbolic link is absolute, so we need to start from scratch. - resolved = "/"; - } else if (resolved.size() > 1) { - // The symbolic link is relative, so we just lose the last path component (which - // was the link). - resolved.erase(resolved.rfind('/')); - } - - if (!left.empty()) { - const char* maybeSlash = (symlink[symlink.size() - 1] != '/') ? "/" : ""; - left = symlink + maybeSlash + left; - } else { - left = symlink; - } - } - } - - // Remove trailing slash except when the resolved pathname is a single "/". - if (resolved.size() > 1 && resolved[resolved.size() - 1] == '/') { - resolved.erase(resolved.size() - 1, 1); - } - return true; -} diff --git a/luni/src/main/native/java_lang_StringToReal.cpp b/luni/src/main/native/java_lang_StringToReal.cpp index d1902af4c..c3217026f 100644 --- a/luni/src/main/native/java_lang_StringToReal.cpp +++ b/luni/src/main/native/java_lang_StringToReal.cpp @@ -286,6 +286,7 @@ static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, free(y); free(D); free(D2); + y = D = D2 = NULL; if (e >= 0 && k >= 0) { @@ -713,6 +714,7 @@ static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, j free(y); free(D); free(D2); + y = D = D2 = NULL; if (e >= 0 && k >= 0) { diff --git a/luni/src/main/native/java_lang_invoke_MethodHandle.cpp b/luni/src/main/native/java_lang_invoke_MethodHandle.cpp new file mode 100644 index 000000000..4596b420b --- /dev/null +++ b/luni/src/main/native/java_lang_invoke_MethodHandle.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "JniConstants.h" +#include "JNIHelp.h" + +static void MethodHandle_invokeExact(JNIEnv* env, jobject, jobjectArray) { + jniThrowException(env, "java/lang/UnsupportedOperationException", + "MethodHandle.invokeExact cannot be invoked reflectively."); +} + +static void MethodHandle_invoke(JNIEnv* env, jobject, jobjectArray) { + jniThrowException(env, "java/lang/UnsupportedOperationException", + "MethodHandle.invoke cannot be invoked reflectively."); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(MethodHandle, invokeExact, "([Ljava/lang/Object;)Ljava/lang/Object;"), + NATIVE_METHOD(MethodHandle, invoke, "([Ljava/lang/Object;)Ljava/lang/Object;"), +}; + +void register_java_lang_invoke_MethodHandle(JNIEnv* env) { + jniRegisterNativeMethods(env, "java/lang/invoke/MethodHandle", gMethods, NELEM(gMethods)); +} diff --git a/luni/src/main/native/java_math_NativeBN.cpp b/luni/src/main/native/java_math_NativeBN.cpp index 2bbbf6587..5c42be624 100644 --- a/luni/src/main/native/java_math_NativeBN.cpp +++ b/luni/src/main/native/java_math_NativeBN.cpp @@ -26,20 +26,9 @@ #include #include #include +#include #include -#if defined(OPENSSL_IS_BORINGSSL) -/* BoringSSL no longer exports |bn_check_top|. */ -static void bn_check_top(const BIGNUM* bn) { - /* This asserts that |bn->top| (which contains the number of elements of - * |bn->d| that are valid) is minimal. In other words, that there aren't - * superfluous zeros. */ - if (bn != NULL && bn->top != 0 && bn->d[bn->top-1] == 0) { - abort(); - } -} -#endif - struct BN_CTX_Deleter { void operator()(BN_CTX* p) const { BN_CTX_free(p); @@ -51,11 +40,18 @@ static BIGNUM* toBigNum(jlong address) { return reinterpret_cast(static_cast(address)); } -static bool throwExceptionIfNecessary(JNIEnv* env) { +static void throwException(JNIEnv* env) { long error = ERR_get_error(); + // OpenSSL's error queue may contain multiple errors. Clean up after them. + ERR_clear_error(); + if (error == 0) { - return false; + // An operation failed but did not push to the error queue. Throw a default + // exception. + jniThrowException(env, "java/lang/ArithmeticException", "Operation failed"); + return; } + char message[256]; ERR_error_string_n(error, message, sizeof(message)); int reason = ERR_GET_REASON(error); @@ -68,7 +64,6 @@ static bool throwExceptionIfNecessary(JNIEnv* env) { } else { jniThrowException(env, "java/lang/ArithmeticException", message); } - return true; } static int isValidHandle(JNIEnv* env, jlong handle, const char* message) { @@ -100,7 +95,9 @@ static int fourValidHandles(JNIEnv* env, jlong a, jlong b, jlong c, jlong d) { static jlong NativeBN_BN_new(JNIEnv* env, jclass) { jlong result = static_cast(reinterpret_cast(BN_new())); - throwExceptionIfNecessary(env); + if (!result) { + throwException(env); + } return result; } @@ -120,8 +117,9 @@ static int NativeBN_BN_cmp(JNIEnv* env, jclass, jlong a, jlong b) { static void NativeBN_BN_copy(JNIEnv* env, jclass, jlong to, jlong from) { if (!twoValidHandles(env, to, from)) return; - BN_copy(toBigNum(to), toBigNum(from)); - throwExceptionIfNecessary(env); + if (!BN_copy(toBigNum(to), toBigNum(from))) { + throwException(env); + } } static void NativeBN_putULongInt(JNIEnv* env, jclass, jlong a0, jlong java_dw, jboolean neg) { @@ -129,28 +127,13 @@ static void NativeBN_putULongInt(JNIEnv* env, jclass, jlong a0, jlong java_dw, j uint64_t dw = java_dw; BIGNUM* a = toBigNum(a0); - int ok; - static_assert(sizeof(dw) == sizeof(BN_ULONG) || - sizeof(dw) == 2*sizeof(BN_ULONG), "Unknown BN configuration"); - - if (sizeof(dw) == sizeof(BN_ULONG)) { - ok = BN_set_word(a, dw); - } else if (sizeof(dw) == 2 * sizeof(BN_ULONG)) { - ok = (bn_wexpand(a, 2) != NULL); - if (ok) { - a->d[0] = dw; - a->d[1] = dw >> 32; - a->top = 2; - bn_correct_top(a); - } + if (!BN_set_u64(a, dw)) { + throwException(env); + return; } BN_set_negative(a, neg); - - if (!ok) { - throwExceptionIfNecessary(env); - } } static void NativeBN_putLongInt(JNIEnv* env, jclass cls, jlong a, jlong dw) { @@ -169,7 +152,9 @@ static int NativeBN_BN_dec2bn(JNIEnv* env, jclass, jlong a0, jstring str) { } BIGNUM* a = toBigNum(a0); int result = BN_dec2bn(&a, chars.c_str()); - throwExceptionIfNecessary(env); + if (result == 0) { + throwException(env); + } return result; } @@ -181,7 +166,9 @@ static int NativeBN_BN_hex2bn(JNIEnv* env, jclass, jlong a0, jstring str) { } BIGNUM* a = toBigNum(a0); int result = BN_hex2bn(&a, chars.c_str()); - throwExceptionIfNecessary(env); + if (result == 0) { + throwException(env); + } return result; } @@ -191,124 +178,37 @@ static void NativeBN_BN_bin2bn(JNIEnv* env, jclass, jbyteArray arr, int len, jbo if (bytes.get() == NULL) { return; } - BN_bin2bn(reinterpret_cast(bytes.get()), len, toBigNum(ret)); - if (!throwExceptionIfNecessary(env) && neg) { - BN_set_negative(toBigNum(ret), true); + if (!BN_bin2bn(reinterpret_cast(bytes.get()), len, toBigNum(ret))) { + throwException(env); + return; } + + BN_set_negative(toBigNum(ret), neg); } -/** - * Note: - * This procedure directly writes the internal representation of BIGNUMs. - * We do so as there is no direct interface based on Little Endian Integer Arrays. - * Also note that the same representation is used in the Cordoba Java Implementation of BigIntegers, - * whereof certain functionality is still being used. - */ static void NativeBN_litEndInts2bn(JNIEnv* env, jclass, jintArray arr, int len, jboolean neg, jlong ret0) { if (!oneValidHandle(env, ret0)) return; BIGNUM* ret = toBigNum(ret0); - bn_check_top(ret); - if (len > 0) { - ScopedIntArrayRO scopedArray(env, arr); - if (scopedArray.get() == NULL) { - return; - } -#ifdef __LP64__ - const int wlen = (len + 1) / 2; -#else - const int wlen = len; -#endif - const unsigned int* tmpInts = reinterpret_cast(scopedArray.get()); - if ((tmpInts != NULL) && (bn_wexpand(ret, wlen) != NULL)) { -#ifdef __LP64__ - if (len % 2) { - ret->d[wlen - 1] = tmpInts[--len]; - } - if (len > 0) { - for (int i = len - 2; i >= 0; i -= 2) { - ret->d[i/2] = ((unsigned long long)tmpInts[i+1] << 32) | tmpInts[i]; - } - } -#else - int i = len; do { i--; ret->d[i] = tmpInts[i]; } while (i > 0); -#endif - ret->top = wlen; - ret->neg = neg; - // need to call this due to clear byte at top if avoiding - // having the top bit set (-ve number) - // Basically get rid of top zero ints: - bn_correct_top(ret); - } else { - throwExceptionIfNecessary(env); - } - } else { // (len = 0) means value = 0 and sign will be 0, too. - ret->top = 0; - } -} + ScopedIntArrayRO scopedArray(env, arr); -#ifdef __LP64__ -#define BYTES2ULONG(bytes, k) \ - ((bytes[k + 7] & 0xffULL) | (bytes[k + 6] & 0xffULL) << 8 | (bytes[k + 5] & 0xffULL) << 16 | (bytes[k + 4] & 0xffULL) << 24 | \ - (bytes[k + 3] & 0xffULL) << 32 | (bytes[k + 2] & 0xffULL) << 40 | (bytes[k + 1] & 0xffULL) << 48 | (bytes[k + 0] & 0xffULL) << 56) -#else -#define BYTES2ULONG(bytes, k) \ - ((bytes[k + 3] & 0xff) | (bytes[k + 2] & 0xff) << 8 | (bytes[k + 1] & 0xff) << 16 | (bytes[k + 0] & 0xff) << 24) -#endif -static void negBigEndianBytes2bn(JNIEnv*, jclass, const unsigned char* bytes, int bytesLen, jlong ret0) { - BIGNUM* ret = toBigNum(ret0); + if (scopedArray.get() == NULL) { + return; + } - bn_check_top(ret); - // FIXME: assert bytesLen > 0 - int wLen = (bytesLen + sizeof(BN_ULONG) - 1) / sizeof(BN_ULONG); - int firstNonzeroDigit = -2; - if (bn_wexpand(ret, wLen) != NULL) { - BN_ULONG* d = ret->d; - BN_ULONG di; - ret->top = wLen; - int highBytes = bytesLen % sizeof(BN_ULONG); - int k = bytesLen; - // Put bytes to the int array starting from the end of the byte array - int i = 0; - while (k > highBytes) { - k -= sizeof(BN_ULONG); - di = BYTES2ULONG(bytes, k); - if (di != 0) { - d[i] = -di; - firstNonzeroDigit = i; - i++; - while (k > highBytes) { - k -= sizeof(BN_ULONG); - d[i] = ~BYTES2ULONG(bytes, k); - i++; - } - break; - } else { - d[i] = 0; - i++; - } - } - if (highBytes != 0) { - di = -1; - // Put the first bytes in the highest element of the int array - if (firstNonzeroDigit != -2) { - for (k = 0; k < highBytes; k++) { - di = (di << 8) | (bytes[k] & 0xFF); - } - d[i] = ~di; - } else { - for (k = 0; k < highBytes; k++) { - di = (di << 8) | (bytes[k] & 0xFF); - } - d[i] = -di; - } - } - // The top may have superfluous zeros, so fix it. - bn_correct_top(ret); + // We can simply interpret the little-endian integer stream as a + // little-endian byte stream and use BN_le2bn. + const uint8_t* tmpBytes = reinterpret_cast(scopedArray.get()); + size_t numBytes = len * sizeof(int); + + if (!BN_le2bn(tmpBytes, numBytes, ret)) { + throwException(env); } + + BN_set_negative(ret, neg); } -static void NativeBN_twosComp2bn(JNIEnv* env, jclass cls, jbyteArray arr, int bytesLen, jlong ret0) { +static void NativeBN_twosComp2bn(JNIEnv* env, jclass, jbyteArray arr, int bytesLen, jlong ret0) { if (!oneValidHandle(env, ret0)) return; BIGNUM* ret = toBigNum(ret0); @@ -316,42 +216,48 @@ static void NativeBN_twosComp2bn(JNIEnv* env, jclass cls, jbyteArray arr, int by if (bytes.get() == NULL) { return; } - const unsigned char* s = reinterpret_cast(bytes.get()); - if ((bytes[0] & 0X80) == 0) { // Positive value! - // - // We can use the existing BN implementation for unsigned big endian bytes: - // - BN_bin2bn(s, bytesLen, ret); - BN_set_negative(ret, false); - } else { // Negative value! - // - // We need to apply two's complement: - // - negBigEndianBytes2bn(env, cls, s, bytesLen, ret0); - BN_set_negative(ret, true); + + if (bytesLen == 0) { + BN_zero(ret); + return; + } + + const unsigned char* bytesTmp = reinterpret_cast(bytes.get()); + + if (!BN_bin2bn(bytesTmp, bytesLen, ret)) { + throwException(env); + return; + } + + // Use the high bit to determine the sign in twos-complement. + BN_set_negative(ret, (bytes[0] & 0x80) != 0); + + if (BN_is_negative(ret)) { + // For negative values, BN_bin2bn doesn't interpret the twos-complement + // representation, so ret is now (- value - 2^N). We can use nnmod_pow2 to set + // ret to (-value). + if (!BN_nnmod_pow2(ret, ret, bytesLen * 8)) { + throwException(env); + return; + } + + // And now we correct the sign. + BN_set_negative(ret, 1); } - throwExceptionIfNecessary(env); } static jlong NativeBN_longInt(JNIEnv* env, jclass, jlong a0) { if (!oneValidHandle(env, a0)) return -1; - BIGNUM* a = toBigNum(a0); - bn_check_top(a); - int wLen = a->top; - if (wLen == 0) { - return 0; - } + uint64_t word; -#ifdef __LP64__ - jlong result = a->d[0]; -#else - jlong result = static_cast(a->d[0]) & 0xffffffff; - if (wLen > 1) { - result |= static_cast(a->d[1]) << 32; + if (BN_get_u64(a, &word)) { + return BN_is_negative(a) ? -((jlong) word) : word; + } else { + // This should be unreachable if our caller checks BigInt::twosCompFitsIntoBytes(8) + throwException(env); + return 0; } -#endif - return a->neg ? -result : result; } static char* leadingZerosTrimmed(char* s) { @@ -371,6 +277,7 @@ static jstring NativeBN_BN_bn2dec(JNIEnv* env, jclass, jlong a) { if (!oneValidHandle(env, a)) return NULL; char* tmpStr = BN_bn2dec(toBigNum(a)); if (tmpStr == NULL) { + throwException(env); return NULL; } char* retStr = leadingZerosTrimmed(tmpStr); @@ -383,6 +290,7 @@ static jstring NativeBN_BN_bn2hex(JNIEnv* env, jclass, jlong a) { if (!oneValidHandle(env, a)) return NULL; char* tmpStr = BN_bn2hex(toBigNum(a)); if (tmpStr == NULL) { + throwException(env); return NULL; } char* retStr = leadingZerosTrimmed(tmpStr); @@ -408,29 +316,34 @@ static jbyteArray NativeBN_BN_bn2bin(JNIEnv* env, jclass, jlong a0) { static jintArray NativeBN_bn2litEndInts(JNIEnv* env, jclass, jlong a0) { if (!oneValidHandle(env, a0)) return NULL; + BIGNUM* a = toBigNum(a0); - bn_check_top(a); - int wLen = a->top; - if (wLen == 0) { - return NULL; - } - jintArray result = env->NewIntArray(wLen * sizeof(BN_ULONG)/sizeof(unsigned int)); + + // The number of integers we need is BN_num_bytes(a) / sizeof(int), rounded up + int intLen = (BN_num_bytes(a) + sizeof(int) - 1) / sizeof(int); + + // Allocate our result with the JNI boilerplate + jintArray result = env->NewIntArray(intLen); + if (result == NULL) { + throwException(env); return NULL; } + ScopedIntArrayRW ints(env, result); - if (ints.get() == NULL) { - return NULL; - } + unsigned int* uints = reinterpret_cast(ints.get()); if (uints == NULL) { + throwException(env); + return NULL; + } + + // We can simply interpret a little-endian byte stream as a little-endian integer stream. + if (!BN_bn2le_padded(reinterpret_cast(uints), intLen * sizeof(int), a)) { + throwException(env); return NULL; } -#ifdef __LP64__ - int i = wLen; do { i--; uints[i*2+1] = a->d[i] >> 32; uints[i*2] = a->d[i]; } while (i > 0); -#else - int i = wLen; do { i--; uints[i] = a->d[i]; } while (i > 0); -#endif + return result; } @@ -452,129 +365,163 @@ static void NativeBN_BN_set_negative(JNIEnv* env, jclass, jlong b, int n) { static int NativeBN_bitLength(JNIEnv* env, jclass, jlong a0) { if (!oneValidHandle(env, a0)) return JNI_FALSE; BIGNUM* a = toBigNum(a0); - bn_check_top(a); - int wLen = a->top; - if (wLen == 0) return 0; - BN_ULONG* d = a->d; - int i = wLen - 1; - BN_ULONG msd = d[i]; // most significant digit - if (a->neg) { - // Handle negative values correctly: - // i.e. decrement the msd if all other digits are 0: - // while ((i > 0) && (d[i] != 0)) { i--; } - do { i--; } while (!((i < 0) || (d[i] != 0))); - if (i < 0) msd--; // Only if all lower significant digits are 0 we decrement the most significant one. - } - return (wLen - 1) * sizeof(BN_ULONG) * 8 + BN_num_bits_word(msd); + + // If a is not negative, we can use BN_num_bits directly. + if (!BN_is_negative(a)) { + return BN_num_bits(a); + } + + // In the negative case, the number of bits in a is the same as the number of bits in |a|, + // except one less when |a| is a power of two. + BIGNUM positiveA; + BN_init(&positiveA); + + if (!BN_copy(&positiveA, a)) { + BN_free(&positiveA); + throwException(env); + return -1; + } + + BN_set_negative(&positiveA, false); + int numBits = BN_is_pow2(&positiveA) ? BN_num_bits(&positiveA) - 1 : BN_num_bits(&positiveA); + + BN_free(&positiveA); + return numBits; } static jboolean NativeBN_BN_is_bit_set(JNIEnv* env, jclass, jlong a, int n) { if (!oneValidHandle(env, a)) return JNI_FALSE; - return BN_is_bit_set(toBigNum(a), n); + + // NOTE: this is only called in the positive case, so BN_is_bit_set is fine here. + return BN_is_bit_set(toBigNum(a), n) ? JNI_TRUE : JNI_FALSE; } static void NativeBN_BN_shift(JNIEnv* env, jclass, jlong r, jlong a, int n) { if (!twoValidHandles(env, r, a)) return; + int ok; if (n >= 0) { - BN_lshift(toBigNum(r), toBigNum(a), n); + ok = BN_lshift(toBigNum(r), toBigNum(a), n); } else { - BN_rshift(toBigNum(r), toBigNum(a), -n); + ok = BN_rshift(toBigNum(r), toBigNum(a), -n); + } + if (!ok) { + throwException(env); } - throwExceptionIfNecessary(env); } static void NativeBN_BN_add_word(JNIEnv* env, jclass, jlong a, BN_ULONG w) { if (!oneValidHandle(env, a)) return; - BN_add_word(toBigNum(a), w); - throwExceptionIfNecessary(env); + if (!BN_add_word(toBigNum(a), w)) { + throwException(env); + } } static void NativeBN_BN_mul_word(JNIEnv* env, jclass, jlong a, BN_ULONG w) { if (!oneValidHandle(env, a)) return; - BN_mul_word(toBigNum(a), w); - throwExceptionIfNecessary(env); + if (!BN_mul_word(toBigNum(a), w)) { + throwException(env); + } } static BN_ULONG NativeBN_BN_mod_word(JNIEnv* env, jclass, jlong a, BN_ULONG w) { if (!oneValidHandle(env, a)) return 0; - int result = BN_mod_word(toBigNum(a), w); - throwExceptionIfNecessary(env); + BN_ULONG result = BN_mod_word(toBigNum(a), w); + if (result == (BN_ULONG)-1) { + throwException(env); + } return result; } static void NativeBN_BN_add(JNIEnv* env, jclass, jlong r, jlong a, jlong b) { if (!threeValidHandles(env, r, a, b)) return; - BN_add(toBigNum(r), toBigNum(a), toBigNum(b)); - throwExceptionIfNecessary(env); + if (!BN_add(toBigNum(r), toBigNum(a), toBigNum(b))) { + throwException(env); + } } static void NativeBN_BN_sub(JNIEnv* env, jclass, jlong r, jlong a, jlong b) { if (!threeValidHandles(env, r, a, b)) return; - BN_sub(toBigNum(r), toBigNum(a), toBigNum(b)); - throwExceptionIfNecessary(env); + if (!BN_sub(toBigNum(r), toBigNum(a), toBigNum(b))) { + throwException(env); + } } static void NativeBN_BN_gcd(JNIEnv* env, jclass, jlong r, jlong a, jlong b) { if (!threeValidHandles(env, r, a, b)) return; Unique_BN_CTX ctx(BN_CTX_new()); - BN_gcd(toBigNum(r), toBigNum(a), toBigNum(b), ctx.get()); - throwExceptionIfNecessary(env); + if (!BN_gcd(toBigNum(r), toBigNum(a), toBigNum(b), ctx.get())) { + throwException(env); + } } static void NativeBN_BN_mul(JNIEnv* env, jclass, jlong r, jlong a, jlong b) { if (!threeValidHandles(env, r, a, b)) return; Unique_BN_CTX ctx(BN_CTX_new()); - BN_mul(toBigNum(r), toBigNum(a), toBigNum(b), ctx.get()); - throwExceptionIfNecessary(env); + if (!BN_mul(toBigNum(r), toBigNum(a), toBigNum(b), ctx.get())) { + throwException(env); + } } static void NativeBN_BN_exp(JNIEnv* env, jclass, jlong r, jlong a, jlong p) { if (!threeValidHandles(env, r, a, p)) return; Unique_BN_CTX ctx(BN_CTX_new()); - BN_exp(toBigNum(r), toBigNum(a), toBigNum(p), ctx.get()); - throwExceptionIfNecessary(env); + if (!BN_exp(toBigNum(r), toBigNum(a), toBigNum(p), ctx.get())) { + throwException(env); + } } static void NativeBN_BN_div(JNIEnv* env, jclass, jlong dv, jlong rem, jlong m, jlong d) { if (!fourValidHandles(env, (rem ? rem : dv), (dv ? dv : rem), m, d)) return; Unique_BN_CTX ctx(BN_CTX_new()); - BN_div(toBigNum(dv), toBigNum(rem), toBigNum(m), toBigNum(d), ctx.get()); - throwExceptionIfNecessary(env); + if (!BN_div(toBigNum(dv), toBigNum(rem), toBigNum(m), toBigNum(d), ctx.get())) { + throwException(env); + } } static void NativeBN_BN_nnmod(JNIEnv* env, jclass, jlong r, jlong a, jlong m) { if (!threeValidHandles(env, r, a, m)) return; Unique_BN_CTX ctx(BN_CTX_new()); - BN_nnmod(toBigNum(r), toBigNum(a), toBigNum(m), ctx.get()); - throwExceptionIfNecessary(env); + if (!BN_nnmod(toBigNum(r), toBigNum(a), toBigNum(m), ctx.get())) { + throwException(env); + } } static void NativeBN_BN_mod_exp(JNIEnv* env, jclass, jlong r, jlong a, jlong p, jlong m) { if (!fourValidHandles(env, r, a, p, m)) return; Unique_BN_CTX ctx(BN_CTX_new()); - BN_mod_exp(toBigNum(r), toBigNum(a), toBigNum(p), toBigNum(m), ctx.get()); - throwExceptionIfNecessary(env); + if (!BN_mod_exp(toBigNum(r), toBigNum(a), toBigNum(p), toBigNum(m), ctx.get())) { + throwException(env); + } } static void NativeBN_BN_mod_inverse(JNIEnv* env, jclass, jlong ret, jlong a, jlong n) { if (!threeValidHandles(env, ret, a, n)) return; Unique_BN_CTX ctx(BN_CTX_new()); - BN_mod_inverse(toBigNum(ret), toBigNum(a), toBigNum(n), ctx.get()); - throwExceptionIfNecessary(env); + if (!BN_mod_inverse(toBigNum(ret), toBigNum(a), toBigNum(n), ctx.get())) { + throwException(env); + } } static void NativeBN_BN_generate_prime_ex(JNIEnv* env, jclass, jlong ret, int bits, - jboolean safe, jlong add, jlong rem, jlong cb) { + jboolean safe, jlong add, jlong rem) { if (!oneValidHandle(env, ret)) return; - BN_generate_prime_ex(toBigNum(ret), bits, safe, toBigNum(add), toBigNum(rem), - reinterpret_cast(cb)); - throwExceptionIfNecessary(env); + if (!BN_generate_prime_ex(toBigNum(ret), bits, safe, toBigNum(add), toBigNum(rem), + NULL)) { + throwException(env); + } } -static jboolean NativeBN_BN_is_prime_ex(JNIEnv* env, jclass, jlong p, int nchecks, jlong cb) { - if (!oneValidHandle(env, p)) return JNI_FALSE; +static jboolean NativeBN_BN_primality_test(JNIEnv* env, jclass, jlong candidate, int checks, + jboolean do_trial_decryption) { + if (!oneValidHandle(env, candidate)) return JNI_FALSE; Unique_BN_CTX ctx(BN_CTX_new()); - return BN_is_prime_ex(toBigNum(p), nchecks, ctx.get(), reinterpret_cast(cb)); + int is_probably_prime; + if (!BN_primality_test(&is_probably_prime, toBigNum(candidate), checks, ctx.get(), + do_trial_decryption, NULL)) { + throwException(env); + return JNI_FALSE; + } + return is_probably_prime ? JNI_TRUE : JNI_FALSE; } static JNINativeMethod gMethods[] = { @@ -591,10 +538,10 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(NativeBN, BN_exp, "(JJJ)V"), NATIVE_METHOD(NativeBN, BN_free, "(J)V"), NATIVE_METHOD(NativeBN, BN_gcd, "(JJJ)V"), - NATIVE_METHOD(NativeBN, BN_generate_prime_ex, "(JIZJJJ)V"), + NATIVE_METHOD(NativeBN, BN_generate_prime_ex, "(JIZJJ)V"), NATIVE_METHOD(NativeBN, BN_hex2bn, "(JLjava/lang/String;)I"), NATIVE_METHOD(NativeBN, BN_is_bit_set, "(JI)Z"), - NATIVE_METHOD(NativeBN, BN_is_prime_ex, "(JIJ)Z"), + NATIVE_METHOD(NativeBN, BN_primality_test, "(JIZ)Z"), NATIVE_METHOD(NativeBN, BN_mod_exp, "(JJJJ)V"), NATIVE_METHOD(NativeBN, BN_mod_inverse, "(JJJ)V"), NATIVE_METHOD(NativeBN, BN_mod_word, "(JI)I"), diff --git a/luni/src/main/native/java_util_regex_Matcher.cpp b/luni/src/main/native/java_util_regex_Matcher.cpp index 3b2523313..2461afcf8 100644 --- a/luni/src/main/native/java_util_regex_Matcher.cpp +++ b/luni/src/main/native/java_util_regex_Matcher.cpp @@ -23,6 +23,7 @@ #include "JniConstants.h" #include "JniException.h" #include "ScopedPrimitiveArray.h" +#include "ScopedJavaUnicodeString.h" #include "jni.h" #include "unicode/parseerr.h" #include "unicode/regex.h" @@ -207,7 +208,25 @@ static void Matcher_useTransparentBoundsImpl(JNIEnv* env, jclass, jlong addr, jb matcher->useTransparentBounds(value); } +static jint Matcher_getMatchedGroupIndex0(JNIEnv* env, jclass, jlong patternAddr, jstring javaGroupName) { + icu::RegexPattern* pattern = reinterpret_cast(static_cast(patternAddr)); + ScopedJavaUnicodeString groupName(env, javaGroupName); + UErrorCode status = U_ZERO_ERROR; + + jint result = pattern->groupNumberFromName(groupName.unicodeString(), status); + if (U_SUCCESS(status)) { + return result; + } + if (status == U_REGEX_INVALID_CAPTURE_GROUP_NAME) { + return -1; + } + maybeThrowIcuException(env, "RegexPattern::groupNumberFromName", status); + return -1; +} + + static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Matcher, getMatchedGroupIndex0, "(JLjava/lang/String;)I"), NATIVE_METHOD(Matcher, findImpl, "(JLjava/lang/String;I[I)Z"), NATIVE_METHOD(Matcher, findNextImpl, "(JLjava/lang/String;[I)Z"), NATIVE_METHOD(Matcher, getNativeFinalizer, "()J"), diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp index d8f80bbfa..0e9a2bcb8 100644 --- a/luni/src/main/native/libcore_icu_ICU.cpp +++ b/luni/src/main/native/libcore_icu_ICU.cpp @@ -16,16 +16,32 @@ #define LOG_TAG "ICU" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + #include "IcuUtilities.h" #include "JNIHelp.h" #include "JniConstants.h" #include "JniException.h" -#include "ScopedFd.h" #include "ScopedIcuLocale.h" #include "ScopedJavaUnicodeString.h" #include "ScopedLocalRef.h" #include "ScopedUtfChars.h" -#include "cutils/log.h" #include "toStringArray.h" #include "unicode/brkiter.h" #include "unicode/calendar.h" @@ -41,33 +57,21 @@ #include "unicode/timezone.h" #include "unicode/ubrk.h" #include "unicode/ucal.h" +#include "unicode/ucasemap.h" #include "unicode/uclean.h" #include "unicode/ucol.h" #include "unicode/ucurr.h" #include "unicode/udat.h" #include "unicode/uloc.h" #include "unicode/ulocdata.h" +#include "unicode/ures.h" #include "unicode/ustring.h" #include "ureslocs.h" #include "valueOf.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - class ScopedResourceBundle { public: - ScopedResourceBundle(UResourceBundle* bundle) : bundle_(bundle) { + explicit ScopedResourceBundle(UResourceBundle* bundle) : bundle_(bundle) { } ~ScopedResourceBundle() { @@ -357,7 +361,22 @@ static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, int index) { UErrorCode status = U_ZERO_ERROR; int charCount; - const UChar* chars = ures_getStringByIndex(bundle, index, &charCount, &status); + const UChar* chars; + UResourceBundle* currentBundle = ures_getByIndex(bundle, index, NULL, &status); + switch (ures_getType(currentBundle)) { + case URES_STRING: + chars = ures_getString(currentBundle, &charCount, &status); + break; + case URES_ARRAY: + // In case there is an array, Android currently only cares about the + // first string of that array, the rest of the array is used by ICU + // for additional data ignored by Android. + chars = ures_getStringByIndex(currentBundle, 0, &charCount, &status); + break; + default: + status = U_INVALID_FORMAT_ERROR; + } + ures_close(currentBundle); if (U_SUCCESS(status)) { setStringField(env, obj, fieldName, env->NewString(chars, charCount)); } else { @@ -821,56 +840,110 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), }; +// +// Global initialization & Teardown for ICU Setup +// - Contains handlers for JNI_OnLoad and JNI_OnUnload +// + #define FAIL_WITH_STRERROR(s) \ - ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \ + ALOGE("Couldn't " s " '%s': %s", path_.c_str(), strerror(errno)); \ return FALSE; #define MAYBE_FAIL_WITH_ICU_ERROR(s) \ if (status != U_ZERO_ERROR) {\ - ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \ + ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path_.c_str()); \ return FALSE; \ } -static bool mapIcuData(const std::string& path) { +// Contain the memory map for ICU data files. +// Automatically adds the data file to ICU's list of data files upon constructing. +// +// - Automatically unmaps in the destructor. +struct IcuDataMap { + // Map in ICU data at the path, returning null if it failed (prints error to ALOGE). + static std::unique_ptr Create(const std::string& path) { + std::unique_ptr map(new IcuDataMap(path)); + + if (!map->TryMap()) { + // madvise or ICU could fail but mmap still succeeds. + // Destructor will take care of cleaning up a partial init. + return nullptr; + } + + return map; + } + + // Unmap the ICU data. + ~IcuDataMap() { + TryUnmap(); + } + + private: + IcuDataMap(const std::string& path) + : path_(path), + data_(MAP_FAILED), + data_length_(0) + {} + + bool TryMap() { // Open the file and get its length. - ScopedFd fd(open(path.c_str(), O_RDONLY)); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path_.c_str(), O_RDONLY))); + if (fd.get() == -1) { FAIL_WITH_STRERROR("open"); } + struct stat sb; if (fstat(fd.get(), &sb) == -1) { FAIL_WITH_STRERROR("stat"); } + data_length_ = sb.st_size; + // Map it. - void* data = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd.get(), 0); - if (data == MAP_FAILED) { + data_ = mmap(NULL, data_length_, PROT_READ, MAP_SHARED, fd.get(), 0 /* offset */); + if (data_ == MAP_FAILED) { FAIL_WITH_STRERROR("mmap"); } // Tell the kernel that accesses are likely to be random rather than sequential. - if (madvise(data, sb.st_size, MADV_RANDOM) == -1) { + if (madvise(data_, data_length_, MADV_RANDOM) == -1) { FAIL_WITH_STRERROR("madvise(MADV_RANDOM)"); } UErrorCode status = U_ZERO_ERROR; // Tell ICU to use our memory-mapped data. - udata_setCommonData(data, &status); + udata_setCommonData(data_, &status); MAYBE_FAIL_WITH_ICU_ERROR("udata_setCommonData"); - return TRUE; -} + return true; + } -void register_libcore_icu_ICU(JNIEnv* env) { - // Check the timezone override file exists. If it does, map it first so we use it in preference - // to the one that shipped with the device. - const char* dataPathPrefix = getenv("ANDROID_DATA"); - if (dataPathPrefix == NULL) { - ALOGE("ANDROID_DATA environment variable not set"); \ - abort(); + bool TryUnmap() { + // Don't need to do opposite of udata_setCommonData, + // u_cleanup (performed in unregister_libcore_icu_ICU) takes care of it. + + // Don't need to opposite of madvise, munmap will take care of it. + + if (data_ != MAP_FAILED) { + if (munmap(data_, data_length_) == -1) { + FAIL_WITH_STRERROR("munmap"); + } } + // Don't need to close the file, it was closed automatically during TryMap. + return true; + } + + std::string path_; // Save for error messages. + void* data_; // Save for munmap. + size_t data_length_; // Save for munmap. +}; + +struct ICURegistration { + // Init ICU, configuring it and loading the data files. + ICURegistration(JNIEnv* env) { UErrorCode status = U_ZERO_ERROR; // Tell ICU it can *only* use our memory-mapped data. udata_setFileAccess(UDATA_NO_FILES, &status); @@ -879,15 +952,13 @@ void register_libcore_icu_ICU(JNIEnv* env) { abort(); } - // Map in optional TZ data files. - std::string dataPath; - dataPath = dataPathPrefix; - dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat"; + std::string dataPath = getTzDataOverridePath(); + // Map in optional TZ data files. struct stat sb; if (stat(dataPath.c_str(), &sb) == 0) { ALOGD("Timezone override file found: %s", dataPath.c_str()); - if (!mapIcuData(dataPath)) { + if ((icu_datamap_from_data_ = IcuDataMap::Create(dataPath)) == nullptr) { ALOGW("TZ override file %s exists but could not be loaded. Skipping.", dataPath.c_str()); } } else { @@ -895,18 +966,7 @@ void register_libcore_icu_ICU(JNIEnv* env) { } // Use the ICU data files that shipped with the device for everything else. - const char* systemPathPrefix = getenv("ANDROID_ROOT"); - if (systemPathPrefix == NULL) { - ALOGE("ANDROID_ROOT environment variable not set"); \ - abort(); - } - std::string systemPath; - systemPath = systemPathPrefix; - systemPath += "/usr/icu/"; - systemPath += U_ICUDATA_NAME; - systemPath += ".dat"; - - if (!mapIcuData(systemPath)) { + if ((icu_datamap_from_system_ = IcuDataMap::Create(getSystemPath())) == nullptr) { abort(); } @@ -920,4 +980,69 @@ void register_libcore_icu_ICU(JNIEnv* env) { } jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods)); + } + + // De-init ICU, unloading the data files. Do the opposite of the above function. + ~ICURegistration() { + // Skip unregistering JNI methods explicitly, class unloading takes care of it. + + // Reset libicu state to before it was loaded. + u_cleanup(); + + // Unmap ICU data files that shipped with the device for everything else. + icu_datamap_from_system_.reset(); + + // Unmap optional TZ data files. + icu_datamap_from_data_.reset(); + + // We don't need to call udata_setFileAccess because u_cleanup takes care of it. + } + + // Check the timezone override file exists. If it does, map it first so we use it in preference + // to the one that shipped with the device. + static std::string getTzDataOverridePath() { + const char* dataPathPrefix = getenv("ANDROID_DATA"); + if (dataPathPrefix == NULL) { + ALOGE("ANDROID_DATA environment variable not set"); \ + abort(); + } + std::string dataPath; + dataPath = dataPathPrefix; + dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat"; + + return dataPath; + } + + static std::string getSystemPath() { + const char* systemPathPrefix = getenv("ANDROID_ROOT"); + if (systemPathPrefix == NULL) { + ALOGE("ANDROID_ROOT environment variable not set"); \ + abort(); + } + + std::string systemPath; + systemPath = systemPathPrefix; + systemPath += "/usr/icu/"; + systemPath += U_ICUDATA_NAME; + systemPath += ".dat"; + return systemPath; + } + + std::unique_ptr icu_datamap_from_data_; + std::unique_ptr icu_datamap_from_system_; +}; + +// Use RAII-style initialization/teardown so that we can get unregistered +// when dlclose is called (even if JNI_OnUnload is not). +static std::unique_ptr sIcuRegistration; + +// Init ICU, configuring it and loading the data files. +void register_libcore_icu_ICU(JNIEnv* env) { + sIcuRegistration.reset(new ICURegistration(env)); +} + +// De-init ICU, unloading the data files. Do the opposite of the above function. +void unregister_libcore_icu_ICU(JNIEnv*) { + // Explicitly calling this is optional. Dlclose will take care of it as well. + sIcuRegistration.reset(); } diff --git a/luni/src/main/native/libcore_icu_NativeConverter.cpp b/luni/src/main/native/libcore_icu_NativeConverter.cpp index bf938d1ee..f78ca19da 100644 --- a/luni/src/main/native/libcore_icu_NativeConverter.cpp +++ b/luni/src/main/native/libcore_icu_NativeConverter.cpp @@ -15,6 +15,14 @@ #define LOG_TAG "NativeConverter" +#include +#include + +#include +#include + +#include + #include "IcuUtilities.h" #include "JNIHelp.h" #include "JniConstants.h" @@ -23,7 +31,6 @@ #include "ScopedPrimitiveArray.h" #include "ScopedStringChars.h" #include "ScopedUtfChars.h" -#include "cutils/log.h" #include "toStringArray.h" #include "unicode/ucnv.h" #include "unicode/ucnv_cb.h" @@ -31,12 +38,6 @@ #include "unicode/ustring.h" #include "unicode/utypes.h" -#include -#include - -#include -#include - #define NativeConverter_REPORT 0 #define NativeConverter_IGNORE 1 #define NativeConverter_REPLACE 2 diff --git a/luni/src/main/native/libcore_io_Linux.cpp b/luni/src/main/native/libcore_io_Linux.cpp new file mode 100644 index 000000000..1e2f3a585 --- /dev/null +++ b/luni/src/main/native/libcore_io_Linux.cpp @@ -0,0 +1,2508 @@ +/* + * Copyright (C) 2011 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. + */ + +#define LOG_TAG "Linux" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "AsynchronousCloseMonitor.h" +#include "ExecStrings.h" +#include "JNIHelp.h" +#include "JniConstants.h" +#include "JniException.h" +#include "NetworkUtilities.h" +#include "Portability.h" +#include "ScopedBytes.h" +#include "ScopedLocalRef.h" +#include "ScopedPrimitiveArray.h" +#include "ScopedUtfChars.h" +#include "toStringArray.h" + +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif + +#define TO_JAVA_STRING(NAME, EXP) \ + jstring NAME = env->NewStringUTF(EXP); \ + if ((NAME) == NULL) return NULL; + +struct addrinfo_deleter { + void operator()(addrinfo* p) const { + if (p != NULL) { // bionic's freeaddrinfo(3) crashes when passed NULL. + freeaddrinfo(p); + } + } +}; + +struct c_deleter { + void operator()(void* p) const { + free(p); + } +}; + +static bool isIPv4MappedAddress(const sockaddr *sa) { + const sockaddr_in6 *sin6 = reinterpret_cast(sa); + return sa != NULL && sa->sa_family == AF_INET6 && + (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) || + IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)); // We map 0.0.0.0 to ::, so :: is mapped. +} + +/** + * Perform a socket operation that specifies an IP address, possibly falling back from specifying + * the address as an IPv4-mapped IPv6 address in a struct sockaddr_in6 to specifying it as an IPv4 + * address in a struct sockaddr_in. + * + * This is needed because all sockets created by the java.net APIs are IPv6 sockets, and on those + * sockets, IPv4 operations use IPv4-mapped addresses stored in a struct sockaddr_in6. But sockets + * created using Linux.socket(AF_INET, ...) are IPv4 sockets and only support operations using IPv4 + * socket addresses structures. + */ +#define NET_IPV4_FALLBACK(jni_env, return_type, syscall_name, java_fd, java_addr, port, null_addr_ok, args...) ({ \ + return_type _rc = -1; \ + do { \ + sockaddr_storage _ss; \ + socklen_t _salen; \ + if ((java_addr) == NULL && (null_addr_ok)) { \ + /* No IP address specified (e.g., sendto() on a connected socket). */ \ + _salen = 0; \ + } else if (!inetAddressToSockaddr(jni_env, java_addr, port, _ss, _salen)) { \ + /* Invalid socket address, return -1. inetAddressToSockaddr has already thrown. */ \ + break; \ + } \ + sockaddr* _sa = _salen ? reinterpret_cast(&_ss) : NULL; \ + /* inetAddressToSockaddr always returns an IPv6 sockaddr. Assume that java_fd was created \ + * by Java API calls, which always create IPv6 socket fds, and pass it in as is. */ \ + _rc = NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ##args, _sa, _salen); \ + if (_rc == -1 && errno == EAFNOSUPPORT && _salen && isIPv4MappedAddress(_sa)) { \ + /* We passed in an IPv4 address in an IPv6 sockaddr and the kernel told us that we got \ + * the address family wrong. Pass in the same address in an IPv4 sockaddr. */ \ + (jni_env)->ExceptionClear(); \ + if (!inetAddressToSockaddrVerbatim(jni_env, java_addr, port, _ss, _salen)) { \ + break; \ + } \ + _sa = reinterpret_cast(&_ss); \ + _rc = NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ##args, _sa, _salen); \ + } \ + } while (0); \ + _rc; }) \ + +/** + * Used to retry networking system calls that can be interrupted with a signal. Unlike + * TEMP_FAILURE_RETRY, this also handles the case where + * AsynchronousCloseMonitor::signalBlockedThreads(fd) is used to signal a close() or + * Thread.interrupt(). Other signals that result in an EINTR result are ignored and the system call + * is retried. + * + * Returns the result of the system call though a Java exception will be pending if the result is + * -1: a SocketException if signaled via AsynchronousCloseMonitor, or ErrnoException for other + * failures. + */ +#define NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \ + return_type _rc = -1; \ + int _syscallErrno; \ + do { \ + bool _wasSignaled; \ + { \ + int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \ + AsynchronousCloseMonitor _monitor(_fd); \ + _rc = syscall_name(_fd, __VA_ARGS__); \ + _syscallErrno = errno; \ + _wasSignaled = _monitor.wasSignaled(); \ + } \ + if (_wasSignaled) { \ + jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \ + _rc = -1; \ + break; \ + } \ + if (_rc == -1 && _syscallErrno != EINTR) { \ + /* TODO: with a format string we could show the arguments too, like strace(1). */ \ + throwErrnoException(jni_env, # syscall_name); \ + break; \ + } \ + } while (_rc == -1); /* _syscallErrno == EINTR && !_wasSignaled */ \ + if (_rc == -1) { \ + /* If the syscall failed, re-set errno: throwing an exception might have modified it. */ \ + errno = _syscallErrno; \ + } \ + _rc; }) + +/** + * Used to retry system calls that can be interrupted with a signal. Unlike TEMP_FAILURE_RETRY, this + * also handles the case where AsynchronousCloseMonitor::signalBlockedThreads(fd) is used to signal + * a close() or Thread.interrupt(). Other signals that result in an EINTR result are ignored and the + * system call is retried. + * + * Returns the result of the system call though a Java exception will be pending if the result is + * -1: an IOException if the file descriptor is already closed, a InterruptedIOException if signaled + * via AsynchronousCloseMonitor, or ErrnoException for other failures. + */ +#define IO_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \ + return_type _rc = -1; \ + int _syscallErrno; \ + do { \ + bool _wasSignaled; \ + { \ + int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \ + AsynchronousCloseMonitor _monitor(_fd); \ + _rc = syscall_name(_fd, __VA_ARGS__); \ + _syscallErrno = errno; \ + _wasSignaled = _monitor.wasSignaled(); \ + } \ + if (_wasSignaled) { \ + jniThrowException(jni_env, "java/io/InterruptedIOException", # syscall_name " interrupted"); \ + _rc = -1; \ + break; \ + } \ + if (_rc == -1 && _syscallErrno != EINTR) { \ + /* TODO: with a format string we could show the arguments too, like strace(1). */ \ + throwErrnoException(jni_env, # syscall_name); \ + break; \ + } \ + } while (_rc == -1); /* && _syscallErrno == EINTR && !_wasSignaled */ \ + if (_rc == -1) { \ + /* If the syscall failed, re-set errno: throwing an exception might have modified it. */ \ + errno = _syscallErrno; \ + } \ + _rc; }) + +#define NULL_ADDR_OK true +#define NULL_ADDR_FORBIDDEN false + +static void throwException(JNIEnv* env, jclass exceptionClass, jmethodID ctor3, jmethodID ctor2, + const char* functionName, int error) { + jthrowable cause = NULL; + if (env->ExceptionCheck()) { + cause = env->ExceptionOccurred(); + env->ExceptionClear(); + } + + ScopedLocalRef detailMessage(env, env->NewStringUTF(functionName)); + if (detailMessage.get() == NULL) { + // Not really much we can do here. We're probably dead in the water, + // but let's try to stumble on... + env->ExceptionClear(); + } + + jobject exception; + if (cause != NULL) { + exception = env->NewObject(exceptionClass, ctor3, detailMessage.get(), error, cause); + } else { + exception = env->NewObject(exceptionClass, ctor2, detailMessage.get(), error); + } + env->Throw(reinterpret_cast(exception)); +} + +static void throwErrnoException(JNIEnv* env, const char* functionName) { + int error = errno; + static jmethodID ctor3 = env->GetMethodID(JniConstants::errnoExceptionClass, + "", "(Ljava/lang/String;ILjava/lang/Throwable;)V"); + static jmethodID ctor2 = env->GetMethodID(JniConstants::errnoExceptionClass, + "", "(Ljava/lang/String;I)V"); + throwException(env, JniConstants::errnoExceptionClass, ctor3, ctor2, functionName, error); +} + +static void throwGaiException(JNIEnv* env, const char* functionName, int error) { + // Cache the methods ids before we throw, so we don't call GetMethodID with a pending exception. + static jmethodID ctor3 = env->GetMethodID(JniConstants::gaiExceptionClass, "", + "(Ljava/lang/String;ILjava/lang/Throwable;)V"); + static jmethodID ctor2 = env->GetMethodID(JniConstants::gaiExceptionClass, "", + "(Ljava/lang/String;I)V"); + if (errno != 0) { + // EAI_SYSTEM should mean "look at errno instead", but both glibc and bionic seem to + // mess this up. In particular, if you don't have INTERNET permission, errno will be EACCES + // but you'll get EAI_NONAME or EAI_NODATA. So we want our GaiException to have a + // potentially-relevant ErrnoException as its cause even if error != EAI_SYSTEM. + // http://code.google.com/p/android/issues/detail?id=15722 + throwErrnoException(env, functionName); + // Deliberately fall through to throw another exception... + } + throwException(env, JniConstants::gaiExceptionClass, ctor3, ctor2, functionName, error); +} + +template +static rc_t throwIfMinusOne(JNIEnv* env, const char* name, rc_t rc) { + if (rc == rc_t(-1)) { + throwErrnoException(env, name); + } + return rc; +} + +template +class IoVec { +public: + IoVec(JNIEnv* env, size_t bufferCount) : mEnv(env), mBufferCount(bufferCount) { + } + + bool init(jobjectArray javaBuffers, jintArray javaOffsets, jintArray javaByteCounts) { + // We can't delete our local references until after the I/O, so make sure we have room. + if (mEnv->PushLocalFrame(mBufferCount + 16) < 0) { + return false; + } + ScopedIntArrayRO offsets(mEnv, javaOffsets); + if (offsets.get() == NULL) { + return false; + } + ScopedIntArrayRO byteCounts(mEnv, javaByteCounts); + if (byteCounts.get() == NULL) { + return false; + } + // TODO: Linux actually has a 1024 buffer limit. glibc works around this, and we should too. + // TODO: you can query the limit at runtime with sysconf(_SC_IOV_MAX). + for (size_t i = 0; i < mBufferCount; ++i) { + jobject buffer = mEnv->GetObjectArrayElement(javaBuffers, i); // We keep this local ref. + mScopedBuffers.push_back(new ScopedT(mEnv, buffer)); + jbyte* ptr = const_cast(mScopedBuffers.back()->get()); + if (ptr == NULL) { + return false; + } + struct iovec iov; + iov.iov_base = reinterpret_cast(ptr + offsets[i]); + iov.iov_len = byteCounts[i]; + mIoVec.push_back(iov); + } + return true; + } + + ~IoVec() { + for (size_t i = 0; i < mScopedBuffers.size(); ++i) { + delete mScopedBuffers[i]; + } + mEnv->PopLocalFrame(NULL); + } + + iovec* get() { + return &mIoVec[0]; + } + + size_t size() { + return mBufferCount; + } + +private: + JNIEnv* mEnv; + size_t mBufferCount; + std::vector mIoVec; + std::vector mScopedBuffers; +}; + +/** + * Returns a jbyteArray containing the sockaddr_un.sun_path from ss. As per unix(7) sa_len should be + * the length of ss as returned by getsockname(2), getpeername(2), or accept(2). + * If the returned array is of length 0 the sockaddr_un refers to an unnamed socket. + * A null pointer is returned in the event of an error. See unix(7) for more information. + */ +static jbyteArray getUnixSocketPath(JNIEnv* env, const sockaddr_storage& ss, + const socklen_t& sa_len) { + if (ss.ss_family != AF_UNIX) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "getUnixSocketPath unsupported ss_family: %i", ss.ss_family); + return NULL; + } + + const struct sockaddr_un* un_addr = reinterpret_cast(&ss); + // The length of sun_path is sa_len minus the length of the overhead (ss_family). + // See unix(7) for details. This calculation must match that of socket_make_sockaddr_un() in + // socket_local_client.c and javaUnixSocketAddressToSockaddr() to interoperate. + size_t pathLength = sa_len - offsetof(struct sockaddr_un, sun_path); + + jbyteArray javaSunPath = env->NewByteArray(pathLength); + if (javaSunPath == NULL) { + return NULL; + } + + if (pathLength > 0) { + env->SetByteArrayRegion(javaSunPath, 0, pathLength, + reinterpret_cast(&un_addr->sun_path)); + } + return javaSunPath; +} + +static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage& ss, const socklen_t sa_len) { + if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) { + jint port; + jobject inetAddress = sockaddrToInetAddress(env, ss, &port); + if (inetAddress == NULL) { + return NULL; // Exception already thrown. + } + static jmethodID ctor = env->GetMethodID(JniConstants::inetSocketAddressClass, + "", "(Ljava/net/InetAddress;I)V"); + return env->NewObject(JniConstants::inetSocketAddressClass, ctor, inetAddress, port); + } else if (ss.ss_family == AF_UNIX) { + static jmethodID ctor = env->GetMethodID(JniConstants::unixSocketAddressClass, + "", "([B)V"); + + jbyteArray javaSunPath = getUnixSocketPath(env, ss, sa_len); + if (!javaSunPath) { + return NULL; + } + return env->NewObject(JniConstants::unixSocketAddressClass, ctor, javaSunPath); + } else if (ss.ss_family == AF_NETLINK) { + const struct sockaddr_nl* nl_addr = reinterpret_cast(&ss); + static jmethodID ctor = env->GetMethodID(JniConstants::netlinkSocketAddressClass, + "", "(II)V"); + return env->NewObject(JniConstants::netlinkSocketAddressClass, ctor, + static_cast(nl_addr->nl_pid), + static_cast(nl_addr->nl_groups)); + } else if (ss.ss_family == AF_PACKET) { + const struct sockaddr_ll* sll = reinterpret_cast(&ss); + static jmethodID ctor = env->GetMethodID(JniConstants::packetSocketAddressClass, + "", "(SISB[B)V"); + ScopedLocalRef byteArray(env, env->NewByteArray(sll->sll_halen)); + if (byteArray.get() == NULL) { + return NULL; + } + env->SetByteArrayRegion(byteArray.get(), 0, sll->sll_halen, + reinterpret_cast(sll->sll_addr)); + jobject packetSocketAddress = env->NewObject(JniConstants::packetSocketAddressClass, ctor, + static_cast(ntohs(sll->sll_protocol)), + static_cast(sll->sll_ifindex), + static_cast(sll->sll_hatype), + static_cast(sll->sll_pkttype), + byteArray.get()); + return packetSocketAddress; + } + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "unsupported ss_family: %d", + ss.ss_family); + return NULL; +} + +static jobject makeStructPasswd(JNIEnv* env, const struct passwd& pw) { + TO_JAVA_STRING(pw_name, pw.pw_name); + TO_JAVA_STRING(pw_dir, pw.pw_dir); + TO_JAVA_STRING(pw_shell, pw.pw_shell); + static jmethodID ctor = env->GetMethodID(JniConstants::structPasswdClass, "", + "(Ljava/lang/String;IILjava/lang/String;Ljava/lang/String;)V"); + return env->NewObject(JniConstants::structPasswdClass, ctor, + pw_name, static_cast(pw.pw_uid), static_cast(pw.pw_gid), pw_dir, pw_shell); +} + +static jobject makeStructStat(JNIEnv* env, const struct stat64& sb) { + static jmethodID ctor = env->GetMethodID(JniConstants::structStatClass, "", + "(JJIJIIJJJJJJJ)V"); + return env->NewObject(JniConstants::structStatClass, ctor, + static_cast(sb.st_dev), static_cast(sb.st_ino), + static_cast(sb.st_mode), static_cast(sb.st_nlink), + static_cast(sb.st_uid), static_cast(sb.st_gid), + static_cast(sb.st_rdev), static_cast(sb.st_size), + static_cast(sb.st_atime), static_cast(sb.st_mtime), + static_cast(sb.st_ctime), static_cast(sb.st_blksize), + static_cast(sb.st_blocks)); +} + +static jobject makeStructStatVfs(JNIEnv* env, const struct statvfs& sb) { + static jmethodID ctor = env->GetMethodID(JniConstants::structStatVfsClass, "", + "(JJJJJJJJJJJ)V"); + return env->NewObject(JniConstants::structStatVfsClass, ctor, + static_cast(sb.f_bsize), + static_cast(sb.f_frsize), + static_cast(sb.f_blocks), + static_cast(sb.f_bfree), + static_cast(sb.f_bavail), + static_cast(sb.f_files), + static_cast(sb.f_ffree), + static_cast(sb.f_favail), + static_cast(sb.f_fsid), + static_cast(sb.f_flag), + static_cast(sb.f_namemax)); +} + +static jobject makeStructLinger(JNIEnv* env, const struct linger& l) { + static jmethodID ctor = env->GetMethodID(JniConstants::structLingerClass, "", "(II)V"); + return env->NewObject(JniConstants::structLingerClass, ctor, l.l_onoff, l.l_linger); +} + +static jobject makeStructTimeval(JNIEnv* env, const struct timeval& tv) { + static jmethodID ctor = env->GetMethodID(JniConstants::structTimevalClass, "", "(JJ)V"); + return env->NewObject(JniConstants::structTimevalClass, ctor, + static_cast(tv.tv_sec), static_cast(tv.tv_usec)); +} + +static jobject makeStructUcred(JNIEnv* env, const struct ucred& u __unused) { + static jmethodID ctor = env->GetMethodID(JniConstants::structUcredClass, "", "(III)V"); + return env->NewObject(JniConstants::structUcredClass, ctor, u.pid, u.uid, u.gid); +} + +static jobject makeStructUtsname(JNIEnv* env, const struct utsname& buf) { + TO_JAVA_STRING(sysname, buf.sysname); + TO_JAVA_STRING(nodename, buf.nodename); + TO_JAVA_STRING(release, buf.release); + TO_JAVA_STRING(version, buf.version); + TO_JAVA_STRING(machine, buf.machine); + static jmethodID ctor = env->GetMethodID(JniConstants::structUtsnameClass, "", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + return env->NewObject(JniConstants::structUtsnameClass, ctor, + sysname, nodename, release, version, machine); +}; + +static bool fillIfreq(JNIEnv* env, jstring javaInterfaceName, struct ifreq& req) { + ScopedUtfChars interfaceName(env, javaInterfaceName); + if (interfaceName.c_str() == NULL) { + return false; + } + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_name, interfaceName.c_str(), sizeof(req.ifr_name)); + req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; + return true; +} + +static bool fillUnixSocketAddress(JNIEnv* env, jobject javaUnixSocketAddress, + const sockaddr_storage& ss, const socklen_t& sa_len) { + if (javaUnixSocketAddress == NULL) { + return true; + } + jbyteArray javaSunPath = getUnixSocketPath(env, ss, sa_len); + if (!javaSunPath) { + return false; + } + + static jfieldID sunPathFid = + env->GetFieldID(JniConstants::unixSocketAddressClass, "sun_path", "[B"); + env->SetObjectField(javaUnixSocketAddress, sunPathFid, javaSunPath); + return true; +} + +static bool fillInetSocketAddress(JNIEnv* env, jobject javaInetSocketAddress, + const sockaddr_storage& ss) { + if (javaInetSocketAddress == NULL) { + return true; + } + // Fill out the passed-in InetSocketAddress with the sender's IP address and port number. + jint port; + jobject sender = sockaddrToInetAddress(env, ss, &port); + if (sender == NULL) { + return false; + } + static jfieldID holderFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "holder", + "Ljava/net/InetSocketAddress$InetSocketAddressHolder;"); + jobject holder = env->GetObjectField(javaInetSocketAddress, holderFid); + + static jfieldID addressFid = env->GetFieldID(JniConstants::inetSocketAddressHolderClass, + "addr", "Ljava/net/InetAddress;"); + static jfieldID portFid = env->GetFieldID(JniConstants::inetSocketAddressHolderClass, "port", "I"); + env->SetObjectField(holder, addressFid, sender); + env->SetIntField(holder, portFid, port); + return true; +} + +static bool fillSocketAddress(JNIEnv* env, jobject javaSocketAddress, const sockaddr_storage& ss, + const socklen_t& sa_len) { + if (javaSocketAddress == NULL) { + return true; + } + + if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) { + return fillInetSocketAddress(env, javaSocketAddress, ss); + } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::unixSocketAddressClass)) { + return fillUnixSocketAddress(env, javaSocketAddress, ss, sa_len); + } + jniThrowException(env, "java/lang/UnsupportedOperationException", + "unsupported SocketAddress subclass"); + return false; + +} + +static void javaInetSocketAddressToInetAddressAndPort( + JNIEnv* env, jobject javaInetSocketAddress, jobject& javaInetAddress, jint& port) { + static jfieldID holderFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "holder", + "Ljava/net/InetSocketAddress$InetSocketAddressHolder;"); + jobject holder = env->GetObjectField(javaInetSocketAddress, holderFid); + + static jfieldID addressFid = env->GetFieldID( + JniConstants::inetSocketAddressHolderClass, "addr", "Ljava/net/InetAddress;"); + static jfieldID portFid = env->GetFieldID(JniConstants::inetSocketAddressHolderClass, "port", "I"); + + javaInetAddress = env->GetObjectField(holder, addressFid); + port = env->GetIntField(holder, portFid); +} + +static bool javaInetSocketAddressToSockaddr( + JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) { + jobject javaInetAddress; + jint port; + javaInetSocketAddressToInetAddressAndPort(env, javaSocketAddress, javaInetAddress, port); + return inetAddressToSockaddr(env, javaInetAddress, port, ss, sa_len); +} + +static bool javaNetlinkSocketAddressToSockaddr( + JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) { + static jfieldID nlPidFid = env->GetFieldID( + JniConstants::netlinkSocketAddressClass, "nlPortId", "I"); + static jfieldID nlGroupsFid = env->GetFieldID( + JniConstants::netlinkSocketAddressClass, "nlGroupsMask", "I"); + + sockaddr_nl *nlAddr = reinterpret_cast(&ss); + nlAddr->nl_family = AF_NETLINK; + nlAddr->nl_pid = env->GetIntField(javaSocketAddress, nlPidFid); + nlAddr->nl_groups = env->GetIntField(javaSocketAddress, nlGroupsFid); + sa_len = sizeof(sockaddr_nl); + return true; +} + +static bool javaUnixSocketAddressToSockaddr( + JNIEnv* env, jobject javaUnixSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) { + static jfieldID sunPathFid = env->GetFieldID( + JniConstants::unixSocketAddressClass, "sun_path", "[B"); + + struct sockaddr_un* un_addr = reinterpret_cast(&ss); + memset (un_addr, 0, sizeof(sockaddr_un)); + un_addr->sun_family = AF_UNIX; + + jbyteArray javaSunPath = (jbyteArray) env->GetObjectField(javaUnixSocketAddress, sunPathFid); + jsize pathLength = env->GetArrayLength(javaSunPath); + if ((size_t) pathLength > sizeof(sockaddr_un::sun_path)) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "sun_path too long: max=%i, is=%i", + sizeof(sockaddr_un::sun_path), pathLength); + return false; + } + env->GetByteArrayRegion(javaSunPath, 0, pathLength, (jbyte*) un_addr->sun_path); + // sa_len is sun_path plus the length of the overhead (ss_family_t). See unix(7) for + // details. This calculation must match that of socket_make_sockaddr_un() in + // socket_local_client.c and getUnixSocketPath() to interoperate. + sa_len = offsetof(struct sockaddr_un, sun_path) + pathLength; + return true; +} + +static bool javaPacketSocketAddressToSockaddr( + JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) { + static jfieldID protocolFid = env->GetFieldID( + JniConstants::packetSocketAddressClass, "sll_protocol", "S"); + static jfieldID ifindexFid = env->GetFieldID( + JniConstants::packetSocketAddressClass, "sll_ifindex", "I"); + static jfieldID hatypeFid = env->GetFieldID( + JniConstants::packetSocketAddressClass, "sll_hatype", "S"); + static jfieldID pkttypeFid = env->GetFieldID( + JniConstants::packetSocketAddressClass, "sll_pkttype", "B"); + static jfieldID addrFid = env->GetFieldID( + JniConstants::packetSocketAddressClass, "sll_addr", "[B"); + + sockaddr_ll *sll = reinterpret_cast(&ss); + sll->sll_family = AF_PACKET; + sll->sll_protocol = htons(env->GetShortField(javaSocketAddress, protocolFid)); + sll->sll_ifindex = env->GetIntField(javaSocketAddress, ifindexFid); + sll->sll_hatype = env->GetShortField(javaSocketAddress, hatypeFid); + sll->sll_pkttype = env->GetByteField(javaSocketAddress, pkttypeFid); + + jbyteArray sllAddr = (jbyteArray) env->GetObjectField(javaSocketAddress, addrFid); + if (sllAddr == NULL) { + sll->sll_halen = 0; + memset(&sll->sll_addr, 0, sizeof(sll->sll_addr)); + } else { + jsize len = env->GetArrayLength(sllAddr); + if ((size_t) len > sizeof(sll->sll_addr)) { + len = sizeof(sll->sll_addr); + } + sll->sll_halen = len; + env->GetByteArrayRegion(sllAddr, 0, len, (jbyte*) sll->sll_addr); + } + sa_len = sizeof(sockaddr_ll); + return true; +} + +static bool javaSocketAddressToSockaddr( + JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) { + if (javaSocketAddress == NULL) { + jniThrowNullPointerException(env, NULL); + return false; + } + + if (env->IsInstanceOf(javaSocketAddress, JniConstants::netlinkSocketAddressClass)) { + return javaNetlinkSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len); + } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) { + return javaInetSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len); + } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::packetSocketAddressClass)) { + return javaPacketSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len); + } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::unixSocketAddressClass)) { + return javaUnixSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len); + } + jniThrowException(env, "java/lang/UnsupportedOperationException", + "unsupported SocketAddress subclass"); + return false; +} + +static jobject doStat(JNIEnv* env, jstring javaPath, bool isLstat) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return NULL; + } + struct stat64 sb; + int rc = isLstat ? TEMP_FAILURE_RETRY(lstat64(path.c_str(), &sb)) + : TEMP_FAILURE_RETRY(stat64(path.c_str(), &sb)); + if (rc == -1) { + throwErrnoException(env, isLstat ? "lstat" : "stat"); + return NULL; + } + return makeStructStat(env, sb); +} + +static jobject doGetSockName(JNIEnv* env, jobject javaFd, bool is_sockname) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + sockaddr_storage ss; + sockaddr* sa = reinterpret_cast(&ss); + socklen_t byteCount = sizeof(ss); + memset(&ss, 0, byteCount); + int rc = is_sockname ? TEMP_FAILURE_RETRY(getsockname(fd, sa, &byteCount)) + : TEMP_FAILURE_RETRY(getpeername(fd, sa, &byteCount)); + if (rc == -1) { + throwErrnoException(env, is_sockname ? "getsockname" : "getpeername"); + return NULL; + } + return makeSocketAddress(env, ss, byteCount); +} + +class Passwd { +public: + explicit Passwd(JNIEnv* env) : mEnv(env), mResult(NULL) { + mBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX); + mBuffer.reset(new char[mBufferSize]); + } + + jobject getpwnam(const char* name) { + return process("getpwnam_r", getpwnam_r(name, &mPwd, mBuffer.get(), mBufferSize, &mResult)); + } + + jobject getpwuid(uid_t uid) { + return process("getpwuid_r", getpwuid_r(uid, &mPwd, mBuffer.get(), mBufferSize, &mResult)); + } + + struct passwd* get() { + return mResult; + } + +private: + jobject process(const char* syscall, int error) { + if (mResult == NULL) { + errno = error; + throwErrnoException(mEnv, syscall); + return NULL; + } + return makeStructPasswd(mEnv, *mResult); + } + + JNIEnv* mEnv; + std::unique_ptr mBuffer; + size_t mBufferSize; + struct passwd mPwd; + struct passwd* mResult; +}; + +static void AssertException(JNIEnv* env) { + if (env->ExceptionCheck() == JNI_FALSE) { + env->FatalError("Expected exception"); + } +} + +// Note for capabilities functions: +// We assume the calls are rare enough that it does not make sense to cache class objects. The +// advantage is lower maintenance burden. + +static bool ReadStructCapUserHeader( + JNIEnv* env, jobject java_header, __user_cap_header_struct* c_header) { + if (java_header == nullptr) { + jniThrowNullPointerException(env, "header is null"); + return false; + } + + ScopedLocalRef header_class(env, env->FindClass("android/system/StructCapUserHeader")); + if (header_class.get() == nullptr) { + return false; + } + + { + static jfieldID version_fid = env->GetFieldID(header_class.get(), "version", "I"); + if (version_fid == nullptr) { + return false; + } + c_header->version = env->GetIntField(java_header, version_fid); + } + + { + static jfieldID pid_fid = env->GetFieldID(header_class.get(), "pid", "I"); + if (pid_fid == nullptr) { + return false; + } + c_header->pid = env->GetIntField(java_header, pid_fid); + } + + return true; +} + +static void SetStructCapUserHeaderVersion( + JNIEnv* env, jobject java_header, __user_cap_header_struct* c_header) { + ScopedLocalRef header_class(env, env->FindClass("android/system/StructCapUserHeader")); + if (header_class.get() == nullptr) { + env->ExceptionClear(); + return; + } + + static jfieldID version_fid = env->GetFieldID(header_class.get(), "version", "I"); + if (version_fid == nullptr) { + env->ExceptionClear(); + return; + } + env->SetIntField(java_header, version_fid, c_header->version); +} + +static jobject CreateStructCapUserData( + JNIEnv* env, jclass data_class, __user_cap_data_struct* c_data) { + if (c_data == nullptr) { + // Should not happen. + jniThrowNullPointerException(env, "data is null"); + return nullptr; + } + + static jmethodID data_cons = env->GetMethodID(data_class, "", "(III)V"); + if (data_cons == nullptr) { + return nullptr; + } + + jint e = static_cast(c_data->effective); + jint p = static_cast(c_data->permitted); + jint i = static_cast(c_data->inheritable); + return env->NewObject(data_class, data_cons, e, p, i); +} + +static bool ReadStructCapUserData(JNIEnv* env, jobject java_data, __user_cap_data_struct* c_data) { + if (java_data == nullptr) { + jniThrowNullPointerException(env, "data is null"); + return false; + } + + ScopedLocalRef data_class(env, env->FindClass("android/system/StructCapUserData")); + if (data_class.get() == nullptr) { + return false; + } + + { + static jfieldID effective_fid = env->GetFieldID(data_class.get(), "effective", "I"); + if (effective_fid == nullptr) { + return false; + } + c_data->effective = env->GetIntField(java_data, effective_fid); + } + + { + static jfieldID permitted_fid = env->GetFieldID(data_class.get(), "permitted", "I"); + if (permitted_fid == nullptr) { + return false; + } + c_data->permitted = env->GetIntField(java_data, permitted_fid); + } + + + { + static jfieldID inheritable_fid = env->GetFieldID(data_class.get(), "inheritable", "I"); + if (inheritable_fid == nullptr) { + return false; + } + c_data->inheritable = env->GetIntField(java_data, inheritable_fid); + } + + return true; +} + +static constexpr size_t kMaxCapUserDataLength = 2U; +#ifdef _LINUX_CAPABILITY_VERSION_1 +static_assert(kMaxCapUserDataLength >= _LINUX_CAPABILITY_U32S_1, "Length too small."); +#endif +#ifdef _LINUX_CAPABILITY_VERSION_2 +static_assert(kMaxCapUserDataLength >= _LINUX_CAPABILITY_U32S_2, "Length too small."); +#endif +#ifdef _LINUX_CAPABILITY_VERSION_3 +static_assert(kMaxCapUserDataLength >= _LINUX_CAPABILITY_U32S_3, "Length too small."); +#endif +#ifdef _LINUX_CAPABILITY_VERSION_4 +static_assert(false, "Unsupported capability version, please update."); +#endif + +static size_t GetCapUserDataLength(uint32_t version) { +#ifdef _LINUX_CAPABILITY_VERSION_1 + if (version == _LINUX_CAPABILITY_VERSION_1) { + return _LINUX_CAPABILITY_U32S_1; + } +#endif +#ifdef _LINUX_CAPABILITY_VERSION_2 + if (version == _LINUX_CAPABILITY_VERSION_2) { + return _LINUX_CAPABILITY_U32S_2; + } +#endif +#ifdef _LINUX_CAPABILITY_VERSION_3 + if (version == _LINUX_CAPABILITY_VERSION_3) { + return _LINUX_CAPABILITY_U32S_3; + } +#endif + return 0; +} + +static jobject Linux_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) { + sockaddr_storage ss; + socklen_t sl = sizeof(ss); + memset(&ss, 0, sizeof(ss)); + sockaddr* peer = (javaSocketAddress != NULL) ? reinterpret_cast(&ss) : NULL; + socklen_t* peerLength = (javaSocketAddress != NULL) ? &sl : 0; + jint clientFd = NET_FAILURE_RETRY(env, int, accept, javaFd, peer, peerLength); + if (clientFd == -1 || !fillSocketAddress(env, javaSocketAddress, ss, *peerLength)) { + close(clientFd); + return NULL; + } + return (clientFd != -1) ? jniCreateFileDescriptor(env, clientFd) : NULL; +} + +static jboolean Linux_access(JNIEnv* env, jobject, jstring javaPath, jint mode) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return JNI_FALSE; + } + int rc = TEMP_FAILURE_RETRY(access(path.c_str(), mode)); + if (rc == -1) { + throwErrnoException(env, "access"); + } + return (rc == 0); +} + +static void Linux_bind(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) { + // We don't need the return value because we'll already have thrown. + (void) NET_IPV4_FALLBACK(env, int, bind, javaFd, javaAddress, port, NULL_ADDR_FORBIDDEN); +} + +static void Linux_bindSocketAddress( + JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) { + sockaddr_storage ss; + socklen_t sa_len; + if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) { + return; // Exception already thrown. + } + + const sockaddr* sa = reinterpret_cast(&ss); + // We don't need the return value because we'll already have thrown. + (void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sa_len); +} + +static jobjectArray Linux_capget(JNIEnv* env, jobject, jobject header) { + // Convert Java header struct to kernel datastructure. + __user_cap_header_struct cap_header; + if (!ReadStructCapUserHeader(env, header, &cap_header)) { + AssertException(env); + return nullptr; + } + + // Call capget. + __user_cap_data_struct cap_data[kMaxCapUserDataLength]; + if (capget(&cap_header, &cap_data[0]) == -1) { + // Check for EINVAL. In that case, mutate the header. + if (errno == EINVAL) { + int saved_errno = errno; + SetStructCapUserHeaderVersion(env, header, &cap_header); + errno = saved_errno; + } + throwErrnoException(env, "capget"); + return nullptr; + } + + // Create the result array. + ScopedLocalRef data_class(env, env->FindClass("android/system/StructCapUserData")); + if (data_class.get() == nullptr) { + return nullptr; + } + size_t result_size = GetCapUserDataLength(cap_header.version); + ScopedLocalRef result( + env, env->NewObjectArray(result_size, data_class.get(), nullptr)); + if (result.get() == nullptr) { + return nullptr; + } + // Translate the values we got. + for (size_t i = 0; i < result_size; ++i) { + ScopedLocalRef value( + env, CreateStructCapUserData(env, data_class.get(), &cap_data[i])); + if (value.get() == nullptr) { + AssertException(env); + return nullptr; + } + env->SetObjectArrayElement(result.get(), i, value.get()); + } + return result.release(); +} + +static void Linux_capset( + JNIEnv* env, jobject, jobject header, jobjectArray data) { + // Convert Java header struct to kernel datastructure. + __user_cap_header_struct cap_header; + if (!ReadStructCapUserHeader(env, header, &cap_header)) { + AssertException(env); + return; + } + size_t result_size = GetCapUserDataLength(cap_header.version); + // Ensure that the array has the expected length. + if (env->GetArrayLength(data) != static_cast(result_size)) { + jniThrowExceptionFmt(env, + "java/lang/IllegalArgumentException", + "Unsupported input length %d (expected %zu)", + env->GetArrayLength(data), + result_size); + return; + } + + __user_cap_data_struct cap_data[kMaxCapUserDataLength]; + // Translate the values we got. + for (size_t i = 0; i < result_size; ++i) { + ScopedLocalRef value(env, env->GetObjectArrayElement(data, i)); + if (!ReadStructCapUserData(env, value.get(), &cap_data[i])) { + AssertException(env); + return; + } + } + + throwIfMinusOne(env, "capset", capset(&cap_header, &cap_data[0])); +} + +static void Linux_chmod(JNIEnv* env, jobject, jstring javaPath, jint mode) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "chmod", TEMP_FAILURE_RETRY(chmod(path.c_str(), mode))); +} + +static void Linux_chown(JNIEnv* env, jobject, jstring javaPath, jint uid, jint gid) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "chown", TEMP_FAILURE_RETRY(chown(path.c_str(), uid, gid))); +} + +static void Linux_close(JNIEnv* env, jobject, jobject javaFd) { + // Get the FileDescriptor's 'fd' field and clear it. + // We need to do this before we can throw an IOException (http://b/3222087). + int fd = jniGetFDFromFileDescriptor(env, javaFd); + jniSetFileDescriptorOfFD(env, javaFd, -1); + + // Even if close(2) fails with EINTR, the fd will have been closed. + // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone else's fd. + // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html + throwIfMinusOne(env, "close", close(fd)); +} + +static void Linux_connect(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) { + (void) NET_IPV4_FALLBACK(env, int, connect, javaFd, javaAddress, port, NULL_ADDR_FORBIDDEN); +} + +static void Linux_connectSocketAddress( + JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) { + sockaddr_storage ss; + socklen_t sa_len; + if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) { + return; // Exception already thrown. + } + + const sockaddr* sa = reinterpret_cast(&ss); + // We don't need the return value because we'll already have thrown. + (void) NET_FAILURE_RETRY(env, int, connect, javaFd, sa, sa_len); +} + +static jobject Linux_dup(JNIEnv* env, jobject, jobject javaOldFd) { + int oldFd = jniGetFDFromFileDescriptor(env, javaOldFd); + int newFd = throwIfMinusOne(env, "dup", TEMP_FAILURE_RETRY(dup(oldFd))); + return (newFd != -1) ? jniCreateFileDescriptor(env, newFd) : NULL; +} + +static jobject Linux_dup2(JNIEnv* env, jobject, jobject javaOldFd, jint newFd) { + int oldFd = jniGetFDFromFileDescriptor(env, javaOldFd); + int fd = throwIfMinusOne(env, "dup2", TEMP_FAILURE_RETRY(dup2(oldFd, newFd))); + return (fd != -1) ? jniCreateFileDescriptor(env, fd) : NULL; +} + +static jobjectArray Linux_environ(JNIEnv* env, jobject) { + extern char** environ; // Standard, but not in any header file. + return toStringArray(env, environ); +} + +static void Linux_execve(JNIEnv* env, jobject, jstring javaFilename, jobjectArray javaArgv, jobjectArray javaEnvp) { + ScopedUtfChars path(env, javaFilename); + if (path.c_str() == NULL) { + return; + } + + ExecStrings argv(env, javaArgv); + ExecStrings envp(env, javaEnvp); + TEMP_FAILURE_RETRY(execve(path.c_str(), argv.get(), envp.get())); + + throwErrnoException(env, "execve"); +} + +static void Linux_execv(JNIEnv* env, jobject, jstring javaFilename, jobjectArray javaArgv) { + ScopedUtfChars path(env, javaFilename); + if (path.c_str() == NULL) { + return; + } + + ExecStrings argv(env, javaArgv); + TEMP_FAILURE_RETRY(execv(path.c_str(), argv.get())); + + throwErrnoException(env, "execv"); +} + +static void Linux_fchmod(JNIEnv* env, jobject, jobject javaFd, jint mode) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "fchmod", TEMP_FAILURE_RETRY(fchmod(fd, mode))); +} + +static void Linux_fchown(JNIEnv* env, jobject, jobject javaFd, jint uid, jint gid) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "fchown", TEMP_FAILURE_RETRY(fchown(fd, uid, gid))); +} + +static jint Linux_fcntlFlock(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobject javaFlock) { + static jfieldID typeFid = env->GetFieldID(JniConstants::structFlockClass, "l_type", "S"); + static jfieldID whenceFid = env->GetFieldID(JniConstants::structFlockClass, "l_whence", "S"); + static jfieldID startFid = env->GetFieldID(JniConstants::structFlockClass, "l_start", "J"); + static jfieldID lenFid = env->GetFieldID(JniConstants::structFlockClass, "l_len", "J"); + static jfieldID pidFid = env->GetFieldID(JniConstants::structFlockClass, "l_pid", "I"); + + struct flock64 lock; + memset(&lock, 0, sizeof(lock)); + lock.l_type = env->GetShortField(javaFlock, typeFid); + lock.l_whence = env->GetShortField(javaFlock, whenceFid); + lock.l_start = env->GetLongField(javaFlock, startFid); + lock.l_len = env->GetLongField(javaFlock, lenFid); + lock.l_pid = env->GetIntField(javaFlock, pidFid); + + int rc = IO_FAILURE_RETRY(env, int, fcntl, javaFd, cmd, &lock); + if (rc != -1) { + env->SetShortField(javaFlock, typeFid, lock.l_type); + env->SetShortField(javaFlock, whenceFid, lock.l_whence); + env->SetLongField(javaFlock, startFid, lock.l_start); + env->SetLongField(javaFlock, lenFid, lock.l_len); + env->SetIntField(javaFlock, pidFid, lock.l_pid); + } + return rc; +} + +static jint Linux_fcntlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jint arg) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd, arg))); +} + +static jint Linux_fcntlVoid(JNIEnv* env, jobject, jobject javaFd, jint cmd) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd))); +} + +static void Linux_fdatasync(JNIEnv* env, jobject, jobject javaFd) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "fdatasync", TEMP_FAILURE_RETRY(fdatasync(fd))); +} + +static jobject Linux_fstat(JNIEnv* env, jobject, jobject javaFd) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + struct stat64 sb; + int rc = TEMP_FAILURE_RETRY(fstat64(fd, &sb)); + if (rc == -1) { + throwErrnoException(env, "fstat"); + return NULL; + } + return makeStructStat(env, sb); +} + +static jobject Linux_fstatvfs(JNIEnv* env, jobject, jobject javaFd) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + struct statvfs sb; + int rc = TEMP_FAILURE_RETRY(fstatvfs(fd, &sb)); + if (rc == -1) { + throwErrnoException(env, "fstatvfs"); + return NULL; + } + return makeStructStatVfs(env, sb); +} + +static void Linux_fsync(JNIEnv* env, jobject, jobject javaFd) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "fsync", TEMP_FAILURE_RETRY(fsync(fd))); +} + +static void Linux_ftruncate(JNIEnv* env, jobject, jobject javaFd, jlong length) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "ftruncate", TEMP_FAILURE_RETRY(ftruncate64(fd, length))); +} + +static jstring Linux_gai_strerror(JNIEnv* env, jobject, jint error) { + return env->NewStringUTF(gai_strerror(error)); +} + +static jobjectArray Linux_android_getaddrinfo(JNIEnv* env, jobject, jstring javaNode, + jobject javaHints, jint netId) { + ScopedUtfChars node(env, javaNode); + if (node.c_str() == NULL) { + return NULL; + } + + static jfieldID flagsFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_flags", "I"); + static jfieldID familyFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_family", "I"); + static jfieldID socktypeFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_socktype", "I"); + static jfieldID protocolFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_protocol", "I"); + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = env->GetIntField(javaHints, flagsFid); + hints.ai_family = env->GetIntField(javaHints, familyFid); + hints.ai_socktype = env->GetIntField(javaHints, socktypeFid); + hints.ai_protocol = env->GetIntField(javaHints, protocolFid); + + addrinfo* addressList = NULL; + errno = 0; + int rc = android_getaddrinfofornet(node.c_str(), NULL, &hints, netId, 0, &addressList); + std::unique_ptr addressListDeleter(addressList); + if (rc != 0) { + throwGaiException(env, "android_getaddrinfo", rc); + return NULL; + } + + // Count results so we know how to size the output array. + int addressCount = 0; + for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) { + if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) { + ++addressCount; + } else { + ALOGE("android_getaddrinfo unexpected ai_family %i", ai->ai_family); + } + } + if (addressCount == 0) { + return NULL; + } + + // Prepare output array. + jobjectArray result = env->NewObjectArray(addressCount, JniConstants::inetAddressClass, NULL); + if (result == NULL) { + return NULL; + } + + // Examine returned addresses one by one, save them in the output array. + int index = 0; + for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) { + // Unknown address family. Skip this address. + ALOGE("android_getaddrinfo unexpected ai_family %i", ai->ai_family); + continue; + } + + // Convert each IP address into a Java byte array. + sockaddr_storage& address = *reinterpret_cast(ai->ai_addr); + ScopedLocalRef inetAddress(env, sockaddrToInetAddress(env, address, NULL)); + if (inetAddress.get() == NULL) { + return NULL; + } + env->SetObjectArrayElement(result, index, inetAddress.get()); + ++index; + } + return result; +} + +static jint Linux_getegid(JNIEnv*, jobject) { + return getegid(); +} + +static jint Linux_geteuid(JNIEnv*, jobject) { + return geteuid(); +} + +static jint Linux_getgid(JNIEnv*, jobject) { + return getgid(); +} + +static jstring Linux_getenv(JNIEnv* env, jobject, jstring javaName) { + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return NULL; + } + return env->NewStringUTF(getenv(name.c_str())); +} + +static jstring Linux_getnameinfo(JNIEnv* env, jobject, jobject javaAddress, jint flags) { + sockaddr_storage ss; + socklen_t sa_len; + if (!inetAddressToSockaddrVerbatim(env, javaAddress, 0, ss, sa_len)) { + return NULL; + } + char buf[NI_MAXHOST]; // NI_MAXHOST is longer than INET6_ADDRSTRLEN. + errno = 0; + int rc = getnameinfo(reinterpret_cast(&ss), sa_len, buf, sizeof(buf), NULL, 0, flags); + if (rc != 0) { + throwGaiException(env, "getnameinfo", rc); + return NULL; + } + return env->NewStringUTF(buf); +} + +static jobject Linux_getpeername(JNIEnv* env, jobject, jobject javaFd) { + return doGetSockName(env, javaFd, false); +} + +static jint Linux_getpgid(JNIEnv* env, jobject, jint pid) { + return throwIfMinusOne(env, "getpgid", TEMP_FAILURE_RETRY(getpgid(pid))); +} + +static jint Linux_getpid(JNIEnv*, jobject) { + return TEMP_FAILURE_RETRY(getpid()); +} + +static jint Linux_getppid(JNIEnv*, jobject) { + return TEMP_FAILURE_RETRY(getppid()); +} + +static jobject Linux_getpwnam(JNIEnv* env, jobject, jstring javaName) { + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return NULL; + } + return Passwd(env).getpwnam(name.c_str()); +} + +static jobject Linux_getpwuid(JNIEnv* env, jobject, jint uid) { + return Passwd(env).getpwuid(uid); +} + +static jobject Linux_getsockname(JNIEnv* env, jobject, jobject javaFd) { + return doGetSockName(env, javaFd, true); +} + +static jint Linux_getsockoptByte(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + u_char result = 0; + socklen_t size = sizeof(result); + throwIfMinusOne(env, "getsockopt", TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &result, &size))); + return result; +} + +static jobject Linux_getsockoptInAddr(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + sockaddr_storage ss; + memset(&ss, 0, sizeof(ss)); + ss.ss_family = AF_INET; // This is only for the IPv4-only IP_MULTICAST_IF. + sockaddr_in* sa = reinterpret_cast(&ss); + socklen_t size = sizeof(sa->sin_addr); + int rc = TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &sa->sin_addr, &size)); + if (rc == -1) { + throwErrnoException(env, "getsockopt"); + return NULL; + } + return sockaddrToInetAddress(env, ss, NULL); +} + +static jint Linux_getsockoptInt(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + jint result = 0; + socklen_t size = sizeof(result); + throwIfMinusOne(env, "getsockopt", TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &result, &size))); + return result; +} + +static jobject Linux_getsockoptLinger(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + struct linger l; + socklen_t size = sizeof(l); + memset(&l, 0, size); + int rc = TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &l, &size)); + if (rc == -1) { + throwErrnoException(env, "getsockopt"); + return NULL; + } + return makeStructLinger(env, l); +} + +static jobject Linux_getsockoptTimeval(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + struct timeval tv; + socklen_t size = sizeof(tv); + memset(&tv, 0, size); + int rc = TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &tv, &size)); + if (rc == -1) { + throwErrnoException(env, "getsockopt"); + return NULL; + } + return makeStructTimeval(env, tv); +} + +static jobject Linux_getsockoptUcred(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + struct ucred u; + socklen_t size = sizeof(u); + memset(&u, 0, size); + int rc = TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &u, &size)); + if (rc == -1) { + throwErrnoException(env, "getsockopt"); + return NULL; + } + return makeStructUcred(env, u); +} + +static jint Linux_gettid(JNIEnv* env __unused, jobject) { +#if defined(__BIONIC__) + return TEMP_FAILURE_RETRY(gettid()); +#else + return syscall(__NR_gettid); +#endif +} + +static jint Linux_getuid(JNIEnv*, jobject) { + return getuid(); +} + +static jbyteArray Linux_getxattr(JNIEnv* env, jobject, jstring javaPath, + jstring javaName) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return NULL; + } + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return NULL; + } + + while (true) { + // Get the current size of the named extended attribute. + ssize_t valueLength; + if ((valueLength = getxattr(path.c_str(), name.c_str(), NULL, 0)) < 0) { + throwErrnoException(env, "getxattr"); + return NULL; + } + + // Create the actual byte array. + std::vector buf(valueLength); + if ((valueLength = getxattr(path.c_str(), name.c_str(), buf.data(), valueLength)) < 0) { + if (errno == ERANGE) { + // The attribute value has changed since last getxattr call and buf no longer fits, + // try again. + continue; + } + throwErrnoException(env, "getxattr"); + return NULL; + } + jbyteArray array = env->NewByteArray(valueLength); + if (array == NULL) { + return NULL; + } + env->SetByteArrayRegion(array, 0, valueLength, reinterpret_cast(buf.data())); + return array; + } +} + +static jobjectArray Linux_getifaddrs(JNIEnv* env, jobject) { + static jmethodID ctor = env->GetMethodID(JniConstants::structIfaddrs, "", + "(Ljava/lang/String;ILjava/net/InetAddress;Ljava/net/InetAddress;Ljava/net/InetAddress;[B)V"); + + ifaddrs* ifaddr; + int rc = TEMP_FAILURE_RETRY(getifaddrs(&ifaddr)); + if (rc == -1) { + throwErrnoException(env, "getifaddrs"); + return NULL; + } + std::unique_ptr ifaddrPtr(ifaddr, freeifaddrs); + + // Count results so we know how to size the output array. + jint ifCount = 0; + for (ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + ++ifCount; + } + + // Prepare output array. + jobjectArray result = env->NewObjectArray(ifCount, JniConstants::structIfaddrs, NULL); + if (result == NULL) { + return NULL; + } + + // Traverse the list and populate the output array. + int index = 0; + for (ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next, ++index) { + TO_JAVA_STRING(name, ifa->ifa_name); + jint flags = ifa->ifa_flags; + sockaddr_storage* interfaceAddr = + reinterpret_cast(ifa->ifa_addr); + sockaddr_storage* netmaskAddr = + reinterpret_cast(ifa->ifa_netmask); + sockaddr_storage* broadAddr = + reinterpret_cast(ifa->ifa_broadaddr); + + jobject addr, netmask, broad; + jbyteArray hwaddr = NULL; + if (interfaceAddr != NULL) { + switch (interfaceAddr->ss_family) { + case AF_INET: + case AF_INET6: + // IPv4 / IPv6. + // interfaceAddr and netmaskAddr are never null. + if ((addr = sockaddrToInetAddress(env, *interfaceAddr, NULL)) == NULL) { + return NULL; + } + if ((netmask = sockaddrToInetAddress(env, *netmaskAddr, NULL)) == NULL) { + return NULL; + } + if (broadAddr != NULL && (ifa->ifa_flags & IFF_BROADCAST)) { + if ((broad = sockaddrToInetAddress(env, *broadAddr, NULL)) == NULL) { + return NULL; + } + } else { + broad = NULL; + } + break; + case AF_PACKET: + // Raw Interface. + sockaddr_ll* sll = reinterpret_cast(ifa->ifa_addr); + + bool allZero = true; + for (int i = 0; i < sll->sll_halen; ++i) { + if (sll->sll_addr[i] != 0) { + allZero = false; + break; + } + } + + if (!allZero) { + hwaddr = env->NewByteArray(sll->sll_halen); + if (hwaddr == NULL) { + return NULL; + } + env->SetByteArrayRegion(hwaddr, 0, sll->sll_halen, + reinterpret_cast(sll->sll_addr)); + } + addr = netmask = broad = NULL; + break; + } + } else { + // Preserve the entry even if the interface has no interface address. + // http://b/29243557/ + addr = netmask = broad = NULL; + } + + jobject o = env->NewObject(JniConstants::structIfaddrs, ctor, name, flags, addr, netmask, + broad, hwaddr); + env->SetObjectArrayElement(result, index, o); + } + + return result; +} + +static jstring Linux_if_indextoname(JNIEnv* env, jobject, jint index) { + char buf[IF_NAMESIZE]; + char* name = if_indextoname(index, buf); + // if_indextoname(3) returns NULL on failure, which will come out of NewStringUTF unscathed. + // There's no useful information in errno, so we don't bother throwing. Callers can null-check. + return env->NewStringUTF(name); +} + +static jint Linux_if_nametoindex(JNIEnv* env, jobject, jstring name) { + ScopedUtfChars cname(env, name); + if (cname.c_str() == NULL) { + return 0; + } + + // There's no useful information in errno, so we don't bother throwing. Callers can zero-check. + return if_nametoindex(cname.c_str()); +} + +static jobject Linux_inet_pton(JNIEnv* env, jobject, jint family, jstring javaName) { + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return NULL; + } + sockaddr_storage ss; + memset(&ss, 0, sizeof(ss)); + // sockaddr_in and sockaddr_in6 are at the same address, so we can use either here. + void* dst = &reinterpret_cast(&ss)->sin_addr; + if (inet_pton(family, name.c_str(), dst) != 1) { + return NULL; + } + ss.ss_family = family; + return sockaddrToInetAddress(env, ss, NULL); +} + +static jint Linux_ioctlFlags(JNIEnv* env, jobject, jobject javaFd, jstring javaInterfaceName) { + struct ifreq req; + if (!fillIfreq(env, javaInterfaceName, req)) { + return 0; + } + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "ioctl", TEMP_FAILURE_RETRY(ioctl(fd, SIOCGIFFLAGS, &req))); + return req.ifr_flags; +} + +static jobject Linux_ioctlInetAddress(JNIEnv* env, jobject, jobject javaFd, jint cmd, jstring javaInterfaceName) { + struct ifreq req; + if (!fillIfreq(env, javaInterfaceName, req)) { + return NULL; + } + int fd = jniGetFDFromFileDescriptor(env, javaFd); + int rc = throwIfMinusOne(env, "ioctl", TEMP_FAILURE_RETRY(ioctl(fd, cmd, &req))); + if (rc == -1) { + return NULL; + } + return sockaddrToInetAddress(env, reinterpret_cast(req.ifr_addr), NULL); +} + +static jint Linux_ioctlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobject javaArg) { + // This is complicated because ioctls may return their result by updating their argument + // or via their return value, so we need to support both. + int fd = jniGetFDFromFileDescriptor(env, javaFd); + static jfieldID valueFid = env->GetFieldID(JniConstants::mutableIntClass, "value", "I"); + jint arg = env->GetIntField(javaArg, valueFid); + int rc = throwIfMinusOne(env, "ioctl", TEMP_FAILURE_RETRY(ioctl(fd, cmd, &arg))); + if (!env->ExceptionCheck()) { + env->SetIntField(javaArg, valueFid, arg); + } + return rc; +} + +static jint Linux_ioctlMTU(JNIEnv* env, jobject, jobject javaFd, jstring javaInterfaceName) { + struct ifreq req; + if (!fillIfreq(env, javaInterfaceName, req)) { + return 0; + } + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "ioctl", TEMP_FAILURE_RETRY(ioctl(fd, SIOCGIFMTU, &req))); + return req.ifr_mtu; +} + +static jboolean Linux_isatty(JNIEnv* env, jobject, jobject javaFd) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + return TEMP_FAILURE_RETRY(isatty(fd)) == 1; +} + +static void Linux_kill(JNIEnv* env, jobject, jint pid, jint sig) { + throwIfMinusOne(env, "kill", TEMP_FAILURE_RETRY(kill(pid, sig))); +} + +static void Linux_lchown(JNIEnv* env, jobject, jstring javaPath, jint uid, jint gid) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "lchown", TEMP_FAILURE_RETRY(lchown(path.c_str(), uid, gid))); +} + +static void Linux_link(JNIEnv* env, jobject, jstring javaOldPath, jstring javaNewPath) { + ScopedUtfChars oldPath(env, javaOldPath); + if (oldPath.c_str() == NULL) { + return; + } + ScopedUtfChars newPath(env, javaNewPath); + if (newPath.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "link", TEMP_FAILURE_RETRY(link(oldPath.c_str(), newPath.c_str()))); +} + +static void Linux_listen(JNIEnv* env, jobject, jobject javaFd, jint backlog) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "listen", TEMP_FAILURE_RETRY(listen(fd, backlog))); +} + +static jobjectArray Linux_listxattr(JNIEnv* env, jobject, jstring javaPath) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return NULL; + } + + while (true) { + // Get the current size of the named extended attribute. + ssize_t valueLength; + if ((valueLength = listxattr(path.c_str(), NULL, 0)) < 0) { + throwErrnoException(env, "listxattr"); + return NULL; + } + + // Create the actual byte array. + std::string buf(valueLength, '\0'); + if ((valueLength = listxattr(path.c_str(), &buf[0], valueLength)) < 0) { + if (errno == ERANGE) { + // The attribute value has changed since last listxattr call and buf no longer fits, + // try again. + continue; + } + throwErrnoException(env, "listxattr"); + return NULL; + } + + // Split the output by '\0'. + buf.resize(valueLength > 0 ? valueLength - 1 : 0); // Remove the trailing NULL character. + std::string delim("\0", 1); + auto xattrs = android::base::Split(buf, delim); + + return toStringArray(env, xattrs); + } +} + +static jlong Linux_lseek(JNIEnv* env, jobject, jobject javaFd, jlong offset, jint whence) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + return throwIfMinusOne(env, "lseek", TEMP_FAILURE_RETRY(lseek64(fd, offset, whence))); +} + +static jobject Linux_lstat(JNIEnv* env, jobject, jstring javaPath) { + return doStat(env, javaPath, true); +} + +static void Linux_mincore(JNIEnv* env, jobject, jlong address, jlong byteCount, jbyteArray javaVector) { + ScopedByteArrayRW vector(env, javaVector); + if (vector.get() == NULL) { + return; + } + void* ptr = reinterpret_cast(static_cast(address)); + unsigned char* vec = reinterpret_cast(vector.get()); + throwIfMinusOne(env, "mincore", TEMP_FAILURE_RETRY(mincore(ptr, byteCount, vec))); +} + +static void Linux_mkdir(JNIEnv* env, jobject, jstring javaPath, jint mode) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "mkdir", TEMP_FAILURE_RETRY(mkdir(path.c_str(), mode))); +} + +static void Linux_mkfifo(JNIEnv* env, jobject, jstring javaPath, jint mode) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "mkfifo", TEMP_FAILURE_RETRY(mkfifo(path.c_str(), mode))); +} + +static void Linux_mlock(JNIEnv* env, jobject, jlong address, jlong byteCount) { + void* ptr = reinterpret_cast(static_cast(address)); + throwIfMinusOne(env, "mlock", TEMP_FAILURE_RETRY(mlock(ptr, byteCount))); +} + +static jlong Linux_mmap(JNIEnv* env, jobject, jlong address, jlong byteCount, jint prot, jint flags, jobject javaFd, jlong offset) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + void* suggestedPtr = reinterpret_cast(static_cast(address)); + void* ptr = mmap64(suggestedPtr, byteCount, prot, flags, fd, offset); + if (ptr == MAP_FAILED) { + throwErrnoException(env, "mmap"); + } + return static_cast(reinterpret_cast(ptr)); +} + +static void Linux_msync(JNIEnv* env, jobject, jlong address, jlong byteCount, jint flags) { + void* ptr = reinterpret_cast(static_cast(address)); + throwIfMinusOne(env, "msync", TEMP_FAILURE_RETRY(msync(ptr, byteCount, flags))); +} + +static void Linux_munlock(JNIEnv* env, jobject, jlong address, jlong byteCount) { + void* ptr = reinterpret_cast(static_cast(address)); + throwIfMinusOne(env, "munlock", TEMP_FAILURE_RETRY(munlock(ptr, byteCount))); +} + +static void Linux_munmap(JNIEnv* env, jobject, jlong address, jlong byteCount) { + void* ptr = reinterpret_cast(static_cast(address)); + throwIfMinusOne(env, "munmap", TEMP_FAILURE_RETRY(munmap(ptr, byteCount))); +} + +static jobject Linux_open(JNIEnv* env, jobject, jstring javaPath, jint flags, jint mode) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return NULL; + } + int fd = throwIfMinusOne(env, "open", TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode))); + return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL; +} + +static jobjectArray Linux_pipe2(JNIEnv* env, jobject, jint flags __unused) { + int fds[2]; + throwIfMinusOne(env, "pipe2", TEMP_FAILURE_RETRY(pipe2(&fds[0], flags))); + jobjectArray result = env->NewObjectArray(2, JniConstants::fileDescriptorClass, NULL); + if (result == NULL) { + return NULL; + } + for (int i = 0; i < 2; ++i) { + ScopedLocalRef fd(env, jniCreateFileDescriptor(env, fds[i])); + if (fd.get() == NULL) { + return NULL; + } + env->SetObjectArrayElement(result, i, fd.get()); + if (env->ExceptionCheck()) { + return NULL; + } + } + return result; +} + +static jint Linux_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint timeoutMs) { + static jfieldID fdFid = env->GetFieldID(JniConstants::structPollfdClass, "fd", "Ljava/io/FileDescriptor;"); + static jfieldID eventsFid = env->GetFieldID(JniConstants::structPollfdClass, "events", "S"); + static jfieldID reventsFid = env->GetFieldID(JniConstants::structPollfdClass, "revents", "S"); + + // Turn the Java android.system.StructPollfd[] into a C++ struct pollfd[]. + size_t arrayLength = env->GetArrayLength(javaStructs); + std::unique_ptr fds(new struct pollfd[arrayLength]); + memset(fds.get(), 0, sizeof(struct pollfd) * arrayLength); + size_t count = 0; // Some trailing array elements may be irrelevant. (See below.) + for (size_t i = 0; i < arrayLength; ++i) { + ScopedLocalRef javaStruct(env, env->GetObjectArrayElement(javaStructs, i)); + if (javaStruct.get() == NULL) { + break; // We allow trailing nulls in the array for caller convenience. + } + ScopedLocalRef javaFd(env, env->GetObjectField(javaStruct.get(), fdFid)); + if (javaFd.get() == NULL) { + break; // We also allow callers to just clear the fd field (this is what Selector does). + } + fds[count].fd = jniGetFDFromFileDescriptor(env, javaFd.get()); + fds[count].events = env->GetShortField(javaStruct.get(), eventsFid); + ++count; + } + + std::vector monitors; + for (size_t i = 0; i < count; ++i) { + monitors.push_back(new AsynchronousCloseMonitor(fds[i].fd)); + } + + int rc; + while (true) { + timespec before; + clock_gettime(CLOCK_MONOTONIC, &before); + + rc = poll(fds.get(), count, timeoutMs); + if (rc >= 0 || errno != EINTR) { + break; + } + + // We got EINTR. Work out how much of the original timeout is still left. + if (timeoutMs > 0) { + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + timespec diff; + diff.tv_sec = now.tv_sec - before.tv_sec; + diff.tv_nsec = now.tv_nsec - before.tv_nsec; + if (diff.tv_nsec < 0) { + --diff.tv_sec; + diff.tv_nsec += 1000000000; + } + + jint diffMs = diff.tv_sec * 1000 + diff.tv_nsec / 1000000; + if (diffMs >= timeoutMs) { + rc = 0; // We have less than 1ms left anyway, so just time out. + break; + } + + timeoutMs -= diffMs; + } + } + + for (size_t i = 0; i < monitors.size(); ++i) { + delete monitors[i]; + } + if (rc == -1) { + throwErrnoException(env, "poll"); + return -1; + } + + // Update the revents fields in the Java android.system.StructPollfd[]. + for (size_t i = 0; i < count; ++i) { + ScopedLocalRef javaStruct(env, env->GetObjectArrayElement(javaStructs, i)); + if (javaStruct.get() == NULL) { + return -1; + } + env->SetShortField(javaStruct.get(), reventsFid, fds[i].revents); + } + return rc; +} + +static void Linux_posix_fallocate(JNIEnv* env, jobject, jobject javaFd __unused, + jlong offset __unused, jlong length __unused) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + while ((errno = posix_fallocate64(fd, offset, length)) == EINTR) { + } + if (errno != 0) { + throwErrnoException(env, "posix_fallocate"); + } +} + +static jint Linux_prctl(JNIEnv* env, jobject, jint option __unused, jlong arg2 __unused, + jlong arg3 __unused, jlong arg4 __unused, jlong arg5 __unused) { + int result = TEMP_FAILURE_RETRY(prctl(static_cast(option), + static_cast(arg2), + static_cast(arg3), + static_cast(arg4), + static_cast(arg5))); + return throwIfMinusOne(env, "prctl", result); +} + +static jint Linux_preadBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jlong offset) { + ScopedBytesRW bytes(env, javaBytes); + if (bytes.get() == NULL) { + return -1; + } + return IO_FAILURE_RETRY(env, ssize_t, pread64, javaFd, bytes.get() + byteOffset, byteCount, offset); +} + +static jint Linux_pwriteBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount, jlong offset) { + ScopedBytesRO bytes(env, javaBytes); + if (bytes.get() == NULL) { + return -1; + } + return IO_FAILURE_RETRY(env, ssize_t, pwrite64, javaFd, bytes.get() + byteOffset, byteCount, offset); +} + +static jint Linux_readBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount) { + ScopedBytesRW bytes(env, javaBytes); + if (bytes.get() == NULL) { + return -1; + } + return IO_FAILURE_RETRY(env, ssize_t, read, javaFd, bytes.get() + byteOffset, byteCount); +} + +static jstring Linux_readlink(JNIEnv* env, jobject, jstring javaPath) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return NULL; + } + + std::string result; + if (!android::base::Readlink(path.c_str(), &result)) { + throwErrnoException(env, "readlink"); + return NULL; + } + return env->NewStringUTF(result.c_str()); +} + +static jstring Linux_realpath(JNIEnv* env, jobject, jstring javaPath) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return NULL; + } + + std::unique_ptr real_path(realpath(path.c_str(), nullptr)); + if (real_path.get() == nullptr) { + throwErrnoException(env, "realpath"); + return NULL; + } + + return env->NewStringUTF(real_path.get()); +} + +static jint Linux_readv(JNIEnv* env, jobject, jobject javaFd, jobjectArray buffers, jintArray offsets, jintArray byteCounts) { + IoVec ioVec(env, env->GetArrayLength(buffers)); + if (!ioVec.init(buffers, offsets, byteCounts)) { + return -1; + } + return IO_FAILURE_RETRY(env, ssize_t, readv, javaFd, ioVec.get(), ioVec.size()); +} + +static jint Linux_recvfromBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetSocketAddress) { + ScopedBytesRW bytes(env, javaBytes); + if (bytes.get() == NULL) { + return -1; + } + sockaddr_storage ss; + socklen_t sl = sizeof(ss); + memset(&ss, 0, sizeof(ss)); + sockaddr* from = (javaInetSocketAddress != NULL) ? reinterpret_cast(&ss) : NULL; + socklen_t* fromLength = (javaInetSocketAddress != NULL) ? &sl : 0; + jint recvCount = NET_FAILURE_RETRY(env, ssize_t, recvfrom, javaFd, bytes.get() + byteOffset, byteCount, flags, from, fromLength); + if (recvCount >= 0) { + // The socket may have performed orderly shutdown and recvCount would return 0 (see man 2 + // recvfrom), in which case ss.ss_family == AF_UNIX and fillInetSocketAddress would fail. + // Don't fill in the address if recvfrom didn't succeed. http://b/33483694 + if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) { + fillInetSocketAddress(env, javaInetSocketAddress, ss); + } + } + return recvCount; +} + +static void Linux_remove(JNIEnv* env, jobject, jstring javaPath) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "remove", TEMP_FAILURE_RETRY(remove(path.c_str()))); +} + +static void Linux_removexattr(JNIEnv* env, jobject, jstring javaPath, jstring javaName) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return; + } + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return; + } + + int res = removexattr(path.c_str(), name.c_str()); + if (res < 0) { + throwErrnoException(env, "removexattr"); + } +} + +static void Linux_rename(JNIEnv* env, jobject, jstring javaOldPath, jstring javaNewPath) { + ScopedUtfChars oldPath(env, javaOldPath); + if (oldPath.c_str() == NULL) { + return; + } + ScopedUtfChars newPath(env, javaNewPath); + if (newPath.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "rename", TEMP_FAILURE_RETRY(rename(oldPath.c_str(), newPath.c_str()))); +} + +static jlong Linux_sendfile(JNIEnv* env, jobject, jobject javaOutFd, jobject javaInFd, jobject javaOffset, jlong byteCount) { + int outFd = jniGetFDFromFileDescriptor(env, javaOutFd); + int inFd = jniGetFDFromFileDescriptor(env, javaInFd); + static jfieldID valueFid = env->GetFieldID(JniConstants::mutableLongClass, "value", "J"); + off_t offset = 0; + off_t* offsetPtr = NULL; + if (javaOffset != NULL) { + // TODO: fix bionic so we can have a 64-bit off_t! + offset = env->GetLongField(javaOffset, valueFid); + offsetPtr = &offset; + } + jlong result = throwIfMinusOne(env, "sendfile", TEMP_FAILURE_RETRY(sendfile(outFd, inFd, offsetPtr, byteCount))); + if (javaOffset != NULL) { + env->SetLongField(javaOffset, valueFid, offset); + } + return result; +} + +static jint Linux_sendtoBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetAddress, jint port) { + ScopedBytesRO bytes(env, javaBytes); + if (bytes.get() == NULL) { + return -1; + } + + return NET_IPV4_FALLBACK(env, ssize_t, sendto, javaFd, javaInetAddress, port, + NULL_ADDR_OK, bytes.get() + byteOffset, byteCount, flags); +} + +static jint Linux_sendtoBytesSocketAddress(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaSocketAddress) { + if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) { + // Use the InetAddress version so we get the benefit of NET_IPV4_FALLBACK. + jobject javaInetAddress; + jint port; + javaInetSocketAddressToInetAddressAndPort(env, javaSocketAddress, javaInetAddress, port); + return Linux_sendtoBytes(env, NULL, javaFd, javaBytes, byteOffset, byteCount, flags, + javaInetAddress, port); + } + + ScopedBytesRO bytes(env, javaBytes); + if (bytes.get() == NULL) { + return -1; + } + + sockaddr_storage ss; + socklen_t sa_len; + if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) { + return -1; + } + + const sockaddr* sa = reinterpret_cast(&ss); + // We don't need the return value because we'll already have thrown. + return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, sa, sa_len); +} + +static void Linux_setegid(JNIEnv* env, jobject, jint egid) { + throwIfMinusOne(env, "setegid", TEMP_FAILURE_RETRY(setegid(egid))); +} + +static void Linux_setenv(JNIEnv* env, jobject, jstring javaName, jstring javaValue, jboolean overwrite) { + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return; + } + ScopedUtfChars value(env, javaValue); + if (value.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "setenv", setenv(name.c_str(), value.c_str(), overwrite)); +} + +static void Linux_seteuid(JNIEnv* env, jobject, jint euid) { + throwIfMinusOne(env, "seteuid", TEMP_FAILURE_RETRY(seteuid(euid))); +} + +static void Linux_setgid(JNIEnv* env, jobject, jint gid) { + throwIfMinusOne(env, "setgid", TEMP_FAILURE_RETRY(setgid(gid))); +} + +static void Linux_setpgid(JNIEnv* env, jobject, jint pid, int pgid) { + throwIfMinusOne(env, "setpgid", TEMP_FAILURE_RETRY(setpgid(pid, pgid))); +} + +static void Linux_setregid(JNIEnv* env, jobject, jint rgid, int egid) { + throwIfMinusOne(env, "setregid", TEMP_FAILURE_RETRY(setregid(rgid, egid))); +} + +static void Linux_setreuid(JNIEnv* env, jobject, jint ruid, int euid) { + throwIfMinusOne(env, "setreuid", TEMP_FAILURE_RETRY(setreuid(ruid, euid))); +} + +static jint Linux_setsid(JNIEnv* env, jobject) { + return throwIfMinusOne(env, "setsid", TEMP_FAILURE_RETRY(setsid())); +} + +static void Linux_setsockoptByte(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jint value) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + u_char byte = value; + throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &byte, sizeof(byte)))); +} + +static void Linux_setsockoptIfreq(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jstring javaInterfaceName) { + struct ifreq req; + if (!fillIfreq(env, javaInterfaceName, req)) { + return; + } + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req, sizeof(req)))); +} + +static void Linux_setsockoptInt(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jint value) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &value, sizeof(value)))); +} + +static void Linux_setsockoptIpMreqn(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jint value) { + ip_mreqn req; + memset(&req, 0, sizeof(req)); + req.imr_ifindex = value; + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req, sizeof(req)))); +} + +static void Linux_setsockoptGroupReq(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jobject javaGroupReq) { + struct group_req req; + memset(&req, 0, sizeof(req)); + + static jfieldID grInterfaceFid = env->GetFieldID(JniConstants::structGroupReqClass, "gr_interface", "I"); + req.gr_interface = env->GetIntField(javaGroupReq, grInterfaceFid); + // Get the IPv4 or IPv6 multicast address to join or leave. + static jfieldID grGroupFid = env->GetFieldID(JniConstants::structGroupReqClass, "gr_group", "Ljava/net/InetAddress;"); + ScopedLocalRef javaGroup(env, env->GetObjectField(javaGroupReq, grGroupFid)); + socklen_t sa_len; + if (!inetAddressToSockaddrVerbatim(env, javaGroup.get(), 0, req.gr_group, sa_len)) { + return; + } + + int fd = jniGetFDFromFileDescriptor(env, javaFd); + int rc = TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req, sizeof(req))); + if (rc == -1 && errno == EINVAL) { + // Maybe we're a 32-bit binary talking to a 64-bit kernel? + // glibc doesn't automatically handle this. + // http://sourceware.org/bugzilla/show_bug.cgi?id=12080 + struct group_req64 { + uint32_t gr_interface; + uint32_t my_padding; + sockaddr_storage gr_group; + }; + group_req64 req64; + req64.gr_interface = req.gr_interface; + memcpy(&req64.gr_group, &req.gr_group, sizeof(req.gr_group)); + rc = TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req64, sizeof(req64))); + } + throwIfMinusOne(env, "setsockopt", rc); +} + +static void Linux_setsockoptGroupSourceReq(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jobject javaGroupSourceReq) { + socklen_t sa_len; + struct group_source_req req; + memset(&req, 0, sizeof(req)); + + static jfieldID gsrInterfaceFid = env->GetFieldID(JniConstants::structGroupSourceReqClass, "gsr_interface", "I"); + req.gsr_interface = env->GetIntField(javaGroupSourceReq, gsrInterfaceFid); + // Get the IPv4 or IPv6 multicast address to join or leave. + static jfieldID gsrGroupFid = env->GetFieldID(JniConstants::structGroupSourceReqClass, "gsr_group", "Ljava/net/InetAddress;"); + ScopedLocalRef javaGroup(env, env->GetObjectField(javaGroupSourceReq, gsrGroupFid)); + if (!inetAddressToSockaddrVerbatim(env, javaGroup.get(), 0, req.gsr_group, sa_len)) { + return; + } + + // Get the IPv4 or IPv6 multicast address to add to the filter. + static jfieldID gsrSourceFid = env->GetFieldID(JniConstants::structGroupSourceReqClass, "gsr_source", "Ljava/net/InetAddress;"); + ScopedLocalRef javaSource(env, env->GetObjectField(javaGroupSourceReq, gsrSourceFid)); + if (!inetAddressToSockaddrVerbatim(env, javaSource.get(), 0, req.gsr_source, sa_len)) { + return; + } + + int fd = jniGetFDFromFileDescriptor(env, javaFd); + int rc = TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req, sizeof(req))); + if (rc == -1 && errno == EINVAL) { + // Maybe we're a 32-bit binary talking to a 64-bit kernel? + // glibc doesn't automatically handle this. + // http://sourceware.org/bugzilla/show_bug.cgi?id=12080 + struct group_source_req64 { + uint32_t gsr_interface; + uint32_t my_padding; + sockaddr_storage gsr_group; + sockaddr_storage gsr_source; + }; + group_source_req64 req64; + req64.gsr_interface = req.gsr_interface; + memcpy(&req64.gsr_group, &req.gsr_group, sizeof(req.gsr_group)); + memcpy(&req64.gsr_source, &req.gsr_source, sizeof(req.gsr_source)); + rc = TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req64, sizeof(req64))); + } + throwIfMinusOne(env, "setsockopt", rc); +} + +static void Linux_setsockoptLinger(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jobject javaLinger) { + static jfieldID lOnoffFid = env->GetFieldID(JniConstants::structLingerClass, "l_onoff", "I"); + static jfieldID lLingerFid = env->GetFieldID(JniConstants::structLingerClass, "l_linger", "I"); + int fd = jniGetFDFromFileDescriptor(env, javaFd); + struct linger value; + value.l_onoff = env->GetIntField(javaLinger, lOnoffFid); + value.l_linger = env->GetIntField(javaLinger, lLingerFid); + throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &value, sizeof(value)))); +} + +static void Linux_setsockoptTimeval(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jobject javaTimeval) { + static jfieldID tvSecFid = env->GetFieldID(JniConstants::structTimevalClass, "tv_sec", "J"); + static jfieldID tvUsecFid = env->GetFieldID(JniConstants::structTimevalClass, "tv_usec", "J"); + int fd = jniGetFDFromFileDescriptor(env, javaFd); + struct timeval value; + value.tv_sec = env->GetLongField(javaTimeval, tvSecFid); + value.tv_usec = env->GetLongField(javaTimeval, tvUsecFid); + throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &value, sizeof(value)))); +} + +static void Linux_setuid(JNIEnv* env, jobject, jint uid) { + throwIfMinusOne(env, "setuid", TEMP_FAILURE_RETRY(setuid(uid))); +} + +static void Linux_setxattr(JNIEnv* env, jobject, jstring javaPath, jstring javaName, + jbyteArray javaValue, jint flags) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return; + } + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return; + } + ScopedBytesRO value(env, javaValue); + if (value.get() == NULL) { + return; + } + size_t valueLength = env->GetArrayLength(javaValue); + int res = setxattr(path.c_str(), name.c_str(), value.get(), valueLength, flags); + if (res < 0) { + throwErrnoException(env, "setxattr"); + } +} + +static void Linux_shutdown(JNIEnv* env, jobject, jobject javaFd, jint how) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "shutdown", TEMP_FAILURE_RETRY(shutdown(fd, how))); +} + +static jobject Linux_socket(JNIEnv* env, jobject, jint domain, jint type, jint protocol) { + if (domain == AF_PACKET) { + protocol = htons(protocol); // Packet sockets specify the protocol in host byte order. + } + int fd = throwIfMinusOne(env, "socket", TEMP_FAILURE_RETRY(socket(domain, type, protocol))); + return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL; +} + +static void Linux_socketpair(JNIEnv* env, jobject, jint domain, jint type, jint protocol, jobject javaFd1, jobject javaFd2) { + int fds[2]; + int rc = throwIfMinusOne(env, "socketpair", TEMP_FAILURE_RETRY(socketpair(domain, type, protocol, fds))); + if (rc != -1) { + jniSetFileDescriptorOfFD(env, javaFd1, fds[0]); + jniSetFileDescriptorOfFD(env, javaFd2, fds[1]); + } +} + +static jobject Linux_stat(JNIEnv* env, jobject, jstring javaPath) { + return doStat(env, javaPath, false); +} + +static jobject Linux_statvfs(JNIEnv* env, jobject, jstring javaPath) { + ScopedUtfChars path(env, javaPath); + if (path.c_str() == NULL) { + return NULL; + } + struct statvfs sb; + int rc = TEMP_FAILURE_RETRY(statvfs(path.c_str(), &sb)); + if (rc == -1) { + throwErrnoException(env, "statvfs"); + return NULL; + } + return makeStructStatVfs(env, sb); +} + +static jstring Linux_strerror(JNIEnv* env, jobject, jint errnum) { + char buffer[BUFSIZ]; + const char* message = jniStrError(errnum, buffer, sizeof(buffer)); + return env->NewStringUTF(message); +} + +static jstring Linux_strsignal(JNIEnv* env, jobject, jint signal) { + return env->NewStringUTF(strsignal(signal)); +} + +static void Linux_symlink(JNIEnv* env, jobject, jstring javaOldPath, jstring javaNewPath) { + ScopedUtfChars oldPath(env, javaOldPath); + if (oldPath.c_str() == NULL) { + return; + } + ScopedUtfChars newPath(env, javaNewPath); + if (newPath.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "symlink", TEMP_FAILURE_RETRY(symlink(oldPath.c_str(), newPath.c_str()))); +} + +static jlong Linux_sysconf(JNIEnv* env, jobject, jint name) { + // Since -1 is a valid result from sysconf(3), detecting failure is a little more awkward. + errno = 0; + long result = sysconf(name); + if (result == -1L && errno == EINVAL) { + throwErrnoException(env, "sysconf"); + } + return result; +} + +static void Linux_tcdrain(JNIEnv* env, jobject, jobject javaFd) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "tcdrain", TEMP_FAILURE_RETRY(tcdrain(fd))); +} + +static void Linux_tcsendbreak(JNIEnv* env, jobject, jobject javaFd, jint duration) { + int fd = jniGetFDFromFileDescriptor(env, javaFd); + throwIfMinusOne(env, "tcsendbreak", TEMP_FAILURE_RETRY(tcsendbreak(fd, duration))); +} + +static jint Linux_umaskImpl(JNIEnv*, jobject, jint mask) { + return umask(mask); +} + +static jobject Linux_uname(JNIEnv* env, jobject) { + struct utsname buf; + if (TEMP_FAILURE_RETRY(uname(&buf)) == -1) { + return NULL; // Can't happen. + } + return makeStructUtsname(env, buf); +} + +static void Linux_unlink(JNIEnv* env, jobject, jstring javaPathname) { + ScopedUtfChars pathname(env, javaPathname); + if (pathname.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "unlink", unlink(pathname.c_str())); +} + +static void Linux_unsetenv(JNIEnv* env, jobject, jstring javaName) { + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return; + } + throwIfMinusOne(env, "unsetenv", unsetenv(name.c_str())); +} + +static jint Linux_waitpid(JNIEnv* env, jobject, jint pid, jobject javaStatus, jint options) { + int status; + int rc = throwIfMinusOne(env, "waitpid", TEMP_FAILURE_RETRY(waitpid(pid, &status, options))); + if (rc != -1) { + static jfieldID valueFid = env->GetFieldID(JniConstants::mutableIntClass, "value", "I"); + env->SetIntField(javaStatus, valueFid, status); + } + return rc; +} + +static jint Linux_writeBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount) { + ScopedBytesRO bytes(env, javaBytes); + if (bytes.get() == NULL) { + return -1; + } + return IO_FAILURE_RETRY(env, ssize_t, write, javaFd, bytes.get() + byteOffset, byteCount); +} + +static jint Linux_writev(JNIEnv* env, jobject, jobject javaFd, jobjectArray buffers, jintArray offsets, jintArray byteCounts) { + IoVec ioVec(env, env->GetArrayLength(buffers)); + if (!ioVec.init(buffers, offsets, byteCounts)) { + return -1; + } + return IO_FAILURE_RETRY(env, ssize_t, writev, javaFd, ioVec.get(), ioVec.size()); +} + +#define NATIVE_METHOD_OVERLOAD(className, functionName, signature, variant) \ + { #functionName, signature, reinterpret_cast(className ## _ ## functionName ## variant) } + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Linux, accept, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)Ljava/io/FileDescriptor;"), + NATIVE_METHOD(Linux, access, "(Ljava/lang/String;I)Z"), + NATIVE_METHOD(Linux, android_getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;I)[Ljava/net/InetAddress;"), + NATIVE_METHOD(Linux, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"), + NATIVE_METHOD_OVERLOAD(Linux, bind, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V", SocketAddress), + NATIVE_METHOD(Linux, capget, + "(Landroid/system/StructCapUserHeader;)[Landroid/system/StructCapUserData;"), + NATIVE_METHOD(Linux, capset, + "(Landroid/system/StructCapUserHeader;[Landroid/system/StructCapUserData;)V"), + NATIVE_METHOD(Linux, chmod, "(Ljava/lang/String;I)V"), + NATIVE_METHOD(Linux, chown, "(Ljava/lang/String;II)V"), + NATIVE_METHOD(Linux, close, "(Ljava/io/FileDescriptor;)V"), + NATIVE_METHOD(Linux, connect, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"), + NATIVE_METHOD_OVERLOAD(Linux, connect, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V", SocketAddress), + NATIVE_METHOD(Linux, dup, "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;"), + NATIVE_METHOD(Linux, dup2, "(Ljava/io/FileDescriptor;I)Ljava/io/FileDescriptor;"), + NATIVE_METHOD(Linux, environ, "()[Ljava/lang/String;"), + NATIVE_METHOD(Linux, execv, "(Ljava/lang/String;[Ljava/lang/String;)V"), + NATIVE_METHOD(Linux, execve, "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V"), + NATIVE_METHOD(Linux, fchmod, "(Ljava/io/FileDescriptor;I)V"), + NATIVE_METHOD(Linux, fchown, "(Ljava/io/FileDescriptor;II)V"), + NATIVE_METHOD(Linux, fcntlFlock, "(Ljava/io/FileDescriptor;ILandroid/system/StructFlock;)I"), + NATIVE_METHOD(Linux, fcntlInt, "(Ljava/io/FileDescriptor;II)I"), + NATIVE_METHOD(Linux, fcntlVoid, "(Ljava/io/FileDescriptor;I)I"), + NATIVE_METHOD(Linux, fdatasync, "(Ljava/io/FileDescriptor;)V"), + NATIVE_METHOD(Linux, fstat, "(Ljava/io/FileDescriptor;)Landroid/system/StructStat;"), + NATIVE_METHOD(Linux, fstatvfs, "(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs;"), + NATIVE_METHOD(Linux, fsync, "(Ljava/io/FileDescriptor;)V"), + NATIVE_METHOD(Linux, ftruncate, "(Ljava/io/FileDescriptor;J)V"), + NATIVE_METHOD(Linux, gai_strerror, "(I)Ljava/lang/String;"), + NATIVE_METHOD(Linux, getegid, "()I"), + NATIVE_METHOD(Linux, geteuid, "()I"), + NATIVE_METHOD(Linux, getgid, "()I"), + NATIVE_METHOD(Linux, getenv, "(Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(Linux, getnameinfo, "(Ljava/net/InetAddress;I)Ljava/lang/String;"), + NATIVE_METHOD(Linux, getpeername, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"), + NATIVE_METHOD(Linux, getpgid, "(I)I"), + NATIVE_METHOD(Linux, getpid, "()I"), + NATIVE_METHOD(Linux, getppid, "()I"), + NATIVE_METHOD(Linux, getpwnam, "(Ljava/lang/String;)Landroid/system/StructPasswd;"), + NATIVE_METHOD(Linux, getpwuid, "(I)Landroid/system/StructPasswd;"), + NATIVE_METHOD(Linux, getsockname, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"), + NATIVE_METHOD(Linux, getsockoptByte, "(Ljava/io/FileDescriptor;II)I"), + NATIVE_METHOD(Linux, getsockoptInAddr, "(Ljava/io/FileDescriptor;II)Ljava/net/InetAddress;"), + NATIVE_METHOD(Linux, getsockoptInt, "(Ljava/io/FileDescriptor;II)I"), + NATIVE_METHOD(Linux, getsockoptLinger, "(Ljava/io/FileDescriptor;II)Landroid/system/StructLinger;"), + NATIVE_METHOD(Linux, getsockoptTimeval, "(Ljava/io/FileDescriptor;II)Landroid/system/StructTimeval;"), + NATIVE_METHOD(Linux, getsockoptUcred, "(Ljava/io/FileDescriptor;II)Landroid/system/StructUcred;"), + NATIVE_METHOD(Linux, gettid, "()I"), + NATIVE_METHOD(Linux, getuid, "()I"), + NATIVE_METHOD(Linux, getxattr, "(Ljava/lang/String;Ljava/lang/String;)[B"), + NATIVE_METHOD(Linux, getifaddrs, "()[Landroid/system/StructIfaddrs;"), + NATIVE_METHOD(Linux, if_indextoname, "(I)Ljava/lang/String;"), + NATIVE_METHOD(Linux, if_nametoindex, "(Ljava/lang/String;)I"), + NATIVE_METHOD(Linux, inet_pton, "(ILjava/lang/String;)Ljava/net/InetAddress;"), + NATIVE_METHOD(Linux, ioctlFlags, "(Ljava/io/FileDescriptor;Ljava/lang/String;)I"), + NATIVE_METHOD(Linux, ioctlInetAddress, "(Ljava/io/FileDescriptor;ILjava/lang/String;)Ljava/net/InetAddress;"), + NATIVE_METHOD(Linux, ioctlInt, "(Ljava/io/FileDescriptor;ILandroid/util/MutableInt;)I"), + NATIVE_METHOD(Linux, ioctlMTU, "(Ljava/io/FileDescriptor;Ljava/lang/String;)I"), + NATIVE_METHOD(Linux, isatty, "(Ljava/io/FileDescriptor;)Z"), + NATIVE_METHOD(Linux, kill, "(II)V"), + NATIVE_METHOD(Linux, lchown, "(Ljava/lang/String;II)V"), + NATIVE_METHOD(Linux, link, "(Ljava/lang/String;Ljava/lang/String;)V"), + NATIVE_METHOD(Linux, listen, "(Ljava/io/FileDescriptor;I)V"), + NATIVE_METHOD(Linux, listxattr, "(Ljava/lang/String;)[Ljava/lang/String;"), + NATIVE_METHOD(Linux, lseek, "(Ljava/io/FileDescriptor;JI)J"), + NATIVE_METHOD(Linux, lstat, "(Ljava/lang/String;)Landroid/system/StructStat;"), + NATIVE_METHOD(Linux, mincore, "(JJ[B)V"), + NATIVE_METHOD(Linux, mkdir, "(Ljava/lang/String;I)V"), + NATIVE_METHOD(Linux, mkfifo, "(Ljava/lang/String;I)V"), + NATIVE_METHOD(Linux, mlock, "(JJ)V"), + NATIVE_METHOD(Linux, mmap, "(JJIILjava/io/FileDescriptor;J)J"), + NATIVE_METHOD(Linux, msync, "(JJI)V"), + NATIVE_METHOD(Linux, munlock, "(JJ)V"), + NATIVE_METHOD(Linux, munmap, "(JJ)V"), + NATIVE_METHOD(Linux, open, "(Ljava/lang/String;II)Ljava/io/FileDescriptor;"), + NATIVE_METHOD(Linux, pipe2, "(I)[Ljava/io/FileDescriptor;"), + NATIVE_METHOD(Linux, poll, "([Landroid/system/StructPollfd;I)I"), + NATIVE_METHOD(Linux, posix_fallocate, "(Ljava/io/FileDescriptor;JJ)V"), + NATIVE_METHOD(Linux, prctl, "(IJJJJ)I"), + NATIVE_METHOD(Linux, preadBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIJ)I"), + NATIVE_METHOD(Linux, pwriteBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIJ)I"), + NATIVE_METHOD(Linux, readBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"), + NATIVE_METHOD(Linux, readlink, "(Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(Linux, realpath, "(Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(Linux, readv, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"), + NATIVE_METHOD(Linux, recvfromBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetSocketAddress;)I"), + NATIVE_METHOD(Linux, remove, "(Ljava/lang/String;)V"), + NATIVE_METHOD(Linux, removexattr, "(Ljava/lang/String;Ljava/lang/String;)V"), + NATIVE_METHOD(Linux, rename, "(Ljava/lang/String;Ljava/lang/String;)V"), + NATIVE_METHOD(Linux, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Landroid/util/MutableLong;J)J"), + NATIVE_METHOD(Linux, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetAddress;I)I"), + NATIVE_METHOD_OVERLOAD(Linux, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/SocketAddress;)I", SocketAddress), + NATIVE_METHOD(Linux, setegid, "(I)V"), + NATIVE_METHOD(Linux, setenv, "(Ljava/lang/String;Ljava/lang/String;Z)V"), + NATIVE_METHOD(Linux, seteuid, "(I)V"), + NATIVE_METHOD(Linux, setgid, "(I)V"), + NATIVE_METHOD(Linux, setpgid, "(II)V"), + NATIVE_METHOD(Linux, setregid, "(II)V"), + NATIVE_METHOD(Linux, setreuid, "(II)V"), + NATIVE_METHOD(Linux, setsid, "()I"), + NATIVE_METHOD(Linux, setsockoptByte, "(Ljava/io/FileDescriptor;III)V"), + NATIVE_METHOD(Linux, setsockoptIfreq, "(Ljava/io/FileDescriptor;IILjava/lang/String;)V"), + NATIVE_METHOD(Linux, setsockoptInt, "(Ljava/io/FileDescriptor;III)V"), + NATIVE_METHOD(Linux, setsockoptIpMreqn, "(Ljava/io/FileDescriptor;III)V"), + NATIVE_METHOD(Linux, setsockoptGroupReq, "(Ljava/io/FileDescriptor;IILandroid/system/StructGroupReq;)V"), + NATIVE_METHOD(Linux, setsockoptGroupSourceReq, "(Ljava/io/FileDescriptor;IILandroid/system/StructGroupSourceReq;)V"), + NATIVE_METHOD(Linux, setsockoptLinger, "(Ljava/io/FileDescriptor;IILandroid/system/StructLinger;)V"), + NATIVE_METHOD(Linux, setsockoptTimeval, "(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V"), + NATIVE_METHOD(Linux, setuid, "(I)V"), + NATIVE_METHOD(Linux, setxattr, "(Ljava/lang/String;Ljava/lang/String;[BI)V"), + NATIVE_METHOD(Linux, shutdown, "(Ljava/io/FileDescriptor;I)V"), + NATIVE_METHOD(Linux, socket, "(III)Ljava/io/FileDescriptor;"), + NATIVE_METHOD(Linux, socketpair, "(IIILjava/io/FileDescriptor;Ljava/io/FileDescriptor;)V"), + NATIVE_METHOD(Linux, stat, "(Ljava/lang/String;)Landroid/system/StructStat;"), + NATIVE_METHOD(Linux, statvfs, "(Ljava/lang/String;)Landroid/system/StructStatVfs;"), + NATIVE_METHOD(Linux, strerror, "(I)Ljava/lang/String;"), + NATIVE_METHOD(Linux, strsignal, "(I)Ljava/lang/String;"), + NATIVE_METHOD(Linux, symlink, "(Ljava/lang/String;Ljava/lang/String;)V"), + NATIVE_METHOD(Linux, sysconf, "(I)J"), + NATIVE_METHOD(Linux, tcdrain, "(Ljava/io/FileDescriptor;)V"), + NATIVE_METHOD(Linux, tcsendbreak, "(Ljava/io/FileDescriptor;I)V"), + NATIVE_METHOD(Linux, umaskImpl, "(I)I"), + NATIVE_METHOD(Linux, uname, "()Landroid/system/StructUtsname;"), + NATIVE_METHOD(Linux, unlink, "(Ljava/lang/String;)V"), + NATIVE_METHOD(Linux, unsetenv, "(Ljava/lang/String;)V"), + NATIVE_METHOD(Linux, waitpid, "(ILandroid/util/MutableInt;I)I"), + NATIVE_METHOD(Linux, writeBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"), + NATIVE_METHOD(Linux, writev, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"), +}; +void register_libcore_io_Linux(JNIEnv* env) { + jniRegisterNativeMethods(env, "libcore/io/Linux", gMethods, NELEM(gMethods)); +} diff --git a/luni/src/main/native/libcore_io_Memory.cpp b/luni/src/main/native/libcore_io_Memory.cpp index 5122a6c1b..1acb8f48c 100644 --- a/luni/src/main/native/libcore_io_Memory.cpp +++ b/luni/src/main/native/libcore_io_Memory.cpp @@ -18,6 +18,7 @@ #include "JNIHelp.h" #include "JniConstants.h" +#include "nativehelper/jni_macros.h" #include "Portability.h" #include "ScopedBytes.h" #include "ScopedPrimitiveArray.h" @@ -124,7 +125,7 @@ static void Memory_peekByteArray(JNIEnv* env, jclass, jlong srcAddress, jbyteArr return; \ } \ const SWAP_TYPE* src = cast(srcAddress); \ - SWAP_FN(reinterpret_cast(elements.get()) + dstOffset, src, count); \ + SWAP_FN(reinterpret_cast(elements.get()) + dstOffset, src, count); /*NOLINT*/ \ } else { \ const SCALAR_TYPE* src = cast(srcAddress); \ env->Set ## JNI_NAME ## ArrayRegion(dst, dstOffset, count, src); \ @@ -177,9 +178,9 @@ static void Memory_pokeByteArray(JNIEnv* env, jclass, jlong dstAddress, jbyteArr return; \ } \ const SWAP_TYPE* src = reinterpret_cast(elements.get()) + srcOffset; \ - SWAP_FN(cast(dstAddress), src, count); \ + SWAP_FN(cast(dstAddress), src, count); /*NOLINT*/ \ } else { \ - env->Get ## JNI_NAME ## ArrayRegion(src, srcOffset, count, cast(dstAddress)); \ + env->Get ## JNI_NAME ## ArrayRegion(src, srcOffset, count, cast(dstAddress)); /*NOLINT*/ \ } \ } @@ -289,27 +290,27 @@ static void Memory_unsafeBulkPut(JNIEnv* env, jclass, jbyteArray dstArray, jint static JNINativeMethod gMethods[] = { NATIVE_METHOD(Memory, memmove, "(Ljava/lang/Object;ILjava/lang/Object;IJ)V"), - NATIVE_METHOD(Memory, peekByte, "!(J)B"), + FAST_NATIVE_METHOD(Memory, peekByte, "(J)B"), NATIVE_METHOD(Memory, peekByteArray, "(J[BII)V"), NATIVE_METHOD(Memory, peekCharArray, "(J[CIIZ)V"), NATIVE_METHOD(Memory, peekDoubleArray, "(J[DIIZ)V"), NATIVE_METHOD(Memory, peekFloatArray, "(J[FIIZ)V"), - NATIVE_METHOD(Memory, peekIntNative, "!(J)I"), + FAST_NATIVE_METHOD(Memory, peekIntNative, "(J)I"), NATIVE_METHOD(Memory, peekIntArray, "(J[IIIZ)V"), - NATIVE_METHOD(Memory, peekLongNative, "!(J)J"), + FAST_NATIVE_METHOD(Memory, peekLongNative, "(J)J"), NATIVE_METHOD(Memory, peekLongArray, "(J[JIIZ)V"), - NATIVE_METHOD(Memory, peekShortNative, "!(J)S"), + FAST_NATIVE_METHOD(Memory, peekShortNative, "(J)S"), NATIVE_METHOD(Memory, peekShortArray, "(J[SIIZ)V"), - NATIVE_METHOD(Memory, pokeByte, "!(JB)V"), + FAST_NATIVE_METHOD(Memory, pokeByte, "(JB)V"), NATIVE_METHOD(Memory, pokeByteArray, "(J[BII)V"), NATIVE_METHOD(Memory, pokeCharArray, "(J[CIIZ)V"), NATIVE_METHOD(Memory, pokeDoubleArray, "(J[DIIZ)V"), NATIVE_METHOD(Memory, pokeFloatArray, "(J[FIIZ)V"), - NATIVE_METHOD(Memory, pokeIntNative, "!(JI)V"), + FAST_NATIVE_METHOD(Memory, pokeIntNative, "(JI)V"), NATIVE_METHOD(Memory, pokeIntArray, "(J[IIIZ)V"), - NATIVE_METHOD(Memory, pokeLongNative, "!(JJ)V"), + FAST_NATIVE_METHOD(Memory, pokeLongNative, "(JJ)V"), NATIVE_METHOD(Memory, pokeLongArray, "(J[JIIZ)V"), - NATIVE_METHOD(Memory, pokeShortNative, "!(JS)V"), + FAST_NATIVE_METHOD(Memory, pokeShortNative, "(JS)V"), NATIVE_METHOD(Memory, pokeShortArray, "(J[SIIZ)V"), NATIVE_METHOD(Memory, unsafeBulkGet, "(Ljava/lang/Object;II[BIIZ)V"), NATIVE_METHOD(Memory, unsafeBulkPut, "([BIILjava/lang/Object;IIZ)V"), diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp deleted file mode 100644 index 51c2a9406..000000000 --- a/luni/src/main/native/libcore_io_Posix.cpp +++ /dev/null @@ -1,2087 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Posix" - -#include "AsynchronousCloseMonitor.h" -#include "cutils/log.h" -#include "ExecStrings.h" -#include "JNIHelp.h" -#include "JniConstants.h" -#include "JniException.h" -#include "NetworkUtilities.h" -#include "Portability.h" -#include "readlink.h" -#include "ScopedBytes.h" -#include "ScopedLocalRef.h" -#include "ScopedPrimitiveArray.h" -#include "ScopedUtfChars.h" -#include "toStringArray.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef __unused -#define __unused __attribute__((__unused__)) -#endif - -#define TO_JAVA_STRING(NAME, EXP) \ - jstring NAME = env->NewStringUTF(EXP); \ - if (NAME == NULL) return NULL; - -struct addrinfo_deleter { - void operator()(addrinfo* p) const { - if (p != NULL) { // bionic's freeaddrinfo(3) crashes when passed NULL. - freeaddrinfo(p); - } - } -}; - -struct c_deleter { - void operator()(void* p) const { - free(p); - } -}; - -static bool isIPv4MappedAddress(const sockaddr *sa) { - const sockaddr_in6 *sin6 = reinterpret_cast(sa); - return sa != NULL && sa->sa_family == AF_INET6 && - (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) || - IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)); // We map 0.0.0.0 to ::, so :: is mapped. -} - -/** - * Perform a socket operation that specifies an IP address, possibly falling back from specifying - * the address as an IPv4-mapped IPv6 address in a struct sockaddr_in6 to specifying it as an IPv4 - * address in a struct sockaddr_in. - * - * This is needed because all sockets created by the java.net APIs are IPv6 sockets, and on those - * sockets, IPv4 operations use IPv4-mapped addresses stored in a struct sockaddr_in6. But sockets - * created using Posix.socket(AF_INET, ...) are IPv4 sockets and only support operations using IPv4 - * socket addresses structures. - */ -#define NET_IPV4_FALLBACK(jni_env, return_type, syscall_name, java_fd, java_addr, port, null_addr_ok, args...) ({ \ - return_type _rc = -1; \ - do { \ - sockaddr_storage _ss; \ - socklen_t _salen; \ - if (java_addr == NULL && null_addr_ok) { \ - /* No IP address specified (e.g., sendto() on a connected socket). */ \ - _salen = 0; \ - } else if (!inetAddressToSockaddr(jni_env, java_addr, port, _ss, _salen)) { \ - /* Invalid socket address, return -1. inetAddressToSockaddr has already thrown. */ \ - break; \ - } \ - sockaddr* _sa = _salen ? reinterpret_cast(&_ss) : NULL; \ - /* inetAddressToSockaddr always returns an IPv6 sockaddr. Assume that java_fd was created \ - * by Java API calls, which always create IPv6 socket fds, and pass it in as is. */ \ - _rc = NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ##args, _sa, _salen); \ - if (_rc == -1 && errno == EAFNOSUPPORT && _salen && isIPv4MappedAddress(_sa)) { \ - /* We passed in an IPv4 address in an IPv6 sockaddr and the kernel told us that we got \ - * the address family wrong. Pass in the same address in an IPv4 sockaddr. */ \ - jni_env->ExceptionClear(); \ - if (!inetAddressToSockaddrVerbatim(jni_env, java_addr, port, _ss, _salen)) { \ - break; \ - } \ - _sa = reinterpret_cast(&_ss); \ - _rc = NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ##args, _sa, _salen); \ - } \ - } while (0); \ - _rc; }) \ - -/** - * Used to retry networking system calls that can be interrupted with a signal. Unlike - * TEMP_FAILURE_RETRY, this also handles the case where - * AsynchronousCloseMonitor::signalBlockedThreads(fd) is used to signal a close() or - * Thread.interrupt(). Other signals that result in an EINTR result are ignored and the system call - * is retried. - * - * Returns the result of the system call though a Java exception will be pending if the result is - * -1: a SocketException if signaled via AsynchronousCloseMonitor, or ErrnoException for other - * failures. - */ -#define NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \ - return_type _rc = -1; \ - int _syscallErrno; \ - do { \ - bool _wasSignaled; \ - { \ - int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \ - AsynchronousCloseMonitor _monitor(_fd); \ - _rc = syscall_name(_fd, __VA_ARGS__); \ - _syscallErrno = errno; \ - _wasSignaled = _monitor.wasSignaled(); \ - } \ - if (_wasSignaled) { \ - jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \ - _rc = -1; \ - break; \ - } \ - if (_rc == -1 && _syscallErrno != EINTR) { \ - /* TODO: with a format string we could show the arguments too, like strace(1). */ \ - throwErrnoException(jni_env, # syscall_name); \ - break; \ - } \ - } while (_rc == -1); /* _syscallErrno == EINTR && !_wasSignaled */ \ - if (_rc == -1) { \ - /* If the syscall failed, re-set errno: throwing an exception might have modified it. */ \ - errno = _syscallErrno; \ - } \ - _rc; }) - -/** - * Used to retry system calls that can be interrupted with a signal. Unlike TEMP_FAILURE_RETRY, this - * also handles the case where AsynchronousCloseMonitor::signalBlockedThreads(fd) is used to signal - * a close() or Thread.interrupt(). Other signals that result in an EINTR result are ignored and the - * system call is retried. - * - * Returns the result of the system call though a Java exception will be pending if the result is - * -1: an IOException if the file descriptor is already closed, a InterruptedIOException if signaled - * via AsynchronousCloseMonitor, or ErrnoException for other failures. - */ -#define IO_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \ - return_type _rc = -1; \ - int _syscallErrno; \ - do { \ - bool _wasSignaled; \ - { \ - int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \ - AsynchronousCloseMonitor _monitor(_fd); \ - _rc = syscall_name(_fd, __VA_ARGS__); \ - _syscallErrno = errno; \ - _wasSignaled = _monitor.wasSignaled(); \ - } \ - if (_wasSignaled) { \ - jniThrowException(jni_env, "java/io/InterruptedIOException", # syscall_name " interrupted"); \ - _rc = -1; \ - break; \ - } \ - if (_rc == -1 && _syscallErrno != EINTR) { \ - /* TODO: with a format string we could show the arguments too, like strace(1). */ \ - throwErrnoException(jni_env, # syscall_name); \ - break; \ - } \ - } while (_rc == -1); /* && _syscallErrno == EINTR && !_wasSignaled */ \ - if (_rc == -1) { \ - /* If the syscall failed, re-set errno: throwing an exception might have modified it. */ \ - errno = _syscallErrno; \ - } \ - _rc; }) - -#define NULL_ADDR_OK true -#define NULL_ADDR_FORBIDDEN false - -static void throwException(JNIEnv* env, jclass exceptionClass, jmethodID ctor3, jmethodID ctor2, - const char* functionName, int error) { - jthrowable cause = NULL; - if (env->ExceptionCheck()) { - cause = env->ExceptionOccurred(); - env->ExceptionClear(); - } - - ScopedLocalRef detailMessage(env, env->NewStringUTF(functionName)); - if (detailMessage.get() == NULL) { - // Not really much we can do here. We're probably dead in the water, - // but let's try to stumble on... - env->ExceptionClear(); - } - - jobject exception; - if (cause != NULL) { - exception = env->NewObject(exceptionClass, ctor3, detailMessage.get(), error, cause); - } else { - exception = env->NewObject(exceptionClass, ctor2, detailMessage.get(), error); - } - env->Throw(reinterpret_cast(exception)); -} - -static void throwErrnoException(JNIEnv* env, const char* functionName) { - int error = errno; - static jmethodID ctor3 = env->GetMethodID(JniConstants::errnoExceptionClass, - "", "(Ljava/lang/String;ILjava/lang/Throwable;)V"); - static jmethodID ctor2 = env->GetMethodID(JniConstants::errnoExceptionClass, - "", "(Ljava/lang/String;I)V"); - throwException(env, JniConstants::errnoExceptionClass, ctor3, ctor2, functionName, error); -} - -static void throwGaiException(JNIEnv* env, const char* functionName, int error) { - // Cache the methods ids before we throw, so we don't call GetMethodID with a pending exception. - static jmethodID ctor3 = env->GetMethodID(JniConstants::gaiExceptionClass, "", - "(Ljava/lang/String;ILjava/lang/Throwable;)V"); - static jmethodID ctor2 = env->GetMethodID(JniConstants::gaiExceptionClass, "", - "(Ljava/lang/String;I)V"); - if (errno != 0) { - // EAI_SYSTEM should mean "look at errno instead", but both glibc and bionic seem to - // mess this up. In particular, if you don't have INTERNET permission, errno will be EACCES - // but you'll get EAI_NONAME or EAI_NODATA. So we want our GaiException to have a - // potentially-relevant ErrnoException as its cause even if error != EAI_SYSTEM. - // http://code.google.com/p/android/issues/detail?id=15722 - throwErrnoException(env, functionName); - // Deliberately fall through to throw another exception... - } - throwException(env, JniConstants::gaiExceptionClass, ctor3, ctor2, functionName, error); -} - -template -static rc_t throwIfMinusOne(JNIEnv* env, const char* name, rc_t rc) { - if (rc == rc_t(-1)) { - throwErrnoException(env, name); - } - return rc; -} - -template -class IoVec { -public: - IoVec(JNIEnv* env, size_t bufferCount) : mEnv(env), mBufferCount(bufferCount) { - } - - bool init(jobjectArray javaBuffers, jintArray javaOffsets, jintArray javaByteCounts) { - // We can't delete our local references until after the I/O, so make sure we have room. - if (mEnv->PushLocalFrame(mBufferCount + 16) < 0) { - return false; - } - ScopedIntArrayRO offsets(mEnv, javaOffsets); - if (offsets.get() == NULL) { - return false; - } - ScopedIntArrayRO byteCounts(mEnv, javaByteCounts); - if (byteCounts.get() == NULL) { - return false; - } - // TODO: Linux actually has a 1024 buffer limit. glibc works around this, and we should too. - // TODO: you can query the limit at runtime with sysconf(_SC_IOV_MAX). - for (size_t i = 0; i < mBufferCount; ++i) { - jobject buffer = mEnv->GetObjectArrayElement(javaBuffers, i); // We keep this local ref. - mScopedBuffers.push_back(new ScopedT(mEnv, buffer)); - jbyte* ptr = const_cast(mScopedBuffers.back()->get()); - if (ptr == NULL) { - return false; - } - struct iovec iov; - iov.iov_base = reinterpret_cast(ptr + offsets[i]); - iov.iov_len = byteCounts[i]; - mIoVec.push_back(iov); - } - return true; - } - - ~IoVec() { - for (size_t i = 0; i < mScopedBuffers.size(); ++i) { - delete mScopedBuffers[i]; - } - mEnv->PopLocalFrame(NULL); - } - - iovec* get() { - return &mIoVec[0]; - } - - size_t size() { - return mBufferCount; - } - -private: - JNIEnv* mEnv; - size_t mBufferCount; - std::vector mIoVec; - std::vector mScopedBuffers; -}; - -/** - * Returns a jbyteArray containing the sockaddr_un.sun_path from ss. As per unix(7) sa_len should be - * the length of ss as returned by getsockname(2), getpeername(2), or accept(2). - * If the returned array is of length 0 the sockaddr_un refers to an unnamed socket. - * A null pointer is returned in the event of an error. See unix(7) for more information. - */ -static jbyteArray getUnixSocketPath(JNIEnv* env, const sockaddr_storage& ss, - const socklen_t& sa_len) { - if (ss.ss_family != AF_UNIX) { - jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", - "getUnixSocketPath unsupported ss_family: %i", ss.ss_family); - return NULL; - } - - const struct sockaddr_un* un_addr = reinterpret_cast(&ss); - // The length of sun_path is sa_len minus the length of the overhead (ss_family). - // See unix(7) for details. This calculation must match that of socket_make_sockaddr_un() in - // socket_local_client.c and javaUnixSocketAddressToSockaddr() to interoperate. - size_t pathLength = sa_len - offsetof(struct sockaddr_un, sun_path); - - jbyteArray javaSunPath = env->NewByteArray(pathLength); - if (javaSunPath == NULL) { - return NULL; - } - - if (pathLength > 0) { - env->SetByteArrayRegion(javaSunPath, 0, pathLength, - reinterpret_cast(&un_addr->sun_path)); - } - return javaSunPath; -} - -static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage& ss, const socklen_t sa_len) { - if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) { - jint port; - jobject inetAddress = sockaddrToInetAddress(env, ss, &port); - if (inetAddress == NULL) { - return NULL; // Exception already thrown. - } - static jmethodID ctor = env->GetMethodID(JniConstants::inetSocketAddressClass, - "", "(Ljava/net/InetAddress;I)V"); - return env->NewObject(JniConstants::inetSocketAddressClass, ctor, inetAddress, port); - } else if (ss.ss_family == AF_UNIX) { - static jmethodID ctor = env->GetMethodID(JniConstants::unixSocketAddressClass, - "", "([B)V"); - - jbyteArray javaSunPath = getUnixSocketPath(env, ss, sa_len); - if (!javaSunPath) { - return NULL; - } - return env->NewObject(JniConstants::unixSocketAddressClass, ctor, javaSunPath); - } else if (ss.ss_family == AF_NETLINK) { - const struct sockaddr_nl* nl_addr = reinterpret_cast(&ss); - static jmethodID ctor = env->GetMethodID(JniConstants::netlinkSocketAddressClass, - "", "(II)V"); - return env->NewObject(JniConstants::netlinkSocketAddressClass, ctor, - static_cast(nl_addr->nl_pid), - static_cast(nl_addr->nl_groups)); - } else if (ss.ss_family == AF_PACKET) { - const struct sockaddr_ll* sll = reinterpret_cast(&ss); - static jmethodID ctor = env->GetMethodID(JniConstants::packetSocketAddressClass, - "", "(SISB[B)V"); - ScopedLocalRef byteArray(env, env->NewByteArray(sll->sll_halen)); - if (byteArray.get() == NULL) { - return NULL; - } - env->SetByteArrayRegion(byteArray.get(), 0, sll->sll_halen, - reinterpret_cast(sll->sll_addr)); - jobject packetSocketAddress = env->NewObject(JniConstants::packetSocketAddressClass, ctor, - static_cast(ntohs(sll->sll_protocol)), - static_cast(sll->sll_ifindex), - static_cast(sll->sll_hatype), - static_cast(sll->sll_pkttype), - byteArray.get()); - return packetSocketAddress; - } - jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "unsupported ss_family: %d", - ss.ss_family); - return NULL; -} - -static jobject makeStructPasswd(JNIEnv* env, const struct passwd& pw) { - TO_JAVA_STRING(pw_name, pw.pw_name); - TO_JAVA_STRING(pw_dir, pw.pw_dir); - TO_JAVA_STRING(pw_shell, pw.pw_shell); - static jmethodID ctor = env->GetMethodID(JniConstants::structPasswdClass, "", - "(Ljava/lang/String;IILjava/lang/String;Ljava/lang/String;)V"); - return env->NewObject(JniConstants::structPasswdClass, ctor, - pw_name, static_cast(pw.pw_uid), static_cast(pw.pw_gid), pw_dir, pw_shell); -} - -static jobject makeStructStat(JNIEnv* env, const struct stat& sb) { - static jmethodID ctor = env->GetMethodID(JniConstants::structStatClass, "", - "(JJIJIIJJJJJJJ)V"); - return env->NewObject(JniConstants::structStatClass, ctor, - static_cast(sb.st_dev), static_cast(sb.st_ino), - static_cast(sb.st_mode), static_cast(sb.st_nlink), - static_cast(sb.st_uid), static_cast(sb.st_gid), - static_cast(sb.st_rdev), static_cast(sb.st_size), - static_cast(sb.st_atime), static_cast(sb.st_mtime), - static_cast(sb.st_ctime), static_cast(sb.st_blksize), - static_cast(sb.st_blocks)); -} - -static jobject makeStructStatVfs(JNIEnv* env, const struct statvfs& sb) { - static jmethodID ctor = env->GetMethodID(JniConstants::structStatVfsClass, "", - "(JJJJJJJJJJJ)V"); - return env->NewObject(JniConstants::structStatVfsClass, ctor, - static_cast(sb.f_bsize), - static_cast(sb.f_frsize), - static_cast(sb.f_blocks), - static_cast(sb.f_bfree), - static_cast(sb.f_bavail), - static_cast(sb.f_files), - static_cast(sb.f_ffree), - static_cast(sb.f_favail), - static_cast(sb.f_fsid), - static_cast(sb.f_flag), - static_cast(sb.f_namemax)); -} - -static jobject makeStructLinger(JNIEnv* env, const struct linger& l) { - static jmethodID ctor = env->GetMethodID(JniConstants::structLingerClass, "", "(II)V"); - return env->NewObject(JniConstants::structLingerClass, ctor, l.l_onoff, l.l_linger); -} - -static jobject makeStructTimeval(JNIEnv* env, const struct timeval& tv) { - static jmethodID ctor = env->GetMethodID(JniConstants::structTimevalClass, "", "(JJ)V"); - return env->NewObject(JniConstants::structTimevalClass, ctor, - static_cast(tv.tv_sec), static_cast(tv.tv_usec)); -} - -static jobject makeStructUcred(JNIEnv* env, const struct ucred& u __unused) { - static jmethodID ctor = env->GetMethodID(JniConstants::structUcredClass, "", "(III)V"); - return env->NewObject(JniConstants::structUcredClass, ctor, u.pid, u.uid, u.gid); -} - -static jobject makeStructUtsname(JNIEnv* env, const struct utsname& buf) { - TO_JAVA_STRING(sysname, buf.sysname); - TO_JAVA_STRING(nodename, buf.nodename); - TO_JAVA_STRING(release, buf.release); - TO_JAVA_STRING(version, buf.version); - TO_JAVA_STRING(machine, buf.machine); - static jmethodID ctor = env->GetMethodID(JniConstants::structUtsnameClass, "", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - return env->NewObject(JniConstants::structUtsnameClass, ctor, - sysname, nodename, release, version, machine); -}; - -static bool fillIfreq(JNIEnv* env, jstring javaInterfaceName, struct ifreq& req) { - ScopedUtfChars interfaceName(env, javaInterfaceName); - if (interfaceName.c_str() == NULL) { - return false; - } - memset(&req, 0, sizeof(req)); - strncpy(req.ifr_name, interfaceName.c_str(), sizeof(req.ifr_name)); - req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; - return true; -} - -static bool fillUnixSocketAddress(JNIEnv* env, jobject javaUnixSocketAddress, - const sockaddr_storage& ss, const socklen_t& sa_len) { - if (javaUnixSocketAddress == NULL) { - return true; - } - jbyteArray javaSunPath = getUnixSocketPath(env, ss, sa_len); - if (!javaSunPath) { - return false; - } - - static jfieldID sunPathFid = - env->GetFieldID(JniConstants::unixSocketAddressClass, "sun_path", "[B"); - env->SetObjectField(javaUnixSocketAddress, sunPathFid, javaSunPath); - return true; -} - -static bool fillInetSocketAddress(JNIEnv* env, jobject javaInetSocketAddress, - const sockaddr_storage& ss) { - if (javaInetSocketAddress == NULL) { - return true; - } - // Fill out the passed-in InetSocketAddress with the sender's IP address and port number. - jint port; - jobject sender = sockaddrToInetAddress(env, ss, &port); - if (sender == NULL) { - return false; - } - static jfieldID holderFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "holder", - "Ljava/net/InetSocketAddress$InetSocketAddressHolder;"); - jobject holder = env->GetObjectField(javaInetSocketAddress, holderFid); - - static jfieldID addressFid = env->GetFieldID(JniConstants::inetSocketAddressHolderClass, - "addr", "Ljava/net/InetAddress;"); - static jfieldID portFid = env->GetFieldID(JniConstants::inetSocketAddressHolderClass, "port", "I"); - env->SetObjectField(holder, addressFid, sender); - env->SetIntField(holder, portFid, port); - return true; -} - -static bool fillSocketAddress(JNIEnv* env, jobject javaSocketAddress, const sockaddr_storage& ss, - const socklen_t& sa_len) { - if (javaSocketAddress == NULL) { - return true; - } - - if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) { - return fillInetSocketAddress(env, javaSocketAddress, ss); - } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::unixSocketAddressClass)) { - return fillUnixSocketAddress(env, javaSocketAddress, ss, sa_len); - } - jniThrowException(env, "java/lang/UnsupportedOperationException", - "unsupported SocketAddress subclass"); - return false; - -} - -static void javaInetSocketAddressToInetAddressAndPort( - JNIEnv* env, jobject javaInetSocketAddress, jobject& javaInetAddress, jint& port) { - static jfieldID holderFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "holder", - "Ljava/net/InetSocketAddress$InetSocketAddressHolder;"); - jobject holder = env->GetObjectField(javaInetSocketAddress, holderFid); - - static jfieldID addressFid = env->GetFieldID( - JniConstants::inetSocketAddressHolderClass, "addr", "Ljava/net/InetAddress;"); - static jfieldID portFid = env->GetFieldID(JniConstants::inetSocketAddressHolderClass, "port", "I"); - - javaInetAddress = env->GetObjectField(holder, addressFid); - port = env->GetIntField(holder, portFid); -} - -static bool javaInetSocketAddressToSockaddr( - JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) { - jobject javaInetAddress; - jint port; - javaInetSocketAddressToInetAddressAndPort(env, javaSocketAddress, javaInetAddress, port); - return inetAddressToSockaddr(env, javaInetAddress, port, ss, sa_len); -} - -static bool javaNetlinkSocketAddressToSockaddr( - JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) { - static jfieldID nlPidFid = env->GetFieldID( - JniConstants::netlinkSocketAddressClass, "nlPortId", "I"); - static jfieldID nlGroupsFid = env->GetFieldID( - JniConstants::netlinkSocketAddressClass, "nlGroupsMask", "I"); - - sockaddr_nl *nlAddr = reinterpret_cast(&ss); - nlAddr->nl_family = AF_NETLINK; - nlAddr->nl_pid = env->GetIntField(javaSocketAddress, nlPidFid); - nlAddr->nl_groups = env->GetIntField(javaSocketAddress, nlGroupsFid); - sa_len = sizeof(sockaddr_nl); - return true; -} - -static bool javaUnixSocketAddressToSockaddr( - JNIEnv* env, jobject javaUnixSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) { - static jfieldID sunPathFid = env->GetFieldID( - JniConstants::unixSocketAddressClass, "sun_path", "[B"); - - struct sockaddr_un* un_addr = reinterpret_cast(&ss); - memset (un_addr, 0, sizeof(sockaddr_un)); - un_addr->sun_family = AF_UNIX; - - jbyteArray javaSunPath = (jbyteArray) env->GetObjectField(javaUnixSocketAddress, sunPathFid); - jsize pathLength = env->GetArrayLength(javaSunPath); - if ((size_t) pathLength > sizeof(sockaddr_un::sun_path)) { - jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", - "sun_path too long: max=%i, is=%i", - sizeof(sockaddr_un::sun_path), pathLength); - return false; - } - env->GetByteArrayRegion(javaSunPath, 0, pathLength, (jbyte*) un_addr->sun_path); - // sa_len is sun_path plus the length of the overhead (ss_family_t). See unix(7) for - // details. This calculation must match that of socket_make_sockaddr_un() in - // socket_local_client.c and getUnixSocketPath() to interoperate. - sa_len = offsetof(struct sockaddr_un, sun_path) + pathLength; - return true; -} - -static bool javaPacketSocketAddressToSockaddr( - JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) { - static jfieldID protocolFid = env->GetFieldID( - JniConstants::packetSocketAddressClass, "sll_protocol", "S"); - static jfieldID ifindexFid = env->GetFieldID( - JniConstants::packetSocketAddressClass, "sll_ifindex", "I"); - static jfieldID hatypeFid = env->GetFieldID( - JniConstants::packetSocketAddressClass, "sll_hatype", "S"); - static jfieldID pkttypeFid = env->GetFieldID( - JniConstants::packetSocketAddressClass, "sll_pkttype", "B"); - static jfieldID addrFid = env->GetFieldID( - JniConstants::packetSocketAddressClass, "sll_addr", "[B"); - - sockaddr_ll *sll = reinterpret_cast(&ss); - sll->sll_family = AF_PACKET; - sll->sll_protocol = htons(env->GetShortField(javaSocketAddress, protocolFid)); - sll->sll_ifindex = env->GetIntField(javaSocketAddress, ifindexFid); - sll->sll_hatype = env->GetShortField(javaSocketAddress, hatypeFid); - sll->sll_pkttype = env->GetByteField(javaSocketAddress, pkttypeFid); - - jbyteArray sllAddr = (jbyteArray) env->GetObjectField(javaSocketAddress, addrFid); - if (sllAddr == NULL) { - sll->sll_halen = 0; - memset(&sll->sll_addr, 0, sizeof(sll->sll_addr)); - } else { - jsize len = env->GetArrayLength(sllAddr); - if ((size_t) len > sizeof(sll->sll_addr)) { - len = sizeof(sll->sll_addr); - } - sll->sll_halen = len; - env->GetByteArrayRegion(sllAddr, 0, len, (jbyte*) sll->sll_addr); - } - sa_len = sizeof(sockaddr_ll); - return true; -} - -static bool javaSocketAddressToSockaddr( - JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) { - if (javaSocketAddress == NULL) { - jniThrowNullPointerException(env, NULL); - return false; - } - - if (env->IsInstanceOf(javaSocketAddress, JniConstants::netlinkSocketAddressClass)) { - return javaNetlinkSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len); - } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) { - return javaInetSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len); - } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::packetSocketAddressClass)) { - return javaPacketSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len); - } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::unixSocketAddressClass)) { - return javaUnixSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len); - } - jniThrowException(env, "java/lang/UnsupportedOperationException", - "unsupported SocketAddress subclass"); - return false; -} - -static jobject doStat(JNIEnv* env, jstring javaPath, bool isLstat) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return NULL; - } - struct stat sb; - int rc = isLstat ? TEMP_FAILURE_RETRY(lstat(path.c_str(), &sb)) - : TEMP_FAILURE_RETRY(stat(path.c_str(), &sb)); - if (rc == -1) { - throwErrnoException(env, isLstat ? "lstat" : "stat"); - return NULL; - } - return makeStructStat(env, sb); -} - -static jobject doGetSockName(JNIEnv* env, jobject javaFd, bool is_sockname) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - sockaddr_storage ss; - sockaddr* sa = reinterpret_cast(&ss); - socklen_t byteCount = sizeof(ss); - memset(&ss, 0, byteCount); - int rc = is_sockname ? TEMP_FAILURE_RETRY(getsockname(fd, sa, &byteCount)) - : TEMP_FAILURE_RETRY(getpeername(fd, sa, &byteCount)); - if (rc == -1) { - throwErrnoException(env, is_sockname ? "getsockname" : "getpeername"); - return NULL; - } - return makeSocketAddress(env, ss, byteCount); -} - -class Passwd { -public: - Passwd(JNIEnv* env) : mEnv(env), mResult(NULL) { - mBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX); - mBuffer.reset(new char[mBufferSize]); - } - - jobject getpwnam(const char* name) { - return process("getpwnam_r", getpwnam_r(name, &mPwd, mBuffer.get(), mBufferSize, &mResult)); - } - - jobject getpwuid(uid_t uid) { - return process("getpwuid_r", getpwuid_r(uid, &mPwd, mBuffer.get(), mBufferSize, &mResult)); - } - - struct passwd* get() { - return mResult; - } - -private: - jobject process(const char* syscall, int error) { - if (mResult == NULL) { - errno = error; - throwErrnoException(mEnv, syscall); - return NULL; - } - return makeStructPasswd(mEnv, *mResult); - } - - JNIEnv* mEnv; - std::unique_ptr mBuffer; - size_t mBufferSize; - struct passwd mPwd; - struct passwd* mResult; -}; - -static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) { - sockaddr_storage ss; - socklen_t sl = sizeof(ss); - memset(&ss, 0, sizeof(ss)); - sockaddr* peer = (javaSocketAddress != NULL) ? reinterpret_cast(&ss) : NULL; - socklen_t* peerLength = (javaSocketAddress != NULL) ? &sl : 0; - jint clientFd = NET_FAILURE_RETRY(env, int, accept, javaFd, peer, peerLength); - if (clientFd == -1 || !fillSocketAddress(env, javaSocketAddress, ss, *peerLength)) { - close(clientFd); - return NULL; - } - return (clientFd != -1) ? jniCreateFileDescriptor(env, clientFd) : NULL; -} - -static jboolean Posix_access(JNIEnv* env, jobject, jstring javaPath, jint mode) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return JNI_FALSE; - } - int rc = TEMP_FAILURE_RETRY(access(path.c_str(), mode)); - if (rc == -1) { - throwErrnoException(env, "access"); - } - return (rc == 0); -} - -static void Posix_bind(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) { - // We don't need the return value because we'll already have thrown. - (void) NET_IPV4_FALLBACK(env, int, bind, javaFd, javaAddress, port, NULL_ADDR_FORBIDDEN); -} - -static void Posix_bindSocketAddress( - JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) { - sockaddr_storage ss; - socklen_t sa_len; - if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) { - return; // Exception already thrown. - } - - const sockaddr* sa = reinterpret_cast(&ss); - // We don't need the return value because we'll already have thrown. - (void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sa_len); -} - -static void Posix_chmod(JNIEnv* env, jobject, jstring javaPath, jint mode) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "chmod", TEMP_FAILURE_RETRY(chmod(path.c_str(), mode))); -} - -static void Posix_chown(JNIEnv* env, jobject, jstring javaPath, jint uid, jint gid) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "chown", TEMP_FAILURE_RETRY(chown(path.c_str(), uid, gid))); -} - -static void Posix_close(JNIEnv* env, jobject, jobject javaFd) { - // Get the FileDescriptor's 'fd' field and clear it. - // We need to do this before we can throw an IOException (http://b/3222087). - int fd = jniGetFDFromFileDescriptor(env, javaFd); - jniSetFileDescriptorOfFD(env, javaFd, -1); - - // Even if close(2) fails with EINTR, the fd will have been closed. - // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone else's fd. - // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html - throwIfMinusOne(env, "close", close(fd)); -} - -static void Posix_connect(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) { - (void) NET_IPV4_FALLBACK(env, int, connect, javaFd, javaAddress, port, NULL_ADDR_FORBIDDEN); -} - -static void Posix_connectSocketAddress( - JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) { - sockaddr_storage ss; - socklen_t sa_len; - if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) { - return; // Exception already thrown. - } - - const sockaddr* sa = reinterpret_cast(&ss); - // We don't need the return value because we'll already have thrown. - (void) NET_FAILURE_RETRY(env, int, connect, javaFd, sa, sa_len); -} - -static jobject Posix_dup(JNIEnv* env, jobject, jobject javaOldFd) { - int oldFd = jniGetFDFromFileDescriptor(env, javaOldFd); - int newFd = throwIfMinusOne(env, "dup", TEMP_FAILURE_RETRY(dup(oldFd))); - return (newFd != -1) ? jniCreateFileDescriptor(env, newFd) : NULL; -} - -static jobject Posix_dup2(JNIEnv* env, jobject, jobject javaOldFd, jint newFd) { - int oldFd = jniGetFDFromFileDescriptor(env, javaOldFd); - int fd = throwIfMinusOne(env, "dup2", TEMP_FAILURE_RETRY(dup2(oldFd, newFd))); - return (fd != -1) ? jniCreateFileDescriptor(env, fd) : NULL; -} - -static jobjectArray Posix_environ(JNIEnv* env, jobject) { - extern char** environ; // Standard, but not in any header file. - return toStringArray(env, environ); -} - -static void Posix_execve(JNIEnv* env, jobject, jstring javaFilename, jobjectArray javaArgv, jobjectArray javaEnvp) { - ScopedUtfChars path(env, javaFilename); - if (path.c_str() == NULL) { - return; - } - - ExecStrings argv(env, javaArgv); - ExecStrings envp(env, javaEnvp); - TEMP_FAILURE_RETRY(execve(path.c_str(), argv.get(), envp.get())); - - throwErrnoException(env, "execve"); -} - -static void Posix_execv(JNIEnv* env, jobject, jstring javaFilename, jobjectArray javaArgv) { - ScopedUtfChars path(env, javaFilename); - if (path.c_str() == NULL) { - return; - } - - ExecStrings argv(env, javaArgv); - TEMP_FAILURE_RETRY(execv(path.c_str(), argv.get())); - - throwErrnoException(env, "execv"); -} - -static void Posix_fchmod(JNIEnv* env, jobject, jobject javaFd, jint mode) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "fchmod", TEMP_FAILURE_RETRY(fchmod(fd, mode))); -} - -static void Posix_fchown(JNIEnv* env, jobject, jobject javaFd, jint uid, jint gid) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "fchown", TEMP_FAILURE_RETRY(fchown(fd, uid, gid))); -} - -static jint Posix_fcntlFlock(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobject javaFlock) { - static jfieldID typeFid = env->GetFieldID(JniConstants::structFlockClass, "l_type", "S"); - static jfieldID whenceFid = env->GetFieldID(JniConstants::structFlockClass, "l_whence", "S"); - static jfieldID startFid = env->GetFieldID(JniConstants::structFlockClass, "l_start", "J"); - static jfieldID lenFid = env->GetFieldID(JniConstants::structFlockClass, "l_len", "J"); - static jfieldID pidFid = env->GetFieldID(JniConstants::structFlockClass, "l_pid", "I"); - - struct flock64 lock; - memset(&lock, 0, sizeof(lock)); - lock.l_type = env->GetShortField(javaFlock, typeFid); - lock.l_whence = env->GetShortField(javaFlock, whenceFid); - lock.l_start = env->GetLongField(javaFlock, startFid); - lock.l_len = env->GetLongField(javaFlock, lenFid); - lock.l_pid = env->GetIntField(javaFlock, pidFid); - - int rc = IO_FAILURE_RETRY(env, int, fcntl, javaFd, cmd, &lock); - if (rc != -1) { - env->SetShortField(javaFlock, typeFid, lock.l_type); - env->SetShortField(javaFlock, whenceFid, lock.l_whence); - env->SetLongField(javaFlock, startFid, lock.l_start); - env->SetLongField(javaFlock, lenFid, lock.l_len); - env->SetIntField(javaFlock, pidFid, lock.l_pid); - } - return rc; -} - -static jint Posix_fcntlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jint arg) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd, arg))); -} - -static jint Posix_fcntlVoid(JNIEnv* env, jobject, jobject javaFd, jint cmd) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd))); -} - -static void Posix_fdatasync(JNIEnv* env, jobject, jobject javaFd) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "fdatasync", TEMP_FAILURE_RETRY(fdatasync(fd))); -} - -static jobject Posix_fstat(JNIEnv* env, jobject, jobject javaFd) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - struct stat sb; - int rc = TEMP_FAILURE_RETRY(fstat(fd, &sb)); - if (rc == -1) { - throwErrnoException(env, "fstat"); - return NULL; - } - return makeStructStat(env, sb); -} - -static jobject Posix_fstatvfs(JNIEnv* env, jobject, jobject javaFd) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - struct statvfs sb; - int rc = TEMP_FAILURE_RETRY(fstatvfs(fd, &sb)); - if (rc == -1) { - throwErrnoException(env, "fstatvfs"); - return NULL; - } - return makeStructStatVfs(env, sb); -} - -static void Posix_fsync(JNIEnv* env, jobject, jobject javaFd) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "fsync", TEMP_FAILURE_RETRY(fsync(fd))); -} - -static void Posix_ftruncate(JNIEnv* env, jobject, jobject javaFd, jlong length) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "ftruncate", TEMP_FAILURE_RETRY(ftruncate64(fd, length))); -} - -static jstring Posix_gai_strerror(JNIEnv* env, jobject, jint error) { - return env->NewStringUTF(gai_strerror(error)); -} - -static jobjectArray Posix_android_getaddrinfo(JNIEnv* env, jobject, jstring javaNode, - jobject javaHints, jint netId) { - ScopedUtfChars node(env, javaNode); - if (node.c_str() == NULL) { - return NULL; - } - - static jfieldID flagsFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_flags", "I"); - static jfieldID familyFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_family", "I"); - static jfieldID socktypeFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_socktype", "I"); - static jfieldID protocolFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_protocol", "I"); - - addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = env->GetIntField(javaHints, flagsFid); - hints.ai_family = env->GetIntField(javaHints, familyFid); - hints.ai_socktype = env->GetIntField(javaHints, socktypeFid); - hints.ai_protocol = env->GetIntField(javaHints, protocolFid); - - addrinfo* addressList = NULL; - errno = 0; - int rc = android_getaddrinfofornet(node.c_str(), NULL, &hints, netId, 0, &addressList); - std::unique_ptr addressListDeleter(addressList); - if (rc != 0) { - throwGaiException(env, "android_getaddrinfo", rc); - return NULL; - } - - // Count results so we know how to size the output array. - int addressCount = 0; - for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) { - if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) { - ++addressCount; - } else { - ALOGE("android_getaddrinfo unexpected ai_family %i", ai->ai_family); - } - } - if (addressCount == 0) { - return NULL; - } - - // Prepare output array. - jobjectArray result = env->NewObjectArray(addressCount, JniConstants::inetAddressClass, NULL); - if (result == NULL) { - return NULL; - } - - // Examine returned addresses one by one, save them in the output array. - int index = 0; - for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) { - if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) { - // Unknown address family. Skip this address. - ALOGE("android_getaddrinfo unexpected ai_family %i", ai->ai_family); - continue; - } - - // Convert each IP address into a Java byte array. - sockaddr_storage& address = *reinterpret_cast(ai->ai_addr); - ScopedLocalRef inetAddress(env, sockaddrToInetAddress(env, address, NULL)); - if (inetAddress.get() == NULL) { - return NULL; - } - env->SetObjectArrayElement(result, index, inetAddress.get()); - ++index; - } - return result; -} - -static jint Posix_getegid(JNIEnv*, jobject) { - return getegid(); -} - -static jint Posix_geteuid(JNIEnv*, jobject) { - return geteuid(); -} - -static jint Posix_getgid(JNIEnv*, jobject) { - return getgid(); -} - -static jstring Posix_getenv(JNIEnv* env, jobject, jstring javaName) { - ScopedUtfChars name(env, javaName); - if (name.c_str() == NULL) { - return NULL; - } - return env->NewStringUTF(getenv(name.c_str())); -} - -static jstring Posix_getnameinfo(JNIEnv* env, jobject, jobject javaAddress, jint flags) { - sockaddr_storage ss; - socklen_t sa_len; - if (!inetAddressToSockaddrVerbatim(env, javaAddress, 0, ss, sa_len)) { - return NULL; - } - char buf[NI_MAXHOST]; // NI_MAXHOST is longer than INET6_ADDRSTRLEN. - errno = 0; - int rc = getnameinfo(reinterpret_cast(&ss), sa_len, buf, sizeof(buf), NULL, 0, flags); - if (rc != 0) { - throwGaiException(env, "getnameinfo", rc); - return NULL; - } - return env->NewStringUTF(buf); -} - -static jobject Posix_getpeername(JNIEnv* env, jobject, jobject javaFd) { - return doGetSockName(env, javaFd, false); -} - -static jint Posix_getpgid(JNIEnv* env, jobject, jint pid) { - return throwIfMinusOne(env, "getpgid", TEMP_FAILURE_RETRY(getpgid(pid))); -} - -static jint Posix_getpid(JNIEnv*, jobject) { - return TEMP_FAILURE_RETRY(getpid()); -} - -static jint Posix_getppid(JNIEnv*, jobject) { - return TEMP_FAILURE_RETRY(getppid()); -} - -static jobject Posix_getpwnam(JNIEnv* env, jobject, jstring javaName) { - ScopedUtfChars name(env, javaName); - if (name.c_str() == NULL) { - return NULL; - } - return Passwd(env).getpwnam(name.c_str()); -} - -static jobject Posix_getpwuid(JNIEnv* env, jobject, jint uid) { - return Passwd(env).getpwuid(uid); -} - -static jobject Posix_getsockname(JNIEnv* env, jobject, jobject javaFd) { - return doGetSockName(env, javaFd, true); -} - -static jint Posix_getsockoptByte(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - u_char result = 0; - socklen_t size = sizeof(result); - throwIfMinusOne(env, "getsockopt", TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &result, &size))); - return result; -} - -static jobject Posix_getsockoptInAddr(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - sockaddr_storage ss; - memset(&ss, 0, sizeof(ss)); - ss.ss_family = AF_INET; // This is only for the IPv4-only IP_MULTICAST_IF. - sockaddr_in* sa = reinterpret_cast(&ss); - socklen_t size = sizeof(sa->sin_addr); - int rc = TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &sa->sin_addr, &size)); - if (rc == -1) { - throwErrnoException(env, "getsockopt"); - return NULL; - } - return sockaddrToInetAddress(env, ss, NULL); -} - -static jint Posix_getsockoptInt(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - jint result = 0; - socklen_t size = sizeof(result); - throwIfMinusOne(env, "getsockopt", TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &result, &size))); - return result; -} - -static jobject Posix_getsockoptLinger(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - struct linger l; - socklen_t size = sizeof(l); - memset(&l, 0, size); - int rc = TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &l, &size)); - if (rc == -1) { - throwErrnoException(env, "getsockopt"); - return NULL; - } - return makeStructLinger(env, l); -} - -static jobject Posix_getsockoptTimeval(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - struct timeval tv; - socklen_t size = sizeof(tv); - memset(&tv, 0, size); - int rc = TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &tv, &size)); - if (rc == -1) { - throwErrnoException(env, "getsockopt"); - return NULL; - } - return makeStructTimeval(env, tv); -} - -static jobject Posix_getsockoptUcred(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - struct ucred u; - socklen_t size = sizeof(u); - memset(&u, 0, size); - int rc = TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &u, &size)); - if (rc == -1) { - throwErrnoException(env, "getsockopt"); - return NULL; - } - return makeStructUcred(env, u); -} - -static jint Posix_gettid(JNIEnv* env __unused, jobject) { -#if defined(__BIONIC__) - return TEMP_FAILURE_RETRY(gettid()); -#else - return syscall(__NR_gettid); -#endif -} - -static jint Posix_getuid(JNIEnv*, jobject) { - return getuid(); -} - -static jint Posix_getxattr(JNIEnv* env, jobject, jstring javaPath, - jstring javaName, jbyteArray javaOutValue) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return -1; - } - ScopedUtfChars name(env, javaName); - if (name.c_str() == NULL) { - return -1; - } - ScopedBytesRW outValue(env, javaOutValue); - if (outValue.get() == NULL) { - return -1; - } - size_t outValueLength = env->GetArrayLength(javaOutValue); - ssize_t size = getxattr(path.c_str(), name.c_str(), outValue.get(), outValueLength); - if (size < 0) { - throwErrnoException(env, "getxattr"); - } - return size; -} - -static jstring Posix_if_indextoname(JNIEnv* env, jobject, jint index) { - char buf[IF_NAMESIZE]; - char* name = if_indextoname(index, buf); - // if_indextoname(3) returns NULL on failure, which will come out of NewStringUTF unscathed. - // There's no useful information in errno, so we don't bother throwing. Callers can null-check. - return env->NewStringUTF(name); -} - -static jobject Posix_inet_pton(JNIEnv* env, jobject, jint family, jstring javaName) { - ScopedUtfChars name(env, javaName); - if (name.c_str() == NULL) { - return NULL; - } - sockaddr_storage ss; - memset(&ss, 0, sizeof(ss)); - // sockaddr_in and sockaddr_in6 are at the same address, so we can use either here. - void* dst = &reinterpret_cast(&ss)->sin_addr; - if (inet_pton(family, name.c_str(), dst) != 1) { - return NULL; - } - ss.ss_family = family; - return sockaddrToInetAddress(env, ss, NULL); -} - -static jobject Posix_ioctlInetAddress(JNIEnv* env, jobject, jobject javaFd, jint cmd, jstring javaInterfaceName) { - struct ifreq req; - if (!fillIfreq(env, javaInterfaceName, req)) { - return NULL; - } - int fd = jniGetFDFromFileDescriptor(env, javaFd); - int rc = throwIfMinusOne(env, "ioctl", TEMP_FAILURE_RETRY(ioctl(fd, cmd, &req))); - if (rc == -1) { - return NULL; - } - return sockaddrToInetAddress(env, reinterpret_cast(req.ifr_addr), NULL); -} - -static jint Posix_ioctlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobject javaArg) { - // This is complicated because ioctls may return their result by updating their argument - // or via their return value, so we need to support both. - int fd = jniGetFDFromFileDescriptor(env, javaFd); - static jfieldID valueFid = env->GetFieldID(JniConstants::mutableIntClass, "value", "I"); - jint arg = env->GetIntField(javaArg, valueFid); - int rc = throwIfMinusOne(env, "ioctl", TEMP_FAILURE_RETRY(ioctl(fd, cmd, &arg))); - if (!env->ExceptionCheck()) { - env->SetIntField(javaArg, valueFid, arg); - } - return rc; -} - -static jboolean Posix_isatty(JNIEnv* env, jobject, jobject javaFd) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - return TEMP_FAILURE_RETRY(isatty(fd)) == 1; -} - -static void Posix_kill(JNIEnv* env, jobject, jint pid, jint sig) { - throwIfMinusOne(env, "kill", TEMP_FAILURE_RETRY(kill(pid, sig))); -} - -static void Posix_lchown(JNIEnv* env, jobject, jstring javaPath, jint uid, jint gid) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "lchown", TEMP_FAILURE_RETRY(lchown(path.c_str(), uid, gid))); -} - -static void Posix_link(JNIEnv* env, jobject, jstring javaOldPath, jstring javaNewPath) { - ScopedUtfChars oldPath(env, javaOldPath); - if (oldPath.c_str() == NULL) { - return; - } - ScopedUtfChars newPath(env, javaNewPath); - if (newPath.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "link", TEMP_FAILURE_RETRY(link(oldPath.c_str(), newPath.c_str()))); -} - -static void Posix_listen(JNIEnv* env, jobject, jobject javaFd, jint backlog) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "listen", TEMP_FAILURE_RETRY(listen(fd, backlog))); -} - -static jlong Posix_lseek(JNIEnv* env, jobject, jobject javaFd, jlong offset, jint whence) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - return throwIfMinusOne(env, "lseek", TEMP_FAILURE_RETRY(lseek64(fd, offset, whence))); -} - -static jobject Posix_lstat(JNIEnv* env, jobject, jstring javaPath) { - return doStat(env, javaPath, true); -} - -static void Posix_mincore(JNIEnv* env, jobject, jlong address, jlong byteCount, jbyteArray javaVector) { - ScopedByteArrayRW vector(env, javaVector); - if (vector.get() == NULL) { - return; - } - void* ptr = reinterpret_cast(static_cast(address)); - unsigned char* vec = reinterpret_cast(vector.get()); - throwIfMinusOne(env, "mincore", TEMP_FAILURE_RETRY(mincore(ptr, byteCount, vec))); -} - -static void Posix_mkdir(JNIEnv* env, jobject, jstring javaPath, jint mode) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "mkdir", TEMP_FAILURE_RETRY(mkdir(path.c_str(), mode))); -} - -static void Posix_mkfifo(JNIEnv* env, jobject, jstring javaPath, jint mode) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "mkfifo", TEMP_FAILURE_RETRY(mkfifo(path.c_str(), mode))); -} - -static void Posix_mlock(JNIEnv* env, jobject, jlong address, jlong byteCount) { - void* ptr = reinterpret_cast(static_cast(address)); - throwIfMinusOne(env, "mlock", TEMP_FAILURE_RETRY(mlock(ptr, byteCount))); -} - -static jlong Posix_mmap(JNIEnv* env, jobject, jlong address, jlong byteCount, jint prot, jint flags, jobject javaFd, jlong offset) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - void* suggestedPtr = reinterpret_cast(static_cast(address)); - void* ptr = mmap(suggestedPtr, byteCount, prot, flags, fd, offset); - if (ptr == MAP_FAILED) { - throwErrnoException(env, "mmap"); - } - return static_cast(reinterpret_cast(ptr)); -} - -static void Posix_msync(JNIEnv* env, jobject, jlong address, jlong byteCount, jint flags) { - void* ptr = reinterpret_cast(static_cast(address)); - throwIfMinusOne(env, "msync", TEMP_FAILURE_RETRY(msync(ptr, byteCount, flags))); -} - -static void Posix_munlock(JNIEnv* env, jobject, jlong address, jlong byteCount) { - void* ptr = reinterpret_cast(static_cast(address)); - throwIfMinusOne(env, "munlock", TEMP_FAILURE_RETRY(munlock(ptr, byteCount))); -} - -static void Posix_munmap(JNIEnv* env, jobject, jlong address, jlong byteCount) { - void* ptr = reinterpret_cast(static_cast(address)); - throwIfMinusOne(env, "munmap", TEMP_FAILURE_RETRY(munmap(ptr, byteCount))); -} - -static jobject Posix_open(JNIEnv* env, jobject, jstring javaPath, jint flags, jint mode) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return NULL; - } - int fd = throwIfMinusOne(env, "open", TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode))); - return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL; -} - -static jobjectArray Posix_pipe2(JNIEnv* env, jobject, jint flags __unused) { - int fds[2]; - throwIfMinusOne(env, "pipe2", TEMP_FAILURE_RETRY(pipe2(&fds[0], flags))); - jobjectArray result = env->NewObjectArray(2, JniConstants::fileDescriptorClass, NULL); - if (result == NULL) { - return NULL; - } - for (int i = 0; i < 2; ++i) { - ScopedLocalRef fd(env, jniCreateFileDescriptor(env, fds[i])); - if (fd.get() == NULL) { - return NULL; - } - env->SetObjectArrayElement(result, i, fd.get()); - if (env->ExceptionCheck()) { - return NULL; - } - } - return result; -} - -static jint Posix_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint timeoutMs) { - static jfieldID fdFid = env->GetFieldID(JniConstants::structPollfdClass, "fd", "Ljava/io/FileDescriptor;"); - static jfieldID eventsFid = env->GetFieldID(JniConstants::structPollfdClass, "events", "S"); - static jfieldID reventsFid = env->GetFieldID(JniConstants::structPollfdClass, "revents", "S"); - - // Turn the Java android.system.StructPollfd[] into a C++ struct pollfd[]. - size_t arrayLength = env->GetArrayLength(javaStructs); - std::unique_ptr fds(new struct pollfd[arrayLength]); - memset(fds.get(), 0, sizeof(struct pollfd) * arrayLength); - size_t count = 0; // Some trailing array elements may be irrelevant. (See below.) - for (size_t i = 0; i < arrayLength; ++i) { - ScopedLocalRef javaStruct(env, env->GetObjectArrayElement(javaStructs, i)); - if (javaStruct.get() == NULL) { - break; // We allow trailing nulls in the array for caller convenience. - } - ScopedLocalRef javaFd(env, env->GetObjectField(javaStruct.get(), fdFid)); - if (javaFd.get() == NULL) { - break; // We also allow callers to just clear the fd field (this is what Selector does). - } - fds[count].fd = jniGetFDFromFileDescriptor(env, javaFd.get()); - fds[count].events = env->GetShortField(javaStruct.get(), eventsFid); - ++count; - } - - std::vector monitors; - for (size_t i = 0; i < count; ++i) { - monitors.push_back(new AsynchronousCloseMonitor(fds[i].fd)); - } - - int rc; - while (true) { - timespec before; - clock_gettime(CLOCK_MONOTONIC, &before); - - rc = poll(fds.get(), count, timeoutMs); - if (rc >= 0 || errno != EINTR) { - break; - } - - // We got EINTR. Work out how much of the original timeout is still left. - if (timeoutMs > 0) { - timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - timespec diff; - diff.tv_sec = now.tv_sec - before.tv_sec; - diff.tv_nsec = now.tv_nsec - before.tv_nsec; - if (diff.tv_nsec < 0) { - --diff.tv_sec; - diff.tv_nsec += 1000000000; - } - - jint diffMs = diff.tv_sec * 1000 + diff.tv_nsec / 1000000; - if (diffMs >= timeoutMs) { - rc = 0; // We have less than 1ms left anyway, so just time out. - break; - } - - timeoutMs -= diffMs; - } - } - - for (size_t i = 0; i < monitors.size(); ++i) { - delete monitors[i]; - } - if (rc == -1) { - throwErrnoException(env, "poll"); - return -1; - } - - // Update the revents fields in the Java android.system.StructPollfd[]. - for (size_t i = 0; i < count; ++i) { - ScopedLocalRef javaStruct(env, env->GetObjectArrayElement(javaStructs, i)); - if (javaStruct.get() == NULL) { - return -1; - } - env->SetShortField(javaStruct.get(), reventsFid, fds[i].revents); - } - return rc; -} - -static void Posix_posix_fallocate(JNIEnv* env, jobject, jobject javaFd __unused, - jlong offset __unused, jlong length __unused) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - while ((errno = posix_fallocate64(fd, offset, length)) == EINTR) { - } - if (errno != 0) { - throwErrnoException(env, "posix_fallocate"); - } -} - -static jint Posix_prctl(JNIEnv* env, jobject, jint option __unused, jlong arg2 __unused, - jlong arg3 __unused, jlong arg4 __unused, jlong arg5 __unused) { - int result = TEMP_FAILURE_RETRY(prctl(static_cast(option), - static_cast(arg2), - static_cast(arg3), - static_cast(arg4), - static_cast(arg5))); - return throwIfMinusOne(env, "prctl", result); -} - -static jint Posix_preadBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jlong offset) { - ScopedBytesRW bytes(env, javaBytes); - if (bytes.get() == NULL) { - return -1; - } - return IO_FAILURE_RETRY(env, ssize_t, pread64, javaFd, bytes.get() + byteOffset, byteCount, offset); -} - -static jint Posix_pwriteBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount, jlong offset) { - ScopedBytesRO bytes(env, javaBytes); - if (bytes.get() == NULL) { - return -1; - } - return IO_FAILURE_RETRY(env, ssize_t, pwrite64, javaFd, bytes.get() + byteOffset, byteCount, offset); -} - -static jint Posix_readBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount) { - ScopedBytesRW bytes(env, javaBytes); - if (bytes.get() == NULL) { - return -1; - } - return IO_FAILURE_RETRY(env, ssize_t, read, javaFd, bytes.get() + byteOffset, byteCount); -} - -static jstring Posix_readlink(JNIEnv* env, jobject, jstring javaPath) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return NULL; - } - - std::string result; - if (!readlink(path.c_str(), result)) { - throwErrnoException(env, "readlink"); - return NULL; - } - return env->NewStringUTF(result.c_str()); -} - -static jstring Posix_realpath(JNIEnv* env, jobject, jstring javaPath) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return NULL; - } - - std::unique_ptr real_path(realpath(path.c_str(), nullptr)); - if (real_path.get() == nullptr) { - throwErrnoException(env, "realpath"); - return NULL; - } - - return env->NewStringUTF(real_path.get()); -} - -static jint Posix_readv(JNIEnv* env, jobject, jobject javaFd, jobjectArray buffers, jintArray offsets, jintArray byteCounts) { - IoVec ioVec(env, env->GetArrayLength(buffers)); - if (!ioVec.init(buffers, offsets, byteCounts)) { - return -1; - } - return IO_FAILURE_RETRY(env, ssize_t, readv, javaFd, ioVec.get(), ioVec.size()); -} - -static jint Posix_recvfromBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetSocketAddress) { - ScopedBytesRW bytes(env, javaBytes); - if (bytes.get() == NULL) { - return -1; - } - sockaddr_storage ss; - socklen_t sl = sizeof(ss); - memset(&ss, 0, sizeof(ss)); - sockaddr* from = (javaInetSocketAddress != NULL) ? reinterpret_cast(&ss) : NULL; - socklen_t* fromLength = (javaInetSocketAddress != NULL) ? &sl : 0; - jint recvCount = NET_FAILURE_RETRY(env, ssize_t, recvfrom, javaFd, bytes.get() + byteOffset, byteCount, flags, from, fromLength); - if (recvCount > 0) { - fillInetSocketAddress(env, javaInetSocketAddress, ss); - } - return recvCount; -} - -static void Posix_remove(JNIEnv* env, jobject, jstring javaPath) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "remove", TEMP_FAILURE_RETRY(remove(path.c_str()))); -} - -static void Posix_removexattr(JNIEnv* env, jobject, jstring javaPath, jstring javaName) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return; - } - ScopedUtfChars name(env, javaName); - if (name.c_str() == NULL) { - return; - } - - int res = removexattr(path.c_str(), name.c_str()); - if (res < 0) { - throwErrnoException(env, "removexattr"); - } -} - -static void Posix_rename(JNIEnv* env, jobject, jstring javaOldPath, jstring javaNewPath) { - ScopedUtfChars oldPath(env, javaOldPath); - if (oldPath.c_str() == NULL) { - return; - } - ScopedUtfChars newPath(env, javaNewPath); - if (newPath.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "rename", TEMP_FAILURE_RETRY(rename(oldPath.c_str(), newPath.c_str()))); -} - -static jlong Posix_sendfile(JNIEnv* env, jobject, jobject javaOutFd, jobject javaInFd, jobject javaOffset, jlong byteCount) { - int outFd = jniGetFDFromFileDescriptor(env, javaOutFd); - int inFd = jniGetFDFromFileDescriptor(env, javaInFd); - static jfieldID valueFid = env->GetFieldID(JniConstants::mutableLongClass, "value", "J"); - off_t offset = 0; - off_t* offsetPtr = NULL; - if (javaOffset != NULL) { - // TODO: fix bionic so we can have a 64-bit off_t! - offset = env->GetLongField(javaOffset, valueFid); - offsetPtr = &offset; - } - jlong result = throwIfMinusOne(env, "sendfile", TEMP_FAILURE_RETRY(sendfile(outFd, inFd, offsetPtr, byteCount))); - if (javaOffset != NULL) { - env->SetLongField(javaOffset, valueFid, offset); - } - return result; -} - -static jint Posix_sendtoBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetAddress, jint port) { - ScopedBytesRO bytes(env, javaBytes); - if (bytes.get() == NULL) { - return -1; - } - - return NET_IPV4_FALLBACK(env, ssize_t, sendto, javaFd, javaInetAddress, port, - NULL_ADDR_OK, bytes.get() + byteOffset, byteCount, flags); -} - -static jint Posix_sendtoBytesSocketAddress(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaSocketAddress) { - if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) { - // Use the InetAddress version so we get the benefit of NET_IPV4_FALLBACK. - jobject javaInetAddress; - jint port; - javaInetSocketAddressToInetAddressAndPort(env, javaSocketAddress, javaInetAddress, port); - return Posix_sendtoBytes(env, NULL, javaFd, javaBytes, byteOffset, byteCount, flags, - javaInetAddress, port); - } - - ScopedBytesRO bytes(env, javaBytes); - if (bytes.get() == NULL) { - return -1; - } - - sockaddr_storage ss; - socklen_t sa_len; - if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) { - return -1; - } - - const sockaddr* sa = reinterpret_cast(&ss); - // We don't need the return value because we'll already have thrown. - return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, sa, sa_len); -} - -static void Posix_setegid(JNIEnv* env, jobject, jint egid) { - throwIfMinusOne(env, "setegid", TEMP_FAILURE_RETRY(setegid(egid))); -} - -static void Posix_setenv(JNIEnv* env, jobject, jstring javaName, jstring javaValue, jboolean overwrite) { - ScopedUtfChars name(env, javaName); - if (name.c_str() == NULL) { - return; - } - ScopedUtfChars value(env, javaValue); - if (value.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "setenv", setenv(name.c_str(), value.c_str(), overwrite)); -} - -static void Posix_seteuid(JNIEnv* env, jobject, jint euid) { - throwIfMinusOne(env, "seteuid", TEMP_FAILURE_RETRY(seteuid(euid))); -} - -static void Posix_setgid(JNIEnv* env, jobject, jint gid) { - throwIfMinusOne(env, "setgid", TEMP_FAILURE_RETRY(setgid(gid))); -} - -static void Posix_setpgid(JNIEnv* env, jobject, jint pid, int pgid) { - throwIfMinusOne(env, "setpgid", TEMP_FAILURE_RETRY(setpgid(pid, pgid))); -} - -static void Posix_setregid(JNIEnv* env, jobject, jint rgid, int egid) { - throwIfMinusOne(env, "setregid", TEMP_FAILURE_RETRY(setregid(rgid, egid))); -} - -static void Posix_setreuid(JNIEnv* env, jobject, jint ruid, int euid) { - throwIfMinusOne(env, "setreuid", TEMP_FAILURE_RETRY(setreuid(ruid, euid))); -} - -static jint Posix_setsid(JNIEnv* env, jobject) { - return throwIfMinusOne(env, "setsid", TEMP_FAILURE_RETRY(setsid())); -} - -static void Posix_setsockoptByte(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jint value) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - u_char byte = value; - throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &byte, sizeof(byte)))); -} - -static void Posix_setsockoptIfreq(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jstring javaInterfaceName) { - struct ifreq req; - if (!fillIfreq(env, javaInterfaceName, req)) { - return; - } - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req, sizeof(req)))); -} - -static void Posix_setsockoptInt(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jint value) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &value, sizeof(value)))); -} - -static void Posix_setsockoptIpMreqn(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jint value) { - ip_mreqn req; - memset(&req, 0, sizeof(req)); - req.imr_ifindex = value; - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req, sizeof(req)))); -} - -static void Posix_setsockoptGroupReq(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jobject javaGroupReq) { - struct group_req req; - memset(&req, 0, sizeof(req)); - - static jfieldID grInterfaceFid = env->GetFieldID(JniConstants::structGroupReqClass, "gr_interface", "I"); - req.gr_interface = env->GetIntField(javaGroupReq, grInterfaceFid); - // Get the IPv4 or IPv6 multicast address to join or leave. - static jfieldID grGroupFid = env->GetFieldID(JniConstants::structGroupReqClass, "gr_group", "Ljava/net/InetAddress;"); - ScopedLocalRef javaGroup(env, env->GetObjectField(javaGroupReq, grGroupFid)); - socklen_t sa_len; - if (!inetAddressToSockaddrVerbatim(env, javaGroup.get(), 0, req.gr_group, sa_len)) { - return; - } - - int fd = jniGetFDFromFileDescriptor(env, javaFd); - int rc = TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req, sizeof(req))); - if (rc == -1 && errno == EINVAL) { - // Maybe we're a 32-bit binary talking to a 64-bit kernel? - // glibc doesn't automatically handle this. - // http://sourceware.org/bugzilla/show_bug.cgi?id=12080 - struct group_req64 { - uint32_t gr_interface; - uint32_t my_padding; - sockaddr_storage gr_group; - }; - group_req64 req64; - req64.gr_interface = req.gr_interface; - memcpy(&req64.gr_group, &req.gr_group, sizeof(req.gr_group)); - rc = TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req64, sizeof(req64))); - } - throwIfMinusOne(env, "setsockopt", rc); -} - -static void Posix_setsockoptGroupSourceReq(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jobject javaGroupSourceReq) { - socklen_t sa_len; - struct group_source_req req; - memset(&req, 0, sizeof(req)); - - static jfieldID gsrInterfaceFid = env->GetFieldID(JniConstants::structGroupSourceReqClass, "gsr_interface", "I"); - req.gsr_interface = env->GetIntField(javaGroupSourceReq, gsrInterfaceFid); - // Get the IPv4 or IPv6 multicast address to join or leave. - static jfieldID gsrGroupFid = env->GetFieldID(JniConstants::structGroupSourceReqClass, "gsr_group", "Ljava/net/InetAddress;"); - ScopedLocalRef javaGroup(env, env->GetObjectField(javaGroupSourceReq, gsrGroupFid)); - if (!inetAddressToSockaddrVerbatim(env, javaGroup.get(), 0, req.gsr_group, sa_len)) { - return; - } - - // Get the IPv4 or IPv6 multicast address to add to the filter. - static jfieldID gsrSourceFid = env->GetFieldID(JniConstants::structGroupSourceReqClass, "gsr_source", "Ljava/net/InetAddress;"); - ScopedLocalRef javaSource(env, env->GetObjectField(javaGroupSourceReq, gsrSourceFid)); - if (!inetAddressToSockaddrVerbatim(env, javaSource.get(), 0, req.gsr_source, sa_len)) { - return; - } - - int fd = jniGetFDFromFileDescriptor(env, javaFd); - int rc = TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req, sizeof(req))); - if (rc == -1 && errno == EINVAL) { - // Maybe we're a 32-bit binary talking to a 64-bit kernel? - // glibc doesn't automatically handle this. - // http://sourceware.org/bugzilla/show_bug.cgi?id=12080 - struct group_source_req64 { - uint32_t gsr_interface; - uint32_t my_padding; - sockaddr_storage gsr_group; - sockaddr_storage gsr_source; - }; - group_source_req64 req64; - req64.gsr_interface = req.gsr_interface; - memcpy(&req64.gsr_group, &req.gsr_group, sizeof(req.gsr_group)); - memcpy(&req64.gsr_source, &req.gsr_source, sizeof(req.gsr_source)); - rc = TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req64, sizeof(req64))); - } - throwIfMinusOne(env, "setsockopt", rc); -} - -static void Posix_setsockoptLinger(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jobject javaLinger) { - static jfieldID lOnoffFid = env->GetFieldID(JniConstants::structLingerClass, "l_onoff", "I"); - static jfieldID lLingerFid = env->GetFieldID(JniConstants::structLingerClass, "l_linger", "I"); - int fd = jniGetFDFromFileDescriptor(env, javaFd); - struct linger value; - value.l_onoff = env->GetIntField(javaLinger, lOnoffFid); - value.l_linger = env->GetIntField(javaLinger, lLingerFid); - throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &value, sizeof(value)))); -} - -static void Posix_setsockoptTimeval(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jobject javaTimeval) { - static jfieldID tvSecFid = env->GetFieldID(JniConstants::structTimevalClass, "tv_sec", "J"); - static jfieldID tvUsecFid = env->GetFieldID(JniConstants::structTimevalClass, "tv_usec", "J"); - int fd = jniGetFDFromFileDescriptor(env, javaFd); - struct timeval value; - value.tv_sec = env->GetLongField(javaTimeval, tvSecFid); - value.tv_usec = env->GetLongField(javaTimeval, tvUsecFid); - throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &value, sizeof(value)))); -} - -static void Posix_setuid(JNIEnv* env, jobject, jint uid) { - throwIfMinusOne(env, "setuid", TEMP_FAILURE_RETRY(setuid(uid))); -} - -static void Posix_setxattr(JNIEnv* env, jobject, jstring javaPath, jstring javaName, - jbyteArray javaValue, jint flags) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return; - } - ScopedUtfChars name(env, javaName); - if (name.c_str() == NULL) { - return; - } - ScopedBytesRO value(env, javaValue); - if (value.get() == NULL) { - return; - } - size_t valueLength = env->GetArrayLength(javaValue); - int res = setxattr(path.c_str(), name.c_str(), value.get(), valueLength, flags); - if (res < 0) { - throwErrnoException(env, "setxattr"); - } -} - -static void Posix_shutdown(JNIEnv* env, jobject, jobject javaFd, jint how) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "shutdown", TEMP_FAILURE_RETRY(shutdown(fd, how))); -} - -static jobject Posix_socket(JNIEnv* env, jobject, jint domain, jint type, jint protocol) { - if (domain == AF_PACKET) { - protocol = htons(protocol); // Packet sockets specify the protocol in host byte order. - } - int fd = throwIfMinusOne(env, "socket", TEMP_FAILURE_RETRY(socket(domain, type, protocol))); - return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL; -} - -static void Posix_socketpair(JNIEnv* env, jobject, jint domain, jint type, jint protocol, jobject javaFd1, jobject javaFd2) { - int fds[2]; - int rc = throwIfMinusOne(env, "socketpair", TEMP_FAILURE_RETRY(socketpair(domain, type, protocol, fds))); - if (rc != -1) { - jniSetFileDescriptorOfFD(env, javaFd1, fds[0]); - jniSetFileDescriptorOfFD(env, javaFd2, fds[1]); - } -} - -static jobject Posix_stat(JNIEnv* env, jobject, jstring javaPath) { - return doStat(env, javaPath, false); -} - -static jobject Posix_statvfs(JNIEnv* env, jobject, jstring javaPath) { - ScopedUtfChars path(env, javaPath); - if (path.c_str() == NULL) { - return NULL; - } - struct statvfs sb; - int rc = TEMP_FAILURE_RETRY(statvfs(path.c_str(), &sb)); - if (rc == -1) { - throwErrnoException(env, "statvfs"); - return NULL; - } - return makeStructStatVfs(env, sb); -} - -static jstring Posix_strerror(JNIEnv* env, jobject, jint errnum) { - char buffer[BUFSIZ]; - const char* message = jniStrError(errnum, buffer, sizeof(buffer)); - return env->NewStringUTF(message); -} - -static jstring Posix_strsignal(JNIEnv* env, jobject, jint signal) { - return env->NewStringUTF(strsignal(signal)); -} - -static void Posix_symlink(JNIEnv* env, jobject, jstring javaOldPath, jstring javaNewPath) { - ScopedUtfChars oldPath(env, javaOldPath); - if (oldPath.c_str() == NULL) { - return; - } - ScopedUtfChars newPath(env, javaNewPath); - if (newPath.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "symlink", TEMP_FAILURE_RETRY(symlink(oldPath.c_str(), newPath.c_str()))); -} - -static jlong Posix_sysconf(JNIEnv* env, jobject, jint name) { - // Since -1 is a valid result from sysconf(3), detecting failure is a little more awkward. - errno = 0; - long result = sysconf(name); - if (result == -1L && errno == EINVAL) { - throwErrnoException(env, "sysconf"); - } - return result; -} - -static void Posix_tcdrain(JNIEnv* env, jobject, jobject javaFd) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "tcdrain", TEMP_FAILURE_RETRY(tcdrain(fd))); -} - -static void Posix_tcsendbreak(JNIEnv* env, jobject, jobject javaFd, jint duration) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); - throwIfMinusOne(env, "tcsendbreak", TEMP_FAILURE_RETRY(tcsendbreak(fd, duration))); -} - -static jint Posix_umaskImpl(JNIEnv*, jobject, jint mask) { - return umask(mask); -} - -static jobject Posix_uname(JNIEnv* env, jobject) { - struct utsname buf; - if (TEMP_FAILURE_RETRY(uname(&buf)) == -1) { - return NULL; // Can't happen. - } - return makeStructUtsname(env, buf); -} - -static void Posix_unlink(JNIEnv* env, jobject, jstring javaPathname) { - ScopedUtfChars pathname(env, javaPathname); - if (pathname.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "unlink", unlink(pathname.c_str())); -} - -static void Posix_unsetenv(JNIEnv* env, jobject, jstring javaName) { - ScopedUtfChars name(env, javaName); - if (name.c_str() == NULL) { - return; - } - throwIfMinusOne(env, "unsetenv", unsetenv(name.c_str())); -} - -static jint Posix_waitpid(JNIEnv* env, jobject, jint pid, jobject javaStatus, jint options) { - int status; - int rc = throwIfMinusOne(env, "waitpid", TEMP_FAILURE_RETRY(waitpid(pid, &status, options))); - if (rc != -1) { - static jfieldID valueFid = env->GetFieldID(JniConstants::mutableIntClass, "value", "I"); - env->SetIntField(javaStatus, valueFid, status); - } - return rc; -} - -static jint Posix_writeBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount) { - ScopedBytesRO bytes(env, javaBytes); - if (bytes.get() == NULL) { - return -1; - } - return IO_FAILURE_RETRY(env, ssize_t, write, javaFd, bytes.get() + byteOffset, byteCount); -} - -static jint Posix_writev(JNIEnv* env, jobject, jobject javaFd, jobjectArray buffers, jintArray offsets, jintArray byteCounts) { - IoVec ioVec(env, env->GetArrayLength(buffers)); - if (!ioVec.init(buffers, offsets, byteCounts)) { - return -1; - } - return IO_FAILURE_RETRY(env, ssize_t, writev, javaFd, ioVec.get(), ioVec.size()); -} - -#define NATIVE_METHOD_OVERLOAD(className, functionName, signature, variant) \ - { #functionName, signature, reinterpret_cast(className ## _ ## functionName ## variant) } - -static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)Ljava/io/FileDescriptor;"), - NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"), - NATIVE_METHOD(Posix, android_getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;I)[Ljava/net/InetAddress;"), - NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"), - NATIVE_METHOD_OVERLOAD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V", SocketAddress), - NATIVE_METHOD(Posix, chmod, "(Ljava/lang/String;I)V"), - NATIVE_METHOD(Posix, chown, "(Ljava/lang/String;II)V"), - NATIVE_METHOD(Posix, close, "(Ljava/io/FileDescriptor;)V"), - NATIVE_METHOD(Posix, connect, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"), - NATIVE_METHOD_OVERLOAD(Posix, connect, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V", SocketAddress), - NATIVE_METHOD(Posix, dup, "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;"), - NATIVE_METHOD(Posix, dup2, "(Ljava/io/FileDescriptor;I)Ljava/io/FileDescriptor;"), - NATIVE_METHOD(Posix, environ, "()[Ljava/lang/String;"), - NATIVE_METHOD(Posix, execv, "(Ljava/lang/String;[Ljava/lang/String;)V"), - NATIVE_METHOD(Posix, execve, "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V"), - NATIVE_METHOD(Posix, fchmod, "(Ljava/io/FileDescriptor;I)V"), - NATIVE_METHOD(Posix, fchown, "(Ljava/io/FileDescriptor;II)V"), - NATIVE_METHOD(Posix, fcntlFlock, "(Ljava/io/FileDescriptor;ILandroid/system/StructFlock;)I"), - NATIVE_METHOD(Posix, fcntlInt, "(Ljava/io/FileDescriptor;II)I"), - NATIVE_METHOD(Posix, fcntlVoid, "(Ljava/io/FileDescriptor;I)I"), - NATIVE_METHOD(Posix, fdatasync, "(Ljava/io/FileDescriptor;)V"), - NATIVE_METHOD(Posix, fstat, "(Ljava/io/FileDescriptor;)Landroid/system/StructStat;"), - NATIVE_METHOD(Posix, fstatvfs, "(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs;"), - NATIVE_METHOD(Posix, fsync, "(Ljava/io/FileDescriptor;)V"), - NATIVE_METHOD(Posix, ftruncate, "(Ljava/io/FileDescriptor;J)V"), - NATIVE_METHOD(Posix, gai_strerror, "(I)Ljava/lang/String;"), - NATIVE_METHOD(Posix, getegid, "()I"), - NATIVE_METHOD(Posix, geteuid, "()I"), - NATIVE_METHOD(Posix, getgid, "()I"), - NATIVE_METHOD(Posix, getenv, "(Ljava/lang/String;)Ljava/lang/String;"), - NATIVE_METHOD(Posix, getnameinfo, "(Ljava/net/InetAddress;I)Ljava/lang/String;"), - NATIVE_METHOD(Posix, getpeername, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"), - NATIVE_METHOD(Posix, getpgid, "(I)I"), - NATIVE_METHOD(Posix, getpid, "()I"), - NATIVE_METHOD(Posix, getppid, "()I"), - NATIVE_METHOD(Posix, getpwnam, "(Ljava/lang/String;)Landroid/system/StructPasswd;"), - NATIVE_METHOD(Posix, getpwuid, "(I)Landroid/system/StructPasswd;"), - NATIVE_METHOD(Posix, getsockname, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"), - NATIVE_METHOD(Posix, getsockoptByte, "(Ljava/io/FileDescriptor;II)I"), - NATIVE_METHOD(Posix, getsockoptInAddr, "(Ljava/io/FileDescriptor;II)Ljava/net/InetAddress;"), - NATIVE_METHOD(Posix, getsockoptInt, "(Ljava/io/FileDescriptor;II)I"), - NATIVE_METHOD(Posix, getsockoptLinger, "(Ljava/io/FileDescriptor;II)Landroid/system/StructLinger;"), - NATIVE_METHOD(Posix, getsockoptTimeval, "(Ljava/io/FileDescriptor;II)Landroid/system/StructTimeval;"), - NATIVE_METHOD(Posix, getsockoptUcred, "(Ljava/io/FileDescriptor;II)Landroid/system/StructUcred;"), - NATIVE_METHOD(Posix, gettid, "()I"), - NATIVE_METHOD(Posix, getuid, "()I"), - NATIVE_METHOD(Posix, getxattr, "(Ljava/lang/String;Ljava/lang/String;[B)I"), - NATIVE_METHOD(Posix, if_indextoname, "(I)Ljava/lang/String;"), - NATIVE_METHOD(Posix, inet_pton, "(ILjava/lang/String;)Ljava/net/InetAddress;"), - NATIVE_METHOD(Posix, ioctlInetAddress, "(Ljava/io/FileDescriptor;ILjava/lang/String;)Ljava/net/InetAddress;"), - NATIVE_METHOD(Posix, ioctlInt, "(Ljava/io/FileDescriptor;ILandroid/util/MutableInt;)I"), - NATIVE_METHOD(Posix, isatty, "(Ljava/io/FileDescriptor;)Z"), - NATIVE_METHOD(Posix, kill, "(II)V"), - NATIVE_METHOD(Posix, lchown, "(Ljava/lang/String;II)V"), - NATIVE_METHOD(Posix, link, "(Ljava/lang/String;Ljava/lang/String;)V"), - NATIVE_METHOD(Posix, listen, "(Ljava/io/FileDescriptor;I)V"), - NATIVE_METHOD(Posix, lseek, "(Ljava/io/FileDescriptor;JI)J"), - NATIVE_METHOD(Posix, lstat, "(Ljava/lang/String;)Landroid/system/StructStat;"), - NATIVE_METHOD(Posix, mincore, "(JJ[B)V"), - NATIVE_METHOD(Posix, mkdir, "(Ljava/lang/String;I)V"), - NATIVE_METHOD(Posix, mkfifo, "(Ljava/lang/String;I)V"), - NATIVE_METHOD(Posix, mlock, "(JJ)V"), - NATIVE_METHOD(Posix, mmap, "(JJIILjava/io/FileDescriptor;J)J"), - NATIVE_METHOD(Posix, msync, "(JJI)V"), - NATIVE_METHOD(Posix, munlock, "(JJ)V"), - NATIVE_METHOD(Posix, munmap, "(JJ)V"), - NATIVE_METHOD(Posix, open, "(Ljava/lang/String;II)Ljava/io/FileDescriptor;"), - NATIVE_METHOD(Posix, pipe2, "(I)[Ljava/io/FileDescriptor;"), - NATIVE_METHOD(Posix, poll, "([Landroid/system/StructPollfd;I)I"), - NATIVE_METHOD(Posix, posix_fallocate, "(Ljava/io/FileDescriptor;JJ)V"), - NATIVE_METHOD(Posix, prctl, "(IJJJJ)I"), - NATIVE_METHOD(Posix, preadBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIJ)I"), - NATIVE_METHOD(Posix, pwriteBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIJ)I"), - NATIVE_METHOD(Posix, readBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"), - NATIVE_METHOD(Posix, readlink, "(Ljava/lang/String;)Ljava/lang/String;"), - NATIVE_METHOD(Posix, realpath, "(Ljava/lang/String;)Ljava/lang/String;"), - NATIVE_METHOD(Posix, readv, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"), - NATIVE_METHOD(Posix, recvfromBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetSocketAddress;)I"), - NATIVE_METHOD(Posix, remove, "(Ljava/lang/String;)V"), - NATIVE_METHOD(Posix, removexattr, "(Ljava/lang/String;Ljava/lang/String;)V"), - NATIVE_METHOD(Posix, rename, "(Ljava/lang/String;Ljava/lang/String;)V"), - NATIVE_METHOD(Posix, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Landroid/util/MutableLong;J)J"), - NATIVE_METHOD(Posix, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetAddress;I)I"), - NATIVE_METHOD_OVERLOAD(Posix, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/SocketAddress;)I", SocketAddress), - NATIVE_METHOD(Posix, setegid, "(I)V"), - NATIVE_METHOD(Posix, setenv, "(Ljava/lang/String;Ljava/lang/String;Z)V"), - NATIVE_METHOD(Posix, seteuid, "(I)V"), - NATIVE_METHOD(Posix, setgid, "(I)V"), - NATIVE_METHOD(Posix, setpgid, "(II)V"), - NATIVE_METHOD(Posix, setregid, "(II)V"), - NATIVE_METHOD(Posix, setreuid, "(II)V"), - NATIVE_METHOD(Posix, setsid, "()I"), - NATIVE_METHOD(Posix, setsockoptByte, "(Ljava/io/FileDescriptor;III)V"), - NATIVE_METHOD(Posix, setsockoptIfreq, "(Ljava/io/FileDescriptor;IILjava/lang/String;)V"), - NATIVE_METHOD(Posix, setsockoptInt, "(Ljava/io/FileDescriptor;III)V"), - NATIVE_METHOD(Posix, setsockoptIpMreqn, "(Ljava/io/FileDescriptor;III)V"), - NATIVE_METHOD(Posix, setsockoptGroupReq, "(Ljava/io/FileDescriptor;IILandroid/system/StructGroupReq;)V"), - NATIVE_METHOD(Posix, setsockoptGroupSourceReq, "(Ljava/io/FileDescriptor;IILandroid/system/StructGroupSourceReq;)V"), - NATIVE_METHOD(Posix, setsockoptLinger, "(Ljava/io/FileDescriptor;IILandroid/system/StructLinger;)V"), - NATIVE_METHOD(Posix, setsockoptTimeval, "(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V"), - NATIVE_METHOD(Posix, setuid, "(I)V"), - NATIVE_METHOD(Posix, setxattr, "(Ljava/lang/String;Ljava/lang/String;[BI)V"), - NATIVE_METHOD(Posix, shutdown, "(Ljava/io/FileDescriptor;I)V"), - NATIVE_METHOD(Posix, socket, "(III)Ljava/io/FileDescriptor;"), - NATIVE_METHOD(Posix, socketpair, "(IIILjava/io/FileDescriptor;Ljava/io/FileDescriptor;)V"), - NATIVE_METHOD(Posix, stat, "(Ljava/lang/String;)Landroid/system/StructStat;"), - NATIVE_METHOD(Posix, statvfs, "(Ljava/lang/String;)Landroid/system/StructStatVfs;"), - NATIVE_METHOD(Posix, strerror, "(I)Ljava/lang/String;"), - NATIVE_METHOD(Posix, strsignal, "(I)Ljava/lang/String;"), - NATIVE_METHOD(Posix, symlink, "(Ljava/lang/String;Ljava/lang/String;)V"), - NATIVE_METHOD(Posix, sysconf, "(I)J"), - NATIVE_METHOD(Posix, tcdrain, "(Ljava/io/FileDescriptor;)V"), - NATIVE_METHOD(Posix, tcsendbreak, "(Ljava/io/FileDescriptor;I)V"), - NATIVE_METHOD(Posix, umaskImpl, "(I)I"), - NATIVE_METHOD(Posix, uname, "()Landroid/system/StructUtsname;"), - NATIVE_METHOD(Posix, unlink, "(Ljava/lang/String;)V"), - NATIVE_METHOD(Posix, unsetenv, "(Ljava/lang/String;)V"), - NATIVE_METHOD(Posix, waitpid, "(ILandroid/util/MutableInt;I)I"), - NATIVE_METHOD(Posix, writeBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"), - NATIVE_METHOD(Posix, writev, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"), -}; -void register_libcore_io_Posix(JNIEnv* env) { - jniRegisterNativeMethods(env, "libcore/io/Posix", gMethods, NELEM(gMethods)); -} diff --git a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp index f6f812c8d..aaf3ca1b7 100644 --- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp +++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp @@ -16,22 +16,24 @@ #define LOG_TAG "ExpatParser" +#include +#include + +#include + +#include +#include + #include "JNIHelp.h" #include "JniConstants.h" #include "JniException.h" -#include "LocalArray.h" #include "ScopedLocalRef.h" #include "ScopedPrimitiveArray.h" #include "ScopedStringChars.h" #include "ScopedUtfChars.h" #include "jni.h" -#include "cutils/log.h" #include "unicode/unistr.h" -#include - -#include -#include #define BUCKET_COUNT 128 @@ -102,7 +104,8 @@ class StringStack { * Data passed to parser handler method by the parser. */ struct ParsingContext { - ParsingContext(jobject object) : env(NULL), object(object), buffer(NULL), bufferSize(-1) { + explicit ParsingContext(jobject object) + : env(NULL), object(object), buffer(NULL), bufferSize(-1) { for (int i = 0; i < BUCKET_COUNT; i++) { internedStrings[i] = NULL; } @@ -518,9 +521,8 @@ class ExpatElementName { } // return prefix + ":" + localName - ::LocalArray<1024> qName(strlen(mPrefix) + 1 + strlen(mLocalName) + 1); - snprintf(&qName[0], qName.size(), "%s:%s", mPrefix, mLocalName); - return internString(mEnv, mParsingContext, &qName[0]); + auto qName = android::base::StringPrintf("%s:%s", mPrefix, mLocalName); + return internString(mEnv, mParsingContext, qName.c_str()); } /** diff --git a/luni/src/main/native/readlink.cpp b/luni/src/main/native/readlink.cpp deleted file mode 100644 index 555d51520..000000000 --- a/luni/src/main/native/readlink.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -#include "LocalArray.h" -#include "readlink.h" - -#include -#include - -bool readlink(const char* path, std::string& result) { - // We can't know how big a buffer readlink(2) will need, so we need to - // loop until it says "that fit". - size_t bufSize = 512; - while (true) { - LocalArray<512> buf(bufSize); - ssize_t len = readlink(path, &buf[0], buf.size()); - if (len == -1) { - // An error occurred. - return false; - } - if (static_cast(len) < buf.size()) { - // The buffer was big enough. - result.assign(&buf[0], len); - return true; - } - // Try again with a bigger buffer. - bufSize *= 2; - } -} diff --git a/luni/src/main/native/readlink.h b/luni/src/main/native/readlink.h deleted file mode 100644 index 14031dc5d..000000000 --- a/luni/src/main/native/readlink.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -#include - -/** - * Fills 'result' with the contents of the symbolic link 'path'. Sets errno and returns false on - * failure, returns true on success. The contents of 'result' on failure are undefined. Possible - * errors are those defined for readlink(2), except that this function takes care of sizing the - * buffer appropriately. - */ -bool readlink(const char* path, std::string& result); diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk index 396bf4d5c..0706ce870 100644 --- a/luni/src/main/native/sub.mk +++ b/luni/src/main/native/sub.mk @@ -11,9 +11,9 @@ LOCAL_SRC_FILES := \ Register.cpp \ ZipUtilities.cpp \ android_system_OsConstants.cpp \ - canonicalize_path.cpp \ cbigint.cpp \ java_lang_StringToReal.cpp \ + java_lang_invoke_MethodHandle.cpp \ java_math_NativeBN.cpp \ java_util_regex_Matcher.cpp \ java_util_regex_Pattern.cpp \ @@ -21,15 +21,15 @@ LOCAL_SRC_FILES := \ libcore_icu_NativeConverter.cpp \ libcore_icu_TimeZoneNames.cpp \ libcore_io_AsynchronousCloseMonitor.cpp \ + libcore_io_Linux.cpp \ libcore_io_Memory.cpp \ - libcore_io_Posix.cpp \ libcore_util_NativeAllocationRegistry.cpp \ org_apache_harmony_xml_ExpatParser.cpp \ - readlink.cpp \ sun_misc_Unsafe.cpp \ valueOf.cpp \ LOCAL_STATIC_LIBRARIES += \ + libbase \ libfdlibm \ LOCAL_SHARED_LIBRARIES += \ diff --git a/luni/src/test/filesystems/resources/META-INF/services/java.nio.file.spi.FileSystemProvider b/luni/src/test/filesystems/resources/META-INF/services/java.nio.file.spi.FileSystemProvider new file mode 100644 index 000000000..61cf54366 --- /dev/null +++ b/luni/src/test/filesystems/resources/META-INF/services/java.nio.file.spi.FileSystemProvider @@ -0,0 +1 @@ +mypackage.MockFileSystemProvider \ No newline at end of file diff --git a/luni/src/test/filesystems/src/mypackage/MockFileSystem.java b/luni/src/test/filesystems/src/mypackage/MockFileSystem.java new file mode 100644 index 000000000..6981186cd --- /dev/null +++ b/luni/src/test/filesystems/src/mypackage/MockFileSystem.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2017 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 + */ + +package mypackage; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.WatchService; +import java.nio.file.attribute.UserPrincipalLookupService; +import java.nio.file.spi.FileSystemProvider; +import java.util.Map; +import java.util.Set; + +public class MockFileSystem extends FileSystem { + private URI uri; + private Map env; + private Path path; + + public MockFileSystem(URI uri, Map env) { + this.uri = uri; + this.env = env; + } + + public MockFileSystem(Path path, Map env) { + this.path = path; + this.env = env; + } + + public URI getURI() { + return uri; + } + + public Path getPath() { + return path; + } + + public Map getEnv() { + return env; + } + + @Override + public FileSystemProvider provider() { + return null; + } + + @Override + public void close() throws IOException { + + } + + @Override + public boolean isOpen() { + return false; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public String getSeparator() { + return null; + } + + @Override + public Iterable getRootDirectories() { + return null; + } + + @Override + public Iterable getFileStores() { + return null; + } + + @Override + public Set supportedFileAttributeViews() { + return null; + } + + @Override + public Path getPath(String first, String... more) { + return null; + } + + @Override + public PathMatcher getPathMatcher(String syntaxAndPattern) { + return null; + } + + @Override + public UserPrincipalLookupService getUserPrincipalLookupService() { + return null; + } + + @Override + public WatchService newWatchService() throws IOException { + return null; + } +} diff --git a/luni/src/test/filesystems/src/mypackage/MockFileSystemProvider.java b/luni/src/test/filesystems/src/mypackage/MockFileSystemProvider.java new file mode 100644 index 000000000..9ccd8dc6e --- /dev/null +++ b/luni/src/test/filesystems/src/mypackage/MockFileSystemProvider.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 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 + */ + +package mypackage; + +import java.io.IOException; +import java.net.URI; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.AccessMode; +import java.nio.file.CopyOption; +import java.nio.file.DirectoryStream; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.LinkOption; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.spi.FileSystemProvider; +import java.util.Map; +import java.util.Set; + +public class MockFileSystemProvider extends FileSystemProvider { + + @Override + public String getScheme() { + return "stubScheme"; + } + + @Override + public FileSystem newFileSystem(URI uri, Map env) throws IOException { + return new MockFileSystem(uri, env); + } + + @Override + public FileSystem newFileSystem(Path path, Map env) throws IOException { + return new MockFileSystem(path, env); + } + + @Override + public FileSystem getFileSystem(URI uri) { + return null; + } + + @Override + public Path getPath(URI uri) { + return null; + } + + @Override + public SeekableByteChannel newByteChannel(Path path, Set options, + FileAttribute[] attrs) throws IOException { + return null; + } + + @Override + public DirectoryStream newDirectoryStream(Path dir, + DirectoryStream.Filter filter) throws IOException { + return null; + } + + @Override + public void createDirectory(Path dir, FileAttribute[] attrs) throws IOException { + + } + + @Override + public void delete(Path path) throws IOException { + + } + + @Override + public void copy(Path source, Path target, CopyOption... options) throws IOException { + + } + + @Override + public void move(Path source, Path target, CopyOption... options) throws IOException { + + } + + @Override + public boolean isSameFile(Path path, Path path2) throws IOException { + return false; + } + + @Override + public boolean isHidden(Path path) throws IOException { + return false; + } + + @Override + public FileStore getFileStore(Path path) throws IOException { + return null; + } + + @Override + public void checkAccess(Path path, AccessMode... modes) throws IOException { + + } + + @Override + public V getFileAttributeView(Path path, Class type, + LinkOption... options) { + return null; + } + + @Override + public A readAttributes(Path path, Class type, + LinkOption... options) throws IOException { + return null; + } + + @Override + public Map readAttributes(Path path, String attributes, + LinkOption... options) throws IOException { + return null; + } + + @Override + public void setAttribute(Path path, String attribute, Object value, LinkOption... options) + throws IOException { + + } +} diff --git a/luni/src/test/filesystems/src/mypackage/package-info.java b/luni/src/test/filesystems/src/mypackage/package-info.java new file mode 100644 index 000000000..38a04c4d4 --- /dev/null +++ b/luni/src/test/filesystems/src/mypackage/package-info.java @@ -0,0 +1,5 @@ +/** + * The classes in the package are used by {@link libcore.java.nio.file.FileSystemsTest + * FileSystemsTest}. The tests creates a custom classloader which loads these classes. + */ +package mypackage; \ No newline at end of file diff --git a/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java b/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java index fce8507d2..ec5ca03df 100644 --- a/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java +++ b/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java @@ -93,9 +93,6 @@ public void doTestMessageDigest(Digest oldDigest, Digest newDigest) { + oldTime.toString()); System.out.println("Time for " + ITERATIONS + " x new hash processing: " + newTime.toString()); - - assertTrue("New hash should be faster:\nold=" + oldTime.toString() + "\nnew=" - + newTime.toString(), newTime.mean() < oldTime.mean()); } /** diff --git a/luni/src/test/java/dalvik/system/BlockGuardTest.java b/luni/src/test/java/dalvik/system/BlockGuardTest.java index 24313cd1a..4f2081966 100644 --- a/luni/src/test/java/dalvik/system/BlockGuardTest.java +++ b/luni/src/test/java/dalvik/system/BlockGuardTest.java @@ -16,17 +16,20 @@ package dalvik.system; +import android.system.Os; +import android.system.OsConstants; import junit.framework.TestCase; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.RandomAccessFile; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; +import java.util.Set; -/** - * Created by narayan on 1/7/16. - */ public class BlockGuardTest extends TestCase { private BlockGuard.Policy oldPolicy; @@ -34,6 +37,7 @@ public class BlockGuardTest extends TestCase { @Override public void setUp() { + recorder.setChecks(EnumSet.allOf(RecordingPolicy.Check.class)); oldPolicy = BlockGuard.getThreadPolicy(); BlockGuard.setThreadPolicy(recorder); } @@ -100,22 +104,33 @@ public void testFile() throws Exception { } public void testFileInputStream() throws Exception { - File f = new File("/proc/version"); - recorder.clear(); + // The file itself doesn't matter: it just has to exist and allow the creation of the + // FileInputStream. The BlockGuard should have the same behavior towards a normal file and + // system file. + File tmpFile = File.createTempFile("inputFile", ".txt"); + try (FileOutputStream fos = new FileOutputStream(tmpFile)) { + fos.write("01234567890".getBytes()); + } - FileInputStream fis = new FileInputStream(f); - recorder.expectAndClear("onReadFromDisk"); + try { + recorder.clear(); - fis.read(new byte[4],0, 4); - recorder.expectAndClear("onReadFromDisk"); + FileInputStream fis = new FileInputStream(tmpFile); + recorder.expectAndClear("onReadFromDisk"); - fis.read(); - recorder.expectAndClear("onReadFromDisk"); + fis.read(new byte[4], 0, 4); + recorder.expectAndClear("onReadFromDisk"); - fis.skip(1); - recorder.expectAndClear("onReadFromDisk"); + fis.read(); + recorder.expectAndClear("onReadFromDisk"); + + fis.skip(1); + recorder.expectAndClear("onReadFromDisk"); - fis.close(); + fis.close(); + } finally { + tmpFile.delete(); + } } public void testFileOutputStream() throws Exception { @@ -138,23 +153,158 @@ public void testFileOutputStream() throws Exception { recorder.expectNoViolations(); } + public void testUnbufferedIO() throws Exception { + File f = File.createTempFile("foo", "bar"); + recorder.setChecks(EnumSet.of(RecordingPolicy.Check.UNBUFFERED_IO)); + recorder.clear(); + + try (FileOutputStream fos = new FileOutputStream(f)) { + recorder.expectNoViolations(); + for (int i = 0; i < 11; i++) { + recorder.expectNoViolations(); + fos.write("a".getBytes()); + } + recorder.expectAndClear("onUnbufferedIO"); + } + + try (FileInputStream fis = new FileInputStream(new File("/dev/null"))) { + recorder.expectNoViolations(); + byte[] b = new byte[1]; + for (int i = 0; i < 11; i++) { + recorder.expectNoViolations(); + fis.read(b); + } + recorder.expectAndClear("onUnbufferedIO"); + } + + try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) { + // seek should reset the IoTracker. + ras.seek(0); + recorder.expectNoViolations(); + for (int i = 0; i < 11; i++) { + recorder.expectNoViolations(); + ras.read("a".getBytes()); + } + recorder.expectAndClear("onUnbufferedIO"); + } + + try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) { + // No violation is expected as a write is called while reading which should reset the + // IoTracker counter. + for (int i = 0; i < 11; i++) { + recorder.expectNoViolations(); + if (i == 5) { + ras.write("a".getBytes()); + } + ras.read("a".getBytes()); + } + recorder.expectNoViolations(); + } + + try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) { + // No violation is expected as a seek is called while reading which should reset the + // IoTracker counter. + for (int i = 0; i < 11; i++) { + recorder.expectNoViolations(); + if (i == 5) { + ras.seek(0); + } + ras.read("a".getBytes()); + } + recorder.expectNoViolations(); + } + + try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) { + // seek should reset the IoTracker. + for (int i = 0; i < 11; i++) { + recorder.expectNoViolations(); + ras.write("a".getBytes()); + } + recorder.expectAndClear("onUnbufferedIO"); + } + + try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) { + // No violation is expected as a read is called while writing which should reset the + // IoTracker counter. + for (int i = 0; i < 11; i++) { + recorder.expectNoViolations(); + if (i == 5) { + ras.read("a".getBytes()); + } + ras.write("a".getBytes()); + } + recorder.expectNoViolations(); + } + + try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) { + for (int i = 0; i < 11; i++) { + recorder.expectNoViolations(); + if (i == 5) { + ras.seek(0); + } + ras.write("a".getBytes()); + } + recorder.expectNoViolations(); + } + } + + public void testOpen() throws Exception { + File temp = File.createTempFile("foo", "bar"); + recorder.clear(); + + // Open in read/write mode : should be recorded as a read and a write to disk. + FileDescriptor fd = Os.open(temp.getPath(), OsConstants.O_RDWR, 0); + recorder.expectAndClear("onReadFromDisk", "onWriteToDisk"); + Os.close(fd); + + // Open in read only mode : should be recorded as a read from disk. + recorder.clear(); + fd = Os.open(temp.getPath(), OsConstants.O_RDONLY, 0); + recorder.expectAndClear("onReadFromDisk"); + Os.close(fd); + } public static class RecordingPolicy implements BlockGuard.Policy { private final List violations = new ArrayList<>(); + private Set checksList; + + public enum Check { + WRITE_TO_DISK, + READ_FROM_DISK, + NETWORK, + UNBUFFERED_IO, + } + + public void setChecks(EnumSet checksList) { + this.checksList = checksList; + } @Override public void onWriteToDisk() { - addViolation("onWriteToDisk"); + if (checksList != null && checksList.contains(Check.WRITE_TO_DISK)) { + addViolation("onWriteToDisk"); + } } @Override public void onReadFromDisk() { - addViolation("onReadFromDisk"); + if (checksList != null && checksList.contains(Check.READ_FROM_DISK)) { + addViolation("onReadFromDisk"); + } } @Override public void onNetwork() { - addViolation("onNetwork"); + if (checksList != null && checksList.contains(Check.NETWORK)) { + addViolation("onNetwork"); + } + } + + @Override + public void onUnbufferedIO() { + if (checksList != null && checksList.contains(Check.UNBUFFERED_IO)) { + addViolation("onUnbufferedIO"); + } } private void addViolation(String type) { diff --git a/luni/src/test/java/dalvik/system/DexClassLoaderTest.java b/luni/src/test/java/dalvik/system/DexClassLoaderTest.java index 0e0ee8da8..625cfa986 100644 --- a/luni/src/test/java/dalvik/system/DexClassLoaderTest.java +++ b/luni/src/test/java/dalvik/system/DexClassLoaderTest.java @@ -76,6 +76,7 @@ private static void cleanUpDir(File dir) { assertTrue(file.delete()); } } + assertTrue(dir.delete()); } /** @@ -168,9 +169,6 @@ private String createLoaderAndGetResource(String resourceName, File... files) th */ public void test_oneJar_init() throws Exception { ClassLoader cl = createLoader(jar1); - File[] files = optimizedDir.listFiles(DEX_FILE_NAME_FILTER); - assertNotNull(files); - assertEquals(1, files.length); } /** @@ -211,9 +209,6 @@ public void test_oneJar_getInstanceVariable() throws Exception { public void test_oneDex_init() throws Exception { ClassLoader cl = createLoader(dex1); - File[] files = optimizedDir.listFiles(DEX_FILE_NAME_FILTER); - assertNotNull(files); - assertEquals(1, files.length); } public void test_oneDex_simpleUse() throws Exception { @@ -245,9 +240,6 @@ public void test_oneDex_getInstanceVariable() throws Exception { public void test_twoJar_init() throws Exception { ClassLoader cl = createLoader(jar1, jar2); - File[] files = optimizedDir.listFiles(DEX_FILE_NAME_FILTER); - assertNotNull(files); - assertEquals(2, files.length); } public void test_twoJar_simpleUse() throws Exception { @@ -299,9 +291,6 @@ public void test_twoJar_diff_getInstanceVariable() throws Exception { public void test_twoDex_init() throws Exception { ClassLoader cl = createLoader(dex1, dex2); - File[] files = optimizedDir.listFiles(DEX_FILE_NAME_FILTER); - assertNotNull(files); - assertEquals(2, files.length); } public void test_twoDex_simpleUse() throws Exception { @@ -397,39 +386,4 @@ public void test_twoJar_diff_directGetResourceAsStream() throws Exception { public void test_twoJar_diff_getResourceAsStream() throws Exception { createLoaderAndCallMethod("test.TestMethods", "test_diff_getResourceAsStream", jar1, jar2); } - - /** - * Test that a DexClassLoader can be used to generate optimized code, then - * a subsequent PathClassLoader can be used to load the optimized code. - * (b/19937016). - */ - public void testDexThenPathClassLoader() throws Exception { - // Use a DexClassLoader to create optimized code. - File dex = new File(srcDir, "dex-then-path.dex"); - copyResource("loading-test.dex", dex); - - File oatDir = new File(new File(srcDir, "oat"), VMRuntime.getCurrentInstructionSet()); - assertTrue(oatDir.mkdirs()); - - DexClassLoader dexloader = new DexClassLoader(dex.getAbsolutePath(), - oatDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader()); - Class c1 = dexloader.loadClass("test.Test1"); - Method m1 = c1.getMethod("test", (Class[]) null); - assertSame("blort", m1.invoke(null, (Object[]) null)); - - // Move the optimized code to the right location to be used by a - // PathClassLoader. - File odexForDexClassLoader = new File(oatDir, "dex-then-path.dex"); - File odexForPathClassLoader = new File(oatDir, "dex-then-path.odex"); - assertTrue(DexFile.isDexOptNeeded(dex.getAbsolutePath())); - assertTrue(odexForDexClassLoader.renameTo(odexForPathClassLoader)); - assertFalse(DexFile.isDexOptNeeded(dex.getAbsolutePath())); - - // Use a PathClassLoader that loads and runs the optimized code. - PathClassLoader pathloader = new PathClassLoader(dex.getAbsolutePath(), - ClassLoader.getSystemClassLoader()); - Class c2 = pathloader.loadClass("test.Test1"); - Method m2 = c2.getMethod("test", (Class[]) null); - assertSame("blort", m2.invoke(null, (Object[]) null)); - } } diff --git a/luni/src/test/java/dalvik/system/EmulatedStackFrameTest.java b/luni/src/test/java/dalvik/system/EmulatedStackFrameTest.java new file mode 100644 index 000000000..461ce89e6 --- /dev/null +++ b/luni/src/test/java/dalvik/system/EmulatedStackFrameTest.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2016 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 + */ + +package dalvik.system; + +import junit.framework.TestCase; + +import java.lang.invoke.MethodType; + +public class EmulatedStackFrameTest extends TestCase { + + public void testReaderWriter_allParamTypes() { + EmulatedStackFrame stackFrame = EmulatedStackFrame.create(MethodType.methodType( + void.class, + new Class[] { boolean.class, char.class, short.class, int.class, long.class, + float.class, double.class, String.class })); + + EmulatedStackFrame.StackFrameWriter writer = new EmulatedStackFrame.StackFrameWriter(); + writer.attach(stackFrame); + + writer.putNextBoolean(true); + writer.putNextChar('a'); + writer.putNextShort((short) 42); + writer.putNextInt(43); + writer.putNextLong(56); + writer.putNextFloat(42.0f); + writer.putNextDouble(52.0); + writer.putNextReference("foo", String.class); + + EmulatedStackFrame.StackFrameReader reader = new EmulatedStackFrame.StackFrameReader(); + reader.attach(stackFrame); + + assertTrue(reader.nextBoolean()); + assertEquals('a', reader.nextChar()); + assertEquals((short) 42, reader.nextShort()); + assertEquals(43, reader.nextInt()); + assertEquals(56, reader.nextLong()); + assertEquals(42.0f, reader.nextFloat()); + assertEquals(52.0, reader.nextDouble()); + assertEquals("foo", reader.nextReference(String.class)); + } + + public void testReaderWriter_allReturnTypes() { + EmulatedStackFrame stackFrame = EmulatedStackFrame.create( + MethodType.methodType(boolean.class)); + + EmulatedStackFrame.StackFrameWriter writer = new EmulatedStackFrame.StackFrameWriter(); + writer.attach(stackFrame).makeReturnValueAccessor(); + + EmulatedStackFrame.StackFrameReader reader = new EmulatedStackFrame.StackFrameReader(); + reader.attach(stackFrame).makeReturnValueAccessor(); + + writer.putNextBoolean(true); + assertTrue(reader.nextBoolean()); + + stackFrame = EmulatedStackFrame.create(MethodType.methodType(char.class)); + writer.attach(stackFrame).makeReturnValueAccessor(); + reader.attach(stackFrame).makeReturnValueAccessor(); + + writer.putNextChar('a'); + assertEquals('a', reader.nextChar()); + + stackFrame = EmulatedStackFrame.create(MethodType.methodType(short.class)); + writer.attach(stackFrame).makeReturnValueAccessor(); + reader.attach(stackFrame).makeReturnValueAccessor(); + + writer.putNextShort((short) 52); + assertEquals((short) 52, reader.nextShort()); + + stackFrame = EmulatedStackFrame.create(MethodType.methodType(int.class)); + writer.attach(stackFrame).makeReturnValueAccessor(); + reader.attach(stackFrame).makeReturnValueAccessor(); + writer.putNextInt(64); + assertEquals(64, reader.nextInt()); + + stackFrame = EmulatedStackFrame.create(MethodType.methodType(long.class)); + writer.attach(stackFrame).makeReturnValueAccessor(); + reader.attach(stackFrame).makeReturnValueAccessor(); + writer.putNextLong(72); + assertEquals(72, reader.nextLong()); + + stackFrame = EmulatedStackFrame.create(MethodType.methodType(float.class)); + writer.attach(stackFrame).makeReturnValueAccessor(); + reader.attach(stackFrame).makeReturnValueAccessor(); + writer.putNextFloat(52.0f); + assertEquals(52.0f, reader.nextFloat()); + + stackFrame = EmulatedStackFrame.create(MethodType.methodType(double.class)); + writer.attach(stackFrame).makeReturnValueAccessor(); + reader.attach(stackFrame).makeReturnValueAccessor(); + writer.putNextDouble(73.0); + assertEquals(73.0, reader.nextDouble()); + + stackFrame = EmulatedStackFrame.create(MethodType.methodType(String.class)); + writer.attach(stackFrame).makeReturnValueAccessor(); + reader.attach(stackFrame).makeReturnValueAccessor(); + writer.putNextReference("foo", String.class); + assertEquals("foo", reader.nextReference(String.class)); + } + + public void testReaderWriter_wrongTypes() { + EmulatedStackFrame stackFrame = EmulatedStackFrame.create( + MethodType.methodType(boolean.class, String.class)); + + EmulatedStackFrame.StackFrameReader reader = new EmulatedStackFrame.StackFrameReader(); + reader.attach(stackFrame); + + try { + reader.nextInt(); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + reader.nextDouble(); + fail(); + } catch (IllegalArgumentException expected) { + } + + assertNull(reader.nextReference(String.class)); + + try { + reader.nextDouble(); + fail(); + } catch (IllegalArgumentException expected) { + } + + EmulatedStackFrame.StackFrameWriter writer = new EmulatedStackFrame.StackFrameWriter(); + writer.attach(stackFrame); + + try { + writer.putNextInt(0); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + writer.putNextDouble(0); + fail(); + } catch (IllegalArgumentException expected) { + } + + writer.putNextReference(null, String.class); + + try { + writer.putNextDouble(0); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testReturnValueReaderWriter_wrongTypes() { + EmulatedStackFrame stackFrame = EmulatedStackFrame.create( + MethodType.methodType(boolean.class, String.class)); + + EmulatedStackFrame.StackFrameReader reader = new EmulatedStackFrame.StackFrameReader(); + reader.attach(stackFrame); + reader.makeReturnValueAccessor(); + + try { + reader.nextInt(); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Should succeeed. + assertFalse(reader.nextBoolean()); + + // The next attempt should fail. + try { + reader.nextBoolean(); + fail(); + } catch (IllegalArgumentException expected) { + } + + EmulatedStackFrame.StackFrameWriter writer = new EmulatedStackFrame.StackFrameWriter(); + writer.attach(stackFrame); + writer.makeReturnValueAccessor(); + + try { + writer.putNextInt(0); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Should succeeed. + writer.putNextBoolean(true); + + // The next attempt should fail. + try { + writer.putNextBoolean(false); + fail(); + } catch (IllegalArgumentException expected) { + } + } +} diff --git a/luni/src/test/java/dalvik/system/InMemoryDexClassLoaderTest.java b/luni/src/test/java/dalvik/system/InMemoryDexClassLoaderTest.java new file mode 100644 index 000000000..c164d3bfe --- /dev/null +++ b/luni/src/test/java/dalvik/system/InMemoryDexClassLoaderTest.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2016 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. + */ + +package dalvik.system; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import libcore.io.Streams; +import junit.framework.TestCase; + +/** + * Tests for the class {@link InMemoryDexClassLoader}. + */ +public class InMemoryDexClassLoaderTest extends TestCase { + private static final String PACKAGE_PATH = "dalvik/system/"; + + private File srcDir; + private File dex1; + private File dex2; + + protected void setUp() throws Exception { + srcDir = File.createTempFile("src", ""); + assertTrue(srcDir.delete()); + assertTrue(srcDir.mkdirs()); + + dex1 = new File(srcDir, "loading-test.dex"); + dex2 = new File(srcDir, "loading-test2.dex"); + + copyResource("loading-test.dex", dex1); + copyResource("loading-test2.dex", dex2); + } + + protected void tearDown() { + cleanUpDir(srcDir); + } + + private static void cleanUpDir(File dir) { + if (!dir.isDirectory()) { + return; + } + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + cleanUpDir(file); + } else { + assertTrue(file.delete()); + } + } + assertTrue(dir.delete()); + } + + /** + * Copy a resource in the package directory to the indicated + * target file. + */ + private static void copyResource(String resourceName, + File destination) throws IOException { + ClassLoader loader = InMemoryDexClassLoaderTest.class.getClassLoader(); + InputStream in = loader.getResourceAsStream(PACKAGE_PATH + resourceName); + if (in == null) { + throw new IllegalStateException("Resource not found: " + PACKAGE_PATH + resourceName); + } + try (FileOutputStream out = new FileOutputStream(destination)) { + Streams.copy(in, out); + } finally { + in.close(); + } + } + + private static ByteBuffer ReadFileToByteBufferDirect(File file) throws IOException { + try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { + ByteBuffer buffer = ByteBuffer.allocateDirect((int)file.length()); + int done = 0; + while (done != file.length()) { + done += raf.getChannel().read(buffer); + } + buffer.rewind(); + return buffer; + } + } + + private static ByteBuffer ReadFileToByteBufferIndirect(File file) throws IOException { + ByteBuffer direct = ReadFileToByteBufferDirect(file); + byte[] array = new byte[direct.limit()]; + direct.get(array); + return ByteBuffer.wrap(array); + } + + /** + * Helper to construct a InMemoryDexClassLoader instance to test. + * + * Creates InMemoryDexClassLoader from ByteBuffer instances that are + * direct allocated. + * + * @param files The .dex files to use for the class path. + */ + private static ClassLoader createLoaderDirect(File... files) throws IOException { + assertNotNull(files); + assertTrue(files.length > 0); + ClassLoader result = ClassLoader.getSystemClassLoader(); + for (int i = 0; i < files.length; ++i) { + ByteBuffer buffer = ReadFileToByteBufferDirect(files[i]); + result = new InMemoryDexClassLoader(buffer, result); + } + return result; + } + + /** + * Helper to construct a InMemoryDexClassLoader instance to test. + * + * Creates InMemoryDexClassLoader from ByteBuffer instances that are + * heap allocated. + * + * @param files The .dex files to use for the class path. + */ + private static ClassLoader createLoaderIndirect(File... files) throws IOException { + assertNotNull(files); + assertTrue(files.length > 0); + ClassLoader result = ClassLoader.getSystemClassLoader(); + for (int i = 0; i < files.length; ++i) { + ByteBuffer buffer = ReadFileToByteBufferIndirect(files[i]); + result = new InMemoryDexClassLoader(buffer, result); + } + return result; + } + + /** + * Helper to construct a new InMemoryDexClassLoader via direct + * ByteBuffer instances. + * + * @param className The name of the class of the method to call. + * @param methodName The name of the method to call. + * @param files The .dex or .jar files to use for the class path. + */ + private Object createLoaderDirectAndCallMethod( + String className, String methodName, File... files) + throws IOException, ReflectiveOperationException { + ClassLoader cl = createLoaderDirect(files); + Class c = cl.loadClass(className); + Method m = c.getMethod(methodName, (Class[]) null); + assertNotNull(m); + return m.invoke(null, (Object[]) null); + } + + /** + * Helper to construct a new InMemoryDexClassLoader via indirect + * ByteBuffer instances. + * + * @param className The name of the class of the method to call. + * @param methodName The name of the method to call. + * @param files The .dex or .jar files to use for the class path. + */ + private Object createLoaderIndirectAndCallMethod( + String className, String methodName, File... files) + throws IOException, ReflectiveOperationException { + ClassLoader cl = createLoaderIndirect(files); + Class c = cl.loadClass(className); + Method m = c.getMethod(methodName, (Class[]) null); + assertNotNull(m); + return m.invoke(null, (Object[]) null); + } + + // ONE_DEX with direct ByteBuffer. + + public void test_oneDexDirect_simpleUse() throws Exception { + String result = (String) createLoaderDirectAndCallMethod("test.Test1", "test", dex1); + assertSame("blort", result); + } + + public void test_oneDexDirect_constructor() throws Exception { + createLoaderDirectAndCallMethod("test.TestMethods", "test_constructor", dex1); + } + + public void test_oneDexDirect_callStaticMethod() throws Exception { + createLoaderDirectAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1); + } + + public void test_oneDexDirect_getStaticVariable() throws Exception { + createLoaderDirectAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1); + } + + public void test_oneDexDirect_callInstanceMethod() throws Exception { + createLoaderDirectAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1); + } + + public void test_oneDexDirect_getInstanceVariable() throws Exception { + createLoaderDirectAndCallMethod("test.TestMethods", "test_getInstanceVariable", dex1); + } + + // ONE_DEX with non-direct ByteBuffer. + + public void test_oneDexIndirect_simpleUse() throws Exception { + String result = (String) createLoaderIndirectAndCallMethod("test.Test1", "test", dex1); + assertSame("blort", result); + } + + public void test_oneDexIndirect_constructor() throws Exception { + createLoaderIndirectAndCallMethod("test.TestMethods", "test_constructor", dex1); + } + + public void test_oneDexIndirect_callStaticMethod() throws Exception { + createLoaderIndirectAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1); + } + + public void test_oneDexIndirect_getStaticVariable() throws Exception { + createLoaderIndirectAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1); + } + + public void test_oneDexIndirect_callInstanceMethod() throws Exception { + createLoaderIndirectAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1); + } + + public void test_oneDexIndirect_getInstanceVariable() throws Exception { + createLoaderIndirectAndCallMethod("test.TestMethods", "test_getInstanceVariable", dex1); + } + + // TWO_DEX with direct ByteBuffer + + public void test_twoDexDirect_simpleUse() throws Exception { + String result = (String) createLoaderDirectAndCallMethod("test.Test1", "test", dex1, dex2); + assertSame("blort", result); + } + + public void test_twoDexDirect_constructor() throws Exception { + createLoaderDirectAndCallMethod("test.TestMethods", "test_constructor", dex1, dex2); + } + + public void test_twoDexDirect_callStaticMethod() throws Exception { + createLoaderDirectAndCallMethod("test.TestMethods", "test_callStaticMethod", dex1, dex2); + } + + public void test_twoDexDirect_getStaticVariable() throws Exception { + createLoaderDirectAndCallMethod("test.TestMethods", "test_getStaticVariable", dex1, dex2); + } + + public void test_twoDexDirect_callInstanceMethod() throws Exception { + createLoaderDirectAndCallMethod("test.TestMethods", "test_callInstanceMethod", dex1, dex2); + } + + public void test_twoDexDirect_getInstanceVariable() throws Exception { + createLoaderDirectAndCallMethod( + "test.TestMethods", "test_getInstanceVariable", dex1, dex2); + } + + public void test_twoDexDirect_target2_static_method() throws Exception { + String result = + (String) createLoaderDirectAndCallMethod("test2.Target2", "frotz", dex1, dex2); + assertSame("frotz", result); + } + + public void test_twoDexDirect_diff_constructor() throws Exception { + // NB Ordering dex2 then dex1 as classloader's are nested and + // each only supports a single DEX image. The + // test.TestMethods.test_diff* methods depend on dex2 hence + // ordering. + createLoaderDirectAndCallMethod("test.TestMethods", "test_diff_constructor", dex2, dex1); + } + + public void test_twoDexDirect_diff_callStaticMethod() throws Exception { + // NB See comment in test_twoDexDirect_diff_constructor. + createLoaderDirectAndCallMethod( + "test.TestMethods", "test_diff_callStaticMethod", dex2, dex1); + } + + public void test_twoDexDirect_diff_getStaticVariable() throws Exception { + // NB See comment in test_twoDexDirect_diff_constructor. + createLoaderDirectAndCallMethod( + "test.TestMethods", "test_diff_getStaticVariable", dex2, dex1); + } + + public void test_twoDexDirect_diff_callInstanceMethod() throws Exception { + // NB See comment in test_twoDexDirect_diff_constructor. + createLoaderDirectAndCallMethod( + "test.TestMethods", "test_diff_callInstanceMethod", dex2, dex1); + } + + public void test_twoDexDirect_diff_getInstanceVariable() throws Exception { + // NB See comment in test_twoDexDirect_diff_constructor. + createLoaderDirectAndCallMethod( + "test.TestMethods", "test_diff_getInstanceVariable", dex2, dex1); + } +} diff --git a/luni/src/test/java/libcore/android/system/OsConstantsTest.java b/luni/src/test/java/libcore/android/system/OsConstantsTest.java index 080464d59..a98707c2e 100644 --- a/luni/src/test/java/libcore/android/system/OsConstantsTest.java +++ b/luni/src/test/java/libcore/android/system/OsConstantsTest.java @@ -30,4 +30,9 @@ public void testBug15602893() { assertTrue(OsConstants.IFA_F_TENTATIVE > 0); } + + // introduced for http://b/30402085 + public void testTcpUserTimeoutIsDefined() { + assertTrue(OsConstants.TCP_USER_TIMEOUT > 0); + } } diff --git a/luni/src/test/java/libcore/dalvik/system/BaseDexClassLoaderTest.java b/luni/src/test/java/libcore/dalvik/system/BaseDexClassLoaderTest.java new file mode 100644 index 000000000..e2fe951a0 --- /dev/null +++ b/luni/src/test/java/libcore/dalvik/system/BaseDexClassLoaderTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.dalvik.system; + +import dalvik.system.BaseDexClassLoader; +import dalvik.system.PathClassLoader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.List; +import java.util.ArrayList; + +import libcore.io.Streams; + +import junit.framework.TestCase; + +public final class BaseDexClassLoaderTest extends TestCase { + private static class Reporter implements BaseDexClassLoader.Reporter { + public List loadedDexPaths = new ArrayList<>(); + + @Override + public void report(List dexPaths) { + loadedDexPaths.addAll(dexPaths); + } + } + + public void testReporting() throws Exception { + // Extract loading-test.jar from the resource. + ClassLoader pcl = BaseDexClassLoaderTest.class.getClassLoader(); + File jar = File.createTempFile("loading-test", ".jar"); + try (InputStream in = pcl.getResourceAsStream("dalvik/system/loading-test.jar"); + FileOutputStream out = new FileOutputStream(jar)) { + Streams.copy(in, out); + } + + // Set the reporter. + Reporter reporter = new Reporter(); + BaseDexClassLoader.setReporter(reporter); + // Load the jar file using a PathClassLoader. + BaseDexClassLoader cl1 = new PathClassLoader(jar.getPath(), pcl); + + // Verify the reporter files. + assertEquals(1, reporter.loadedDexPaths.size()); + assertEquals(jar.getPath(), reporter.loadedDexPaths.get(0)); + + // Reset the reporter and check we don't report anymore. + BaseDexClassLoader.setReporter(null); + + // Load the jar file using another PathClassLoader. + BaseDexClassLoader cl2 = new PathClassLoader(jar.getPath(), pcl); + + // Verify the list reporter files did not change. + assertEquals(1, reporter.loadedDexPaths.size()); + assertEquals(jar.getPath(), reporter.loadedDexPaths.get(0)); + + // Clean up the extracted jar file. + assertTrue(jar.delete()); + } +} diff --git a/luni/src/test/java/libcore/dalvik/system/PathClassLoaderTest.java b/luni/src/test/java/libcore/dalvik/system/PathClassLoaderTest.java index 7faf78054..552c7329e 100644 --- a/luni/src/test/java/libcore/dalvik/system/PathClassLoaderTest.java +++ b/luni/src/test/java/libcore/dalvik/system/PathClassLoaderTest.java @@ -16,6 +16,7 @@ package libcore.dalvik.system; +import dalvik.system.BlockGuard; import dalvik.system.PathClassLoader; import java.lang.reflect.Method; import java.io.File; @@ -64,17 +65,23 @@ private File makeTempFile(File directory, String name) throws IOException { return result; } - public void testAppUseOfPathClassLoader() throws Exception { + private static File extractResourceJar(String name) throws Exception { // Extract loading-test.jar from the resource. ClassLoader pcl = PathClassLoaderTest.class.getClassLoader(); - File jar = File.createTempFile("loading-test", ".jar"); + File jar = File.createTempFile(name, ".jar"); try (InputStream in = pcl.getResourceAsStream("dalvik/system/loading-test.jar"); FileOutputStream out = new FileOutputStream(jar)) { - Streams.copy(in, out); + Streams.copy(in, out); } + return jar; + } + + public void testAppUseOfPathClassLoader() throws Exception { + File jar = extractResourceJar("loading-test"); + // Execute code from the jar file using a PathClassLoader. - PathClassLoader cl = new PathClassLoader(jar.getPath(), pcl); + PathClassLoader cl = new PathClassLoader(jar.getPath(), Object.class.getClassLoader()); Class c = cl.loadClass("test.Test1"); Method m = c.getMethod("test", (Class[]) null); String result = (String) m.invoke(null, (Object[]) null); @@ -113,6 +120,59 @@ public void test_classLoader_tampered_certificate_loadsOK_nullCertificates() thr } } + public void test_classLoader_exceptionDuringLoading() throws Exception { + final File jar = extractResourceJar("loading-test"); + + final PathClassLoader pcl = new PathClassLoader(jar.getAbsolutePath(), + Object.class.getClassLoader()); + + + BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); + BlockGuard.setThreadPolicy(new BlockGuard.Policy() { + @Override + public void onWriteToDisk() { + throw new RuntimeException("onWriteToDisk"); + } + + @Override + public void onReadFromDisk() { + throw new RuntimeException("onReadFromDisk"); + } + + @Override + public void onNetwork() { + throw new RuntimeException("onNetwork"); + } + + @Override + public void onUnbufferedIO() { + throw new RuntimeException("onUnbufferedIO"); + } + + @Override + public int getPolicyMask() { + return 0; + } + }); + + try { + try { + // Resource loading involves a blocking operation and will throw a RuntimeException + // here. + pcl.getResource("test/Resource1.txt"); + fail(); + } catch (RuntimeException expected) { + } + } finally { + BlockGuard.setThreadPolicy(policy); + } + + // Assert that the ClassLoader recovers after the failure above when the BlockGuard is + // removed. This also simulates the ClassLoader being used from another thread with a + // different BlockGuard policy. + assertNotNull(pcl.getResource("test/Resource1.txt")); + } + @Override protected void setUp() throws Exception { super.setUp(); } diff --git a/luni/src/test/java/libcore/icu/LocaleDataTest.java b/luni/src/test/java/libcore/icu/LocaleDataTest.java index 54b60742c..e2fb8b76f 100644 --- a/luni/src/test/java/libcore/icu/LocaleDataTest.java +++ b/luni/src/test/java/libcore/icu/LocaleDataTest.java @@ -131,4 +131,10 @@ public void testTimeFormat12And24() throws Exception { assertEquals("aK:mm", ja_JP.timeFormat_hm); assertEquals("H:mm", ja_JP.timeFormat_Hm); } + + // http://b/26397197 + public void testPatternWithOverride() throws Exception { + LocaleData haw = LocaleData.get(new Locale("haw")); + assertFalse(haw.shortDateFormat.isEmpty()); + } } diff --git a/luni/src/test/java/libcore/io/Base64Test.java b/luni/src/test/java/libcore/io/Base64Test.java deleted file mode 100644 index 6cfe18616..000000000 --- a/luni/src/test/java/libcore/io/Base64Test.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -package libcore.io; - -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CharsetEncoder; -import java.nio.charset.CodingErrorAction; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import junit.framework.AssertionFailedError; -import junit.framework.TestCase; - -public final class Base64Test extends TestCase { - - public void testEncodeDecode() throws Exception { - assertEncodeDecode(""); - assertEncodeDecode("Eg==", 0x12); - assertEncodeDecode("EjQ=", 0x12, 0x34); - assertEncodeDecode("EjRW", 0x12, 0x34, 0x56); - assertEncodeDecode("EjRWeA==", 0x12, 0x34, 0x56, 0x78); - assertEncodeDecode("EjRWeJo=", 0x12, 0x34, 0x56, 0x78, 0x9A); - assertEncodeDecode("EjRWeJq8", 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc); - } - - public void testEncode_doesNotWrap() throws Exception { - int[] data = new int[61]; - Arrays.fill(data, 0xff); - String expected = "///////////////////////////////////////////////////////////////////////" - + "//////////w=="; // 84 chars - assertEncodeDecode(expected, data); - } - - private static void assertEncodeDecode(String expectedEncoded, int... toEncode) - throws Exception { - // We should never expect (or receive) non-ASCII text from Base64.encoder. - asciiToBytes(expectedEncoded); - - // Convert the convenient ints to the bytes we need. - byte[] inputBytes = new byte[toEncode.length]; - for (int i = 0; i < toEncode.length; i++) { - inputBytes[i] = (byte) toEncode[i]; - } - String encoded = Base64.encode(inputBytes); - assertEquals(expectedEncoded, encoded); - - // Check we can round-trip the encoded bytes to - // arrive at what we started with. - int[] actualDecodedBytes = decodeToInts(encoded); - assertArrayEquals(toEncode, actualDecodedBytes); - } - - public void testDecode_empty() throws Exception { - byte[] decoded = Base64.decode(new byte[0]); - assertEquals(0, decoded.length); - } - - public void testDecode_truncated() throws Exception { - // Correct data, for reference. - assertEquals("hello, world", decodeToString("aGVsbG8sIHdvcmxk")); - - // The following are missing the final bytes - assertEquals("hello, wo", decodeToString("aGVsbG8sIHdvcmx")); - assertEquals("hello, wo", decodeToString("aGVsbG8sIHdvcm")); - assertEquals("hello, wo", decodeToString("aGVsbG8sIHdvc")); - assertEquals("hello, wo", decodeToString("aGVsbG8sIHdv")); - } - - public void testDecode_extraChars() throws Exception { - // Characters outside of alphabet before padding. - assertEquals("hello, world", decodeToString(" aGVsbG8sIHdvcmxk")); - assertEquals("hello, world", decodeToString("aGV sbG8sIHdvcmxk")); - assertEquals("hello, world", decodeToString("aGVsbG8sIHdvcmxk ")); - assertEquals(null, decodeToString("*aGVsbG8sIHdvcmxk")); - assertEquals(null, decodeToString("aGV*sbG8sIHdvcmxk")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk*")); - assertEquals("hello, world", decodeToString("\r\naGVsbG8sIHdvcmxk")); - assertEquals("hello, world", decodeToString("aGV\r\nsbG8sIHdvcmxk")); - assertEquals("hello, world", decodeToString("aGVsbG8sIHdvcmxk\r\n")); - assertEquals("hello, world", decodeToString("\naGVsbG8sIHdvcmxk")); - assertEquals("hello, world", decodeToString("aGV\nsbG8sIHdvcmxk")); - assertEquals("hello, world", decodeToString("aGVsbG8sIHdvcmxk\n")); - - // padding 0 - assertEquals("hello, world", decodeToString("aGVsbG8sIHdvcmxk")); - // Extra padding - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk=")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk==")); - // Characters outside alphabet intermixed with (too much) padding. - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk =")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk = = ")); - - // padding 1 - assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE=")); - // Missing padding - assertEquals("hello, world", decodeToString("aGVsbG8sIHdvcmxkPyE")); - // Characters outside alphabet before padding. - assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE =")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE*=")); - // Trailing characters, otherwise valid. - assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE= ")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=*")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=X")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=XY")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=XYZ")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=XYZA")); - assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE=\n")); - assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE=\r\n")); - assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE= ")); - assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE==")); - // Whitespace characters outside alphabet intermixed with (too much) padding. - assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE ==")); - assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE = = ")); - - // padding 2 - assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg==")); - // Missing padding - assertEquals("hello, world", decodeToString("aGVsbG8sIHdvcmxkLg")); - // Partially missing padding - assertEquals("hello, world", decodeToString("aGVsbG8sIHdvcmxkLg=")); - // Characters outside alphabet before padding. - assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg ==")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg*==")); - // Trailing characters, otherwise valid. - assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg== ")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==*")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==X")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==XY")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==XYZ")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==XYZA")); - assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg==\n")); - assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg==\r\n")); - assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg== ")); - assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg===")); - // Characters outside alphabet inside padding. - assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg= =")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg=*=")); - assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg=\r\n=")); - // Characters inside alphabet inside padding. - assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg=X=")); - - // Table 1 chars - assertEquals(null, decodeToString("_aGVsbG8sIHdvcmx")); - assertEquals(null, decodeToString("aGV_sbG8sIHdvcmx")); - assertEquals(null, decodeToString("aGVsbG8sIHdvcmx_")); - - // Table 2 chars. - assertArrayEquals( - new int[] {0xfd, 0xa1, 0x95, 0xb1, 0xb1, 0xbc, 0xb0, 0x81, 0xdd, 0xbd, 0xc9, - 0xb1 }, - decodeToInts("/aGVsbG8sIHdvcmx")); - assertArrayEquals( - new int[] { 0x68, 0x65, 0x7f, 0xb1, 0xb1, 0xbc, 0xb0, 0x81, 0xdd, 0xbd, 0xc9, - 0xb1 }, - decodeToInts("aGV/sbG8sIHdvcmx")); - assertArrayEquals( - new int[] { 104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 0x7f }, - decodeToInts("aGVsbG8sIHdvcmx/")); - } - - private static final int[] BYTE_VALUES = { - 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77 - }; - - public void testDecode_nonAsciiBytes() throws Exception { - assertSubArrayEquals(BYTE_VALUES, 0, decodeToInts("")); - assertSubArrayEquals(BYTE_VALUES, 1, decodeToInts("/w==")); - assertSubArrayEquals(BYTE_VALUES, 2, decodeToInts("/+4=")); - assertSubArrayEquals(BYTE_VALUES, 3, decodeToInts("/+7d")); - assertSubArrayEquals(BYTE_VALUES, 4, decodeToInts("/+7dzA==")); - assertSubArrayEquals(BYTE_VALUES, 5, decodeToInts("/+7dzLs=")); - assertSubArrayEquals(BYTE_VALUES, 6, decodeToInts("/+7dzLuq")); - assertSubArrayEquals(BYTE_VALUES, 7, decodeToInts("/+7dzLuqmQ==")); - assertSubArrayEquals(BYTE_VALUES, 8, decodeToInts("/+7dzLuqmYg=")); - } - - public void testDecode_urlAlphabet() throws Exception { - assertNull(decodeToInts("_w==")); - assertNull(decodeToInts("-w==")); - } - - /** - * Convenience function for decoding from a Base64 ASCII String to an ASCII String. A String is - * used for the output to make the tests compact. Can return null if the decoder returns null. - * If any of the strings involved are non-ASCII an exception is thrown. - * Use {@link #decodeToInts(String)} for decode tests that produce bytes - * outside of the ASCII range. - */ - private static String decodeToString(String in) throws Exception { - byte[] bytes = asciiToBytes(in); - byte[] out = Base64.decode(bytes); - if (out == null) { - return null; - } - return bytesToAscii(out); - } - - private static String bytesToAscii(byte[] bytes) { - try { - CharsetDecoder decoder = StandardCharsets.US_ASCII.newDecoder(); - decoder.onMalformedInput(CodingErrorAction.REPORT); - decoder.onUnmappableCharacter(CodingErrorAction.REPORT); - ByteBuffer bytesBuffer = ByteBuffer.wrap(bytes); - CharBuffer charsBuffer = decoder.decode(bytesBuffer); - char[] chars = new char[charsBuffer.remaining()]; - charsBuffer.get(chars, 0, chars.length); - return new String(chars); - } catch (CharacterCodingException e) { - // Use bytes in your test, not Strings. - throw new AssertionFailedError("Cannot convert test bytes to String safely: " + - Arrays.toString(bytesToInts(bytes)) + " contains non-ASCII codes"); - } - } - - private static byte[] asciiToBytes(String string) { - try { - char[] chars = string.toCharArray(); - - CharsetEncoder encoder = StandardCharsets.US_ASCII.newEncoder(); - encoder.onMalformedInput(CodingErrorAction.REPORT); - encoder.onUnmappableCharacter(CodingErrorAction.REPORT); - CharBuffer charsBuffer = CharBuffer.wrap(chars); - ByteBuffer bytesBuffer = encoder.encode(charsBuffer); - byte[] bytes = new byte[bytesBuffer.remaining()]; - bytesBuffer.get(bytes, 0, bytes.length); - return bytes; - } catch (CharacterCodingException e) { - // Use bytes in your test, not Strings. - throw new AssertionFailedError("Cannot convert test String to bytes safely: " + string + - " contains non-ASCII characters"); - } - } - - /** Decodes an ASCII string, returning an int array. */ - private static int[] decodeToInts(String in) throws Exception { - byte[] bytes = Base64.decode(asciiToBytes(in)); - return bytesToInts(bytes); - } - - /** - * Convert a byte[] to an int[]. int is used because it is more convenient to use ints in - * tests. - */ - private static int[] bytesToInts(byte[] bytes) { - if (bytes == null) { - return null; - } - int[] ints = new int[bytes.length]; - for (int i = 0; i < bytes.length; i++) { - ints[i] = bytes[i] & 0xff; - } - return ints; - } - - /** Assert that decoding 'in' throws ArrayIndexOutOfBoundsException. */ - private static void assertDecodeBad(String in) throws Exception { - try { - byte[] result = Base64.decode(asciiToBytes(in)); - fail("should have failed to decode. Actually received: " + - (result == null ? result : Arrays.toString(bytesToInts(result)))); - } catch (ArrayIndexOutOfBoundsException e) { - } - } - - private static void assertArrayEquals(int[] expected, int[] actual) { - assertSubArrayEquals(expected, expected.length, actual); - } - - /** Assert that actual equals the first len bytes of expected. */ - private static void assertSubArrayEquals(int[] expected, int len, int[] actual) { - // Convert the arrays to Strings for easy comparison / reporting. - String expectedString = intsToString(expected, len); - String actualString = intsToString(actual, actual.length); - assertEquals(expectedString, actualString); - } - - private static String intsToString(int[] toConvert, int length) { - String[] out = new String[length]; - for (int i = 0; i < length; i++) { - out[i] = "0x" + Integer.toHexString(toConvert[i]); - } - return Arrays.toString(out); - } -} - diff --git a/luni/src/test/java/libcore/io/BlockGuardOsTest.java b/luni/src/test/java/libcore/io/BlockGuardOsTest.java new file mode 100644 index 000000000..567cf2d74 --- /dev/null +++ b/luni/src/test/java/libcore/io/BlockGuardOsTest.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.io; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.Assert.fail; + +@RunWith(JUnit4.class) +public class BlockGuardOsTest { + + final static Pattern pattern = Pattern.compile("[\\w\\$]+\\([^)]*\\)"); + + /** + * Checks that BlockGuardOs is updated when the Os interface changes. BlockGuardOs extends + * ForwardingOs so doing so isn't an obvious step and it can be missed. When adding methods to + * Os developers must give consideration to whether extra behavior should be added to + * BlockGuardOs. Developers failing this test should add to the list of method below + * (if the calls cannot block) or should add an override for the method with the appropriate + * calls to BlockGuard (if the calls can block). + */ + @Test + public void test_checkNewMethodsInPosix() { + List methodsNotRequireBlockGuardChecks = Arrays.asList( + "android_getaddrinfo(java.lang.String,android.system.StructAddrinfo,int)", + "bind(java.io.FileDescriptor,java.net.InetAddress,int)", + "bind(java.io.FileDescriptor,java.net.SocketAddress)", + "capget(android.system.StructCapUserHeader)", + "capset(android.system.StructCapUserHeader,android.system.StructCapUserData[])", + "dup(java.io.FileDescriptor)", + "dup2(java.io.FileDescriptor,int)", + "environ()", + "fcntlFlock(java.io.FileDescriptor,int,android.system.StructFlock)", + "fcntlInt(java.io.FileDescriptor,int,int)", + "fcntlVoid(java.io.FileDescriptor,int)", + "gai_strerror(int)", + "getegid()", + "getenv(java.lang.String)", + "geteuid()", + "getgid()", + "getifaddrs()", + "getnameinfo(java.net.InetAddress,int)", + "getpeername(java.io.FileDescriptor)", + "getpgid(int)", + "getpid()", + "getppid()", + "getpwnam(java.lang.String)", + "getpwuid(int)", + "getsockname(java.io.FileDescriptor)", + "getsockoptByte(java.io.FileDescriptor,int,int)", + "getsockoptInAddr(java.io.FileDescriptor,int,int)", + "getsockoptInt(java.io.FileDescriptor,int,int)", + "getsockoptLinger(java.io.FileDescriptor,int,int)", + "getsockoptTimeval(java.io.FileDescriptor,int,int)", + "getsockoptUcred(java.io.FileDescriptor,int,int)", + "gettid()", + "getuid()", + "if_indextoname(int)", + "if_nametoindex(java.lang.String)", + "inet_pton(int,java.lang.String)", + "ioctlFlags(java.io.FileDescriptor,java.lang.String)", + "ioctlInetAddress(java.io.FileDescriptor,int,java.lang.String)", + "ioctlInt(java.io.FileDescriptor,int,android.util.MutableInt)", + "ioctlMTU(java.io.FileDescriptor,java.lang.String)", + "isatty(java.io.FileDescriptor)", + "kill(int,int)", + "listen(java.io.FileDescriptor,int)", + "listxattr(java.lang.String)", + "mincore(long,long,byte[])", + "mlock(long,long)", + "mmap(long,long,int,int,java.io.FileDescriptor,long)", + "munlock(long,long)", + "munmap(long,long)", + "pipe2(int)", + "prctl(int,long,long,long,long)", + "setegid(int)", + "setenv(java.lang.String,java.lang.String,boolean)", + "seteuid(int)", + "setgid(int)", + "setpgid(int,int)", + "setregid(int,int)", + "setreuid(int,int)", + "setsid()", + "setsockoptByte(java.io.FileDescriptor,int,int,int)", + "setsockoptGroupReq(java.io.FileDescriptor,int,int,android.system.StructGroupReq)", + "setsockoptGroupSourceReq(java.io.FileDescriptor,int,int,android.system.StructGroupSourceReq)", + "setsockoptIfreq(java.io.FileDescriptor,int,int,java.lang.String)", + "setsockoptInt(java.io.FileDescriptor,int,int,int)", + "setsockoptIpMreqn(java.io.FileDescriptor,int,int,int)", + "setsockoptLinger(java.io.FileDescriptor,int,int,android.system.StructLinger)", + "setsockoptTimeval(java.io.FileDescriptor,int,int,android.system.StructTimeval)", + "setuid(int)", + "shutdown(java.io.FileDescriptor,int)", + "strerror(int)", + "strsignal(int)", + "sysconf(int)", + "tcdrain(java.io.FileDescriptor)", + "tcsendbreak(java.io.FileDescriptor,int)", + "umask(int)", + "uname()", + "unsetenv(java.lang.String)", + "waitpid(int,android.util.MutableInt,int)" ); + Set methodsNotRequiredBlockGuardCheckSet = new HashSet<>( + methodsNotRequireBlockGuardChecks); + + Set methodsInBlockGuardOs = new HashSet<>(); + + // Populate the set of the public methods implemented in BlockGuardOs. + for (Method method : BlockGuardOs.class.getDeclaredMethods()) { + String methodNameAndParameters = getMethodNameAndParameters(method.toString()); + methodsInBlockGuardOs.add(methodNameAndParameters); + } + + // Verify that all the methods in libcore.io.Os should either be overridden in BlockGuardOs + // or else they should be in the "methodsNotRequiredBlockGuardCheckSet". + for (Method method : Os.class.getDeclaredMethods()) { + String methodSignature = method.toString(); + String methodNameAndParameters = getMethodNameAndParameters(methodSignature); + if (!methodsNotRequiredBlockGuardCheckSet.contains(methodNameAndParameters) && + !methodsInBlockGuardOs.contains(methodNameAndParameters)) { + fail(methodNameAndParameters + " is not present in " + + "methodsNotRequiredBlockGuardCheckSet and is also not overridden in" + + " BlockGuardOs class. Either override the method in BlockGuardOs or" + + " add it in the methodsNotRequiredBlockGuardCheckSet"); + + } + } + } + + /** + * Extract method name and parameter information from the method signature. + * For example, for input "public void package.class.method(A,B)", the output will be + * "method(A,B)". + */ + private static String getMethodNameAndParameters(String methodSignature) { + Matcher methodPatternMatcher = pattern.matcher(methodSignature); + if (methodPatternMatcher.find()) { + return methodPatternMatcher.group(); + } else { + throw new IllegalArgumentException(methodSignature); + } + } +} diff --git a/luni/src/test/java/libcore/io/MemoryMappedFileTest.java b/luni/src/test/java/libcore/io/MemoryMappedFileTest.java new file mode 100644 index 000000000..1b542a58a --- /dev/null +++ b/luni/src/test/java/libcore/io/MemoryMappedFileTest.java @@ -0,0 +1,668 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.io; + +import junit.framework.TestCase; + +import android.system.ErrnoException; +import android.system.OsConstants; + +import java.io.File; +import java.io.FileOutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.util.Arrays; +import java.util.function.Function; + +public class MemoryMappedFileTest extends TestCase { + + private File tempDir; + + @Override + public void setUp() throws Exception { + super.setUp(); + tempDir = IoUtils.createTemporaryDirectory("MemoryMappedFileTest"); + } + + public void testMmapRo_missingFile() throws Exception { + try { + MemoryMappedFile.mmapRO("doesNotExist"); + fail(); + } catch (ErrnoException e) { + assertEquals(OsConstants.ENOENT, e.errno); + } + } + + public void testMmapRo_emptyFile() throws Exception { + byte[] bytes = new byte[0]; + File file = createFile(bytes); + try { + MemoryMappedFile.mmapRO(file.getPath()); + fail(); + } catch (ErrnoException e) { + assertEquals(OsConstants.EINVAL, e.errno); + } finally { + file.delete(); + } + } + + public void testMmapRo() throws Exception { + byte[] bytes = createBytes(10); + File file = createFile(bytes); + try (MemoryMappedFile memoryMappedFile = MemoryMappedFile.mmapRO(file.getPath())) { + assertEquals(10, memoryMappedFile.size()); + } finally { + file.delete(); + } + } + + public void testMmapRo_close() throws Exception { + byte[] bytes = createBytes(10); + File file = createFile(bytes); + MemoryMappedFile memoryMappedFile = MemoryMappedFile.mmapRO(file.getPath()); + memoryMappedFile.close(); + + try { + memoryMappedFile.bigEndianIterator(); + fail(); + } catch (IllegalStateException expected) { + } + + try { + memoryMappedFile.littleEndianIterator(); + fail(); + } catch (IllegalStateException expected) { + } + + // Should not have any effect. + memoryMappedFile.close(); + } + + public void testReadAfterCloseFails() throws Exception { + byte[] bytes = createBytes(10); + File file = createFile(bytes); + MemoryMappedFile memoryMappedFile = MemoryMappedFile.mmapRO(file.getPath()); + BufferIterator iterator = memoryMappedFile.bigEndianIterator(); + memoryMappedFile.close(); + + try { + iterator.readByte(); + fail(); + } catch (IllegalStateException expected) {} + } + + public void testReadByte() throws Exception { + checkReadByte(MemoryMappedFile::bigEndianIterator); + checkReadByte(MemoryMappedFile::littleEndianIterator); + } + + private void checkReadByte( + Function iteratorFactory) throws Exception { + + byte[] bytes = createBytes(10); + File file = createFile(bytes); + try { + MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); + BufferIterator iterator = iteratorFactory.apply(mappedFile); + for (int i = 0; i < bytes.length; i++) { + assertReadByteSucceeds(iterator, bytes[i]); + } + + // Check skip. + iterator.seek(0); + for (int i = 0; i < bytes.length; i += 2) { + assertReadByteSucceeds(iterator, bytes[i]); + iterator.skip(1); + } + } finally { + file.delete(); + } + } + + public void testSeek() throws Exception { + checkSeek(MemoryMappedFile::bigEndianIterator); + checkSeek(MemoryMappedFile::littleEndianIterator); + } + + private void checkSeek( + Function iteratorFactory) throws Exception { + + byte[] bytes = createBytes(10); + File file = createFile(bytes); + try { + MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); + BufferIterator iterator = iteratorFactory.apply(mappedFile); + seekRead(bytes, iterator, 2); + + seekRead(bytes, iterator, 0); + + seekRead(bytes, iterator, 1); + + seekRead(bytes, iterator, 9); + + seekReadExpectFailure(iterator, -1); + + seekRead(bytes, iterator, 1); + + seekReadExpectFailure(iterator, 10); + seekReadExpectFailure(iterator, Integer.MAX_VALUE); + seekReadExpectFailure(iterator, Integer.MIN_VALUE); + } finally { + file.delete(); + } + } + + private static void seekRead(byte[] bytes, BufferIterator iterator, int offset) { + iterator.seek(offset); + assertEquals(offset, iterator.pos()); + assertReadByteSucceeds(iterator, bytes[offset]); + } + + private static void seekReadExpectFailure(BufferIterator iterator, int offset) { + iterator.seek(offset); + assertReadByteFails(iterator); + } + + public void testSkip() throws Exception { + checkSkip(MemoryMappedFile::bigEndianIterator); + checkSkip(MemoryMappedFile::littleEndianIterator); + } + + private void checkSkip( + Function iteratorFactory) throws Exception { + + byte[] bytes = createBytes(10); + File file = createFile(bytes); + try { + MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); + BufferIterator iterator = iteratorFactory.apply(mappedFile); + iterator.skip(1); + assertEquals(1, iterator.pos()); + assertReadByteSucceeds(iterator, bytes[1]); + + iterator.skip(-1); + assertEquals(1, iterator.pos()); + assertReadByteSucceeds(iterator, bytes[1]); + + iterator.skip(2); + assertEquals(4, iterator.pos()); + assertReadByteSucceeds(iterator, bytes[4]); + + iterator.skip(-2); + assertEquals(3, iterator.pos()); + assertReadByteSucceeds(iterator, bytes[3]); + + iterator.skip(3); + assertEquals(7, iterator.pos()); + assertReadByteSucceeds(iterator, bytes[7]); + + iterator.skip(-3); + assertEquals(5, iterator.pos()); + assertReadByteSucceeds(iterator, bytes[5]); + + iterator.skip(4); + assertEquals(10, iterator.pos()); + assertReadByteFails(iterator); + + iterator.skip(-1); + assertEquals(9, iterator.pos()); + assertReadByteSucceeds(iterator, bytes[9]); + } finally { + file.delete(); + } + } + + public void testReadShort_bigEndian() throws Exception { + byte[] bytes = createBytes(10); + File file = createFile(bytes); + try { + MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); + BufferIterator iterator = mappedFile.bigEndianIterator(); + + // Even offset + short expectedValue = (short) ((bytes[0] << 8) | bytes[1]); + assertReadShortSucceeds(iterator, expectedValue); + + checkShortFailureCases(iterator); + + // Odd offset. + iterator.seek(1); + expectedValue = (short) ((bytes[1] << 8) | bytes[2]); + assertReadShortSucceeds(iterator, expectedValue); + } finally { + file.delete(); + } + } + + public void testReadShort_littleEndian() throws Exception { + byte[] bytes = createBytes(10); + File file = createFile(bytes); + try { + MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); + BufferIterator iterator = mappedFile.littleEndianIterator(); + + // Even offset + short expectedValue = (short) ((bytes[1] << 8) | bytes[0]); + assertReadShortSucceeds(iterator, expectedValue); + + checkShortFailureCases(iterator); + + // Odd offset. + iterator.seek(1); + expectedValue = (short) ((bytes[2] << 8) | bytes[1]); + assertReadShortSucceeds(iterator, expectedValue); + } finally { + file.delete(); + } + } + + private static void checkShortFailureCases(BufferIterator iterator) { + // Partly before bounds. + iterator.seek(-1); + assertReadShortFails(iterator); + + // Entirely before bounds. + iterator.seek(-2); + assertReadShortFails(iterator); + + // Partly after bounds. + iterator.seek(9); + assertReadShortFails(iterator); + + // Entirely after bounds. + iterator.seek(10); + assertReadShortFails(iterator); + } + + public void testReadInt_bigEndian() throws Exception { + byte[] bytes = createBytes(10); + File file = createFile(bytes); + try { + MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); + BufferIterator iterator = mappedFile.bigEndianIterator(); + + // Even offset + int expectedValue = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; + assertReadIntSucceeds(iterator, expectedValue); + + checkIntFailureCases(iterator); + + // Odd offset. + iterator.seek(1); + expectedValue = (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4]; + assertReadIntSucceeds(iterator, expectedValue); + } finally { + file.delete(); + } + } + + public void testReadInt_littleEndian() throws Exception { + byte[] bytes = createBytes(10); + File file = createFile(bytes); + try { + MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); + BufferIterator iterator = mappedFile.littleEndianIterator(); + + // Even offset + int expectedValue = (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; + assertReadIntSucceeds(iterator, expectedValue); + + checkIntFailureCases(iterator); + + // Odd offset. + iterator.seek(1); + expectedValue = (bytes[4] << 24) | (bytes[3] << 16) | (bytes[2] << 8) | bytes[1]; + assertReadIntSucceeds(iterator, expectedValue); + } finally { + file.delete(); + } + } + + private static void checkIntFailureCases(BufferIterator iterator) { + // Partly before bounds. + iterator.seek(-1); + assertReadIntFails(iterator); + + // Entirely before bounds. + iterator.seek(-4); + assertReadIntFails(iterator); + + // Partly after bounds. + iterator.seek(7); + assertReadIntFails(iterator); + + // Entirely after bounds. + iterator.seek(10); + assertReadIntFails(iterator); + } + + public void testReadIntArray() throws Exception { + checkReadIntArray(MemoryMappedFile::bigEndianIterator, ByteOrder.BIG_ENDIAN); + checkReadIntArray(MemoryMappedFile::littleEndianIterator, ByteOrder.LITTLE_ENDIAN); + } + + private void checkReadIntArray( + Function iteratorFactory, + ByteOrder byteOrdering) throws Exception { + + byte[] testBytes = createBytes(12); + File file = createFile(testBytes); + try { + MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); + BufferIterator iterator = iteratorFactory.apply(mappedFile); + + // Even offsets. + iterator.seek(4); + assertReadIntArraySucceeds(iterator, testBytes, byteOrdering, 2 /* intCount */); + + iterator.seek(0); + assertReadIntArraySucceeds(iterator, testBytes, byteOrdering, 3 /* intCount */); + + checkIntArrayZeroReadCases(iterator); + + // Odd offsets. + iterator.seek(1); + assertReadIntArraySucceeds(iterator, testBytes, byteOrdering, 2 /* intCount */); + iterator.seek(3); + assertReadIntArraySucceeds(iterator, testBytes, byteOrdering, 2 /* intCount */); + } finally { + file.delete(); + } + } + + private static void checkIntArrayZeroReadCases(BufferIterator iterator) { + // Zero length reads do nothing. + int posBeforeRead = iterator.pos(); + int[] dstWithExistingValues = new int[] { 111, 222 }; + iterator.readIntArray(dstWithExistingValues, 0, 0); + assertEquals(posBeforeRead, iterator.pos()); + assertArrayEquals(new int[] { 111, 222 }, dstWithExistingValues); + + try { + iterator.readIntArray(null, 0, 0); + fail(); + } catch (NullPointerException expected) { + } + assertEquals(posBeforeRead, iterator.pos()); + + int[] dst = new int[2]; + + // Partly before bounds. + iterator.seek(-1); + assertReadIntArrayFails(iterator, dst, 0, 1); + + // Entirely before bounds. + iterator.seek(-2); + assertReadIntArrayFails(iterator, dst, 0, 1); + + // Partly after bounds. + iterator.seek(9); + assertReadIntArrayFails(iterator, dst, 0, 1); + + // Entirely after bounds. + iterator.seek(12); + assertReadIntArrayFails(iterator, dst, 0, 1); + + // dst too small. + assertReadIntArrayFails(iterator, dst, 0, 3); // dst can only hold 2 ints + + // offset leaves dst too small. + assertReadIntArrayFails(iterator, dst, 1, 2); + + // Invalid offset + assertReadIntArrayFails(iterator, dst, -1, 2); + assertReadIntArrayFails(iterator, dst, 2, 2); + + // Null dst + try { + iterator.readIntArray(null, 0, 1); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testReadByteArray() throws Exception { + checkReadByteArray(MemoryMappedFile::bigEndianIterator); + checkReadByteArray(MemoryMappedFile::littleEndianIterator); + } + + private void checkReadByteArray( + Function iteratorFactory) throws Exception { + + byte[] testBytes = createBytes(12); + File file = createFile(testBytes); + try { + MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); + BufferIterator iterator = iteratorFactory.apply(mappedFile); + + // Even offsets. + iterator.seek(4); + assertReadByteArraySucceeds(iterator, testBytes, 2 /* intCount */); + + iterator.seek(0); + assertReadByteArraySucceeds(iterator, testBytes, 3 /* intCount */); + + checkByteArrayZeroReadCases(iterator); + + // Odd offsets. + iterator.seek(1); + assertReadByteArraySucceeds(iterator, testBytes, 2 /* intCount */); + iterator.seek(3); + assertReadByteArraySucceeds(iterator, testBytes, 2 /* intCount */); + } finally { + file.delete(); + } + } + + private static void checkByteArrayZeroReadCases(BufferIterator iterator) { + // Zero length reads do nothing. + int posBeforeRead = iterator.pos(); + byte[] dstWithExistingValues = new byte[] { 11, 22, 33, 44, 55, 66, 77, 88 }; + iterator.readByteArray(dstWithExistingValues, 0, 0); + assertEquals(posBeforeRead, iterator.pos()); + assertArrayEquals(new byte[] { 11, 22, 33, 44, 55, 66, 77, 88 }, dstWithExistingValues); + + try { + iterator.readByteArray(null, 0, 0); + fail(); + } catch (NullPointerException expected) { + } + assertEquals(posBeforeRead, iterator.pos()); + + byte[] dst = new byte[10]; + + // Before bounds. + iterator.seek(-1); + assertReadByteArrayFails(iterator, dst, 0, 1); + + // After bounds. + iterator.seek(12); + assertReadByteArrayFails(iterator, dst, 0, 1); + + // dst too small. + assertReadByteArrayFails(iterator, dst, 0, 11); // dst can only hold 10 bytes + + // offset leaves dst too small. + assertReadByteArrayFails(iterator, dst, 1, 10); + + // Invalid offset + assertReadByteArrayFails(iterator, dst, -1, 2); + assertReadByteArrayFails(iterator, dst, 2, 2); + + // Null dst + try { + iterator.readByteArray(null, 0, 1); + fail(); + } catch (NullPointerException expected) { + } + } + + private static void assertReadByteArrayFails( + BufferIterator iterator, byte[] dst, int offset, int intCount) { + + int posBefore = iterator.pos(); + try { + iterator.readByteArray(dst, offset, intCount); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + assertEquals(posBefore, iterator.pos()); + } + + private static void assertReadByteArraySucceeds( + BufferIterator iterator, byte[] underlyingData, int byteCount) { + + int posBefore = iterator.pos(); + + // Create a byte[] containing book-end bytes we don't expect to be touched: + // [Byte.MAX_VALUE, {the bytes we expect from underlyingData from posBefore onward}, + // Byte.MIN_VALUE]. + byte[] expectedBytes = new byte[byteCount + 2]; + expectedBytes[0] = Byte.MAX_VALUE; + expectedBytes[byteCount - 1] = Byte.MIN_VALUE; + System.arraycopy(underlyingData, posBefore, expectedBytes, 1, byteCount); + + // Get the true data. + byte[] dst = new byte[byteCount + 2]; + // Copy the two bytes we expect to be untouched. + dst[0] = expectedBytes[0]; + dst[byteCount - 1] = expectedBytes[byteCount - 1]; + // Do the read. + iterator.readByteArray(dst, 1, byteCount); + + assertArrayEquals(expectedBytes, dst); + assertEquals(posBefore + byteCount, iterator.pos()); + } + + private static void assertReadIntArrayFails( + BufferIterator iterator, int[] dst, int offset, int intCount) { + + int posBefore = iterator.pos(); + try { + iterator.readIntArray(dst, offset, intCount); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + assertEquals(posBefore, iterator.pos()); + } + + private static void assertReadIntArraySucceeds( + BufferIterator iterator, byte[] underlyingData, ByteOrder byteOrder, int intCount) { + + int posBefore = iterator.pos(); + + // Create an int[] containing book-end ints we don't expect to be touched: + // [Integer.MAX_VALUE, {the ints we expect from underlyingData from posBefore onward}, + // Integer.MIN_VALUE]. + + // Create an IntBuffer containing the ints we'd expect from underlyingData from posBefore + // onward. + ByteBuffer byteBuffer = ByteBuffer.wrap(underlyingData); + byteBuffer.position(posBefore); + IntBuffer expectedIntsBuffer = byteBuffer.slice().order(byteOrder).asIntBuffer(); + assertEquals(byteOrder, expectedIntsBuffer.order()); + + // Copy the ints we expect. + int[] expectedInts = new int[intCount + 2]; + expectedInts[0] = Integer.MAX_VALUE; + expectedInts[intCount - 1] = Integer.MIN_VALUE; + expectedIntsBuffer.get(expectedInts, 1, intCount); + + // Get the true data. + int[] dst = new int[intCount + 2]; + dst[0] = expectedInts[0]; + dst[intCount - 1] = expectedInts[intCount - 1]; + iterator.readIntArray(dst, 1, intCount); + + assertArrayEquals(expectedInts, dst); + assertEquals(posBefore + (intCount * SizeOf.INT), iterator.pos()); + } + + private static void assertReadIntFails(BufferIterator iterator) { + int posBefore = iterator.pos(); + try { + iterator.readInt(); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + assertEquals(posBefore, iterator.pos()); + } + + private static void assertReadIntSucceeds(BufferIterator iterator, int expectedValue) { + int posBefore = iterator.pos(); + assertEquals(expectedValue, iterator.readInt()); + assertEquals(posBefore + SizeOf.INT, iterator.pos()); + } + + private static void assertReadShortFails(BufferIterator iterator) { + int posBefore = iterator.pos(); + try { + iterator.readShort(); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + assertEquals(posBefore, iterator.pos()); + } + + private static void assertReadShortSucceeds(BufferIterator iterator, short expectedValue) { + int posBefore = iterator.pos(); + assertEquals(expectedValue, iterator.readShort()); + assertEquals(posBefore + SizeOf.SHORT, iterator.pos()); + } + + private static void assertReadByteFails(BufferIterator iterator) { + int posBefore = iterator.pos(); + try { + iterator.readByte(); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + // Must not advance pos. + assertEquals(posBefore, iterator.pos()); + } + + private static void assertReadByteSucceeds(BufferIterator iterator, byte expectedValue) { + int posBefore = iterator.pos(); + assertEquals(expectedValue, iterator.readByte()); + assertEquals(posBefore + 1, iterator.pos()); + } + + private static void assertArrayEquals(int[] expected, int[] actual) { + assertEquals(Arrays.toString(expected), Arrays.toString(actual)); + } + + private static void assertArrayEquals(byte[] expected, byte[] actual) { + assertEquals(Arrays.toString(expected), Arrays.toString(actual)); + } + + private static byte[] createBytes(int byteCount) { + byte[] bytes = new byte[byteCount]; + for (int i = 0; i < byteCount; i++) { + bytes[i] = (byte) i; + } + return bytes; + } + + private File createFile(byte[] bytes) throws Exception { + File file = File.createTempFile("bytes", null, tempDir); + try (FileOutputStream fos = new FileOutputStream(file)) { + fos.write(bytes); + } + return file; + } +} diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java index 5a2b95c73..07ce7d223 100644 --- a/luni/src/test/java/libcore/io/OsTest.java +++ b/luni/src/test/java/libcore/io/OsTest.java @@ -28,6 +28,8 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.net.DatagramPacket; +import java.net.DatagramSocket; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -38,9 +40,12 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; + import static android.system.OsConstants.*; public class OsTest extends TestCase { @@ -504,18 +509,20 @@ public void test_xattr() throws Exception { File file = File.createTempFile("xattr", "test"); String path = file.getAbsolutePath(); - byte[] tmp = new byte[1024]; try { try { - Libcore.os.getxattr(path, NAME_TEST, tmp); + Libcore.os.getxattr(path, NAME_TEST); fail("Expected ENODATA"); } catch (ErrnoException e) { assertEquals(OsConstants.ENODATA, e.errno); } + assertFalse(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST)); Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE); - assertEquals(VALUE_CAKE.length, Libcore.os.getxattr(path, NAME_TEST, tmp)); - assertStartsWith(VALUE_CAKE, tmp); + byte[] xattr_create = Libcore.os.getxattr(path, NAME_TEST); + assertTrue(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST)); + assertEquals(VALUE_CAKE.length, xattr_create.length); + assertStartsWith(VALUE_CAKE, xattr_create); try { Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_CREATE); @@ -525,22 +532,132 @@ public void test_xattr() throws Exception { } Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_REPLACE); - assertEquals(VALUE_PIE.length, Libcore.os.getxattr(path, NAME_TEST, tmp)); - assertStartsWith(VALUE_PIE, tmp); + byte[] xattr_replace = Libcore.os.getxattr(path, NAME_TEST); + assertTrue(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST)); + assertEquals(VALUE_PIE.length, xattr_replace.length); + assertStartsWith(VALUE_PIE, xattr_replace); Libcore.os.removexattr(path, NAME_TEST); try { - Libcore.os.getxattr(path, NAME_TEST, tmp); + Libcore.os.getxattr(path, NAME_TEST); fail("Expected ENODATA"); } catch (ErrnoException e) { assertEquals(OsConstants.ENODATA, e.errno); } + assertFalse(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST)); } finally { file.delete(); } } + public void test_xattr_NPE() throws Exception { + File file = File.createTempFile("xattr", "test"); + final String path = file.getAbsolutePath(); + final String NAME_TEST = "user.meow"; + final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8); + + // getxattr + try { + Libcore.os.getxattr(null, NAME_TEST); + fail(); + } catch (NullPointerException expected) { } + try { + Libcore.os.getxattr(path, null); + fail(); + } catch (NullPointerException expected) { } + + // listxattr + try { + Libcore.os.listxattr(null); + fail(); + } catch (NullPointerException expected) { } + + // removexattr + try { + Libcore.os.removexattr(null, NAME_TEST); + fail(); + } catch (NullPointerException expected) { } + try { + Libcore.os.removexattr(path, null); + fail(); + } catch (NullPointerException expected) { } + + // setxattr + try { + Libcore.os.setxattr(null, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE); + fail(); + } catch (NullPointerException expected) { } + try { + Libcore.os.setxattr(path, null, VALUE_CAKE, OsConstants.XATTR_CREATE); + fail(); + } catch (NullPointerException expected) { } + try { + Libcore.os.setxattr(path, NAME_TEST, null, OsConstants.XATTR_CREATE); + fail(); + } catch (NullPointerException expected) { } + } + + public void test_xattr_Errno() throws Exception { + final String NAME_TEST = "user.meow"; + final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8); + + // ENOENT, No such file or directory. + try { + Libcore.os.getxattr("", NAME_TEST); + fail(); + } catch (ErrnoException e) { + assertEquals(ENOENT, e.errno); + } + try { + Libcore.os.listxattr(""); + fail(); + } catch (ErrnoException e) { + assertEquals(ENOENT, e.errno); + } + try { + Libcore.os.removexattr("", NAME_TEST); + fail(); + } catch (ErrnoException e) { + assertEquals(ENOENT, e.errno); + } + try { + Libcore.os.setxattr("", NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE); + fail(); + } catch (ErrnoException e) { + assertEquals(ENOENT, e.errno); + } + + // ENOTSUP, Extended attributes are not supported by the filesystem, or are disabled. + final boolean root = (Libcore.os.getuid() == 0); + final String path = "/proc/self/stat"; + try { + Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE); + fail(); + } catch (ErrnoException e) { + // setxattr(2) requires root permission for writing to this file, will get EACCES otherwise. + assertEquals(root ? ENOTSUP : EACCES, e.errno); + } + try { + Libcore.os.getxattr(path, NAME_TEST); + fail(); + } catch (ErrnoException e) { + assertEquals(ENOTSUP, e.errno); + } + try { + // Linux listxattr does not set errno. + Libcore.os.listxattr(path); + } catch (ErrnoException e) { + fail(); + } + try { + Libcore.os.removexattr(path, NAME_TEST); + fail(); + } catch (ErrnoException e) { + assertEquals(ENOTSUP, e.errno); + } + } + public void test_realpath() throws Exception { File tmpDir = new File(System.getProperty("java.io.tmpdir")); // This is a chicken and egg problem. We have no way of knowing whether @@ -571,6 +688,59 @@ public void test_realpath() throws Exception { } } + /** + * Tests that TCP_USER_TIMEOUT can be set on a TCP socket, but doesn't test + * that it behaves as expected. + */ + public void test_socket_tcpUserTimeout_setAndGet() throws Exception { + final FileDescriptor fd = Libcore.os.socket(AF_INET, SOCK_STREAM, 0); + try { + int v = Libcore.os.getsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT); + assertEquals(0, v); // system default value + int newValue = 3000; + Libcore.os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT, + newValue); + assertEquals(newValue, Libcore.os.getsockoptInt(fd, OsConstants.IPPROTO_TCP, + OsConstants.TCP_USER_TIMEOUT)); + // No need to reset the value to 0, since we're throwing the socket away + } finally { + Libcore.os.close(fd); + } + } + + public void test_socket_tcpUserTimeout_doesNotWorkOnDatagramSocket() throws Exception { + final FileDescriptor fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, 0); + try { + Libcore.os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT, + 3000); + fail("datagram (connectionless) sockets shouldn't support TCP_USER_TIMEOUT"); + } catch (ErrnoException expected) { + // expected + } finally { + Libcore.os.close(fd); + } + } + + public void test_if_nametoindex_if_indextoname() throws Exception { + List nis = Collections.list(NetworkInterface.getNetworkInterfaces()); + + assertTrue(nis.size() > 0); + for (NetworkInterface ni : nis) { + int index = ni.getIndex(); + String name = ni.getName(); + assertEquals(index, Libcore.os.if_nametoindex(name)); + assertTrue(Libcore.os.if_indextoname(index).equals(name)); + } + + assertEquals(0, Libcore.os.if_nametoindex("this-interface-does-not-exist")); + assertEquals(null, Libcore.os.if_indextoname(-1000)); + + try { + Libcore.os.if_nametoindex(null); + fail(); + } catch (NullPointerException expected) { } + } + private static void assertStartsWith(byte[] expectedContents, byte[] container) { for (int i = 0; i < expectedContents.length; i++) { if (expectedContents[i] != container[i]) { @@ -579,4 +749,38 @@ private static void assertStartsWith(byte[] expectedContents, byte[] container) } } } + + public void test_readlink() throws Exception { + File path = new File(IoUtils.createTemporaryDirectory("test_readlink"), "symlink"); + + // ext2 and ext4 have PAGE_SIZE limits on symlink targets. + // If file encryption is enabled, there's extra overhead to store the + // size of the encrypted symlink target. There's also an off-by-one + // in current kernels (and marlin/sailfish where we're seeing this + // failure are still on 3.18, far from current). Given that we don't + // really care here, just use 2048 instead. http://b/33306057. + int size = 2048; + String xs = ""; + for (int i = 0; i < size - 1; ++i) xs += "x"; + + Libcore.os.symlink(xs, path.getPath()); + + assertEquals(xs, Libcore.os.readlink(path.getPath())); + } + + // Address should be correctly set for empty packets. http://b/33481605 + public void test_recvfrom_EmptyPacket() throws Exception { + try (DatagramSocket ds = new DatagramSocket(); + DatagramSocket srcSock = new DatagramSocket()) { + srcSock.send(new DatagramPacket(new byte[0], 0, ds.getLocalSocketAddress())); + + byte[] recvBuf = new byte[16]; + InetSocketAddress address = new InetSocketAddress(); + int recvCount = + android.system.Os.recvfrom(ds.getFileDescriptor$(), recvBuf, 0, 16, 0, address); + assertEquals(0, recvCount); + assertTrue(address.getAddress().isLoopbackAddress()); + assertEquals(srcSock.getLocalPort(), address.getPort()); + } + } } diff --git a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java index 74432e5e0..c199bcab6 100644 --- a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java +++ b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java @@ -26,13 +26,22 @@ import java.util.List; import android.system.ErrnoException; +import android.system.Os; import android.system.OsConstants; -import junit.framework.TestCase; +import android.system.StructStatVfs; +import android.util.MutableInt; import libcore.io.IoUtils; import libcore.io.Libcore; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public final class FileInputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); -public final class FileInputStreamTest extends TestCase { private static final int TOTAL_SIZE = 1024; private static final int SKIP_SIZE = 100; @@ -204,10 +213,22 @@ public void testClose() throws Exception { } // http://b/26117827 - public void testReadProcVersion() throws IOException { - File file = new File("/proc/version"); - FileInputStream input = new FileInputStream(file); - assertTrue(input.available() == 0); + // + // Return 0 (the conservative estimate) for files for which ioctl is not implemented. + public void test_available_on_nonIOCTL_supported_file() throws Exception { + File file = new File("/dev/zero"); + try (FileInputStream input = new FileInputStream(file)) { + assertEquals(0, input.available()); + } + + try (FileInputStream input = new FileInputStream(file)) { + android.system.Os.ioctlInt(input.getFD(), OsConstants.FIONREAD, new MutableInt(0)); + fail(); + } catch (ErrnoException expected) { + assertEquals("FIONREAD should have returned ENOTTY for the file. If it doesn't return" + + " FIONREAD, the test is no longer valid.", OsConstants.ENOTTY, + expected.errno); + } } // http://b/25695227 @@ -226,23 +247,49 @@ public void testFdLeakWhenOpeningDirectory() throws Exception { // http://b/28192631 public void testSkipOnLargeFiles() throws Exception { File largeFile = File.createTempFile("FileInputStreamTest_testSkipOnLargeFiles", ""); - FileOutputStream fos = new FileOutputStream(largeFile); + // Required space is 3.1 GB: 3GB for file plus 100M headroom. + final long requiredFreeSpaceBytes = 3172L * 1024 * 1024; + long fileSize = 3 * 1024L * 1024 * 1024; // 3 GiB + // If system doesn't have enough space free for this test, skip it. + final StructStatVfs statVfs = Os.statvfs(largeFile.getPath()); + final long freeSpaceAvailableBytes = statVfs.f_bsize * statVfs.f_bavail; + if (freeSpaceAvailableBytes < requiredFreeSpaceBytes) { + return; + } try { - byte[] buffer = new byte[1024 * 1024]; // 1 MB - for (int i = 0; i < 3 * 1024; i++) { // 3 GB - fos.write(buffer); + allocateEmptyFile(largeFile, fileSize); + assertEquals(fileSize, largeFile.length()); + try (FileInputStream fis = new FileInputStream(largeFile)) { + long lastByte = fileSize - 1; + assertEquals(0, Libcore.os.lseek(fis.getFD(), 0, OsConstants.SEEK_CUR)); + assertEquals(lastByte, fis.skip(lastByte)); } } finally { - fos.close(); + // Proactively cleanup - it's a pretty large file. + assertTrue(largeFile.delete()); } + } - FileInputStream fis = new FileInputStream(largeFile); - long lastByte = 3 * 1024 * 1024 * 1024L - 1; - assertEquals(0, Libcore.os.lseek(fis.getFD(), 0, OsConstants.SEEK_CUR)); - assertEquals(lastByte, fis.skip(lastByte)); - - // Proactively cleanup - it's a pretty large file. - assertTrue(largeFile.delete()); + /** + * Allocates a file to the specified size using fallocate, falling back to ftruncate. + */ + private static void allocateEmptyFile(File file, long fileSize) + throws IOException, InterruptedException { + // fallocate is much faster than ftruncate (<<1sec rather than 24sec for 3 GiB on Nexus 6P) + try (FileOutputStream fos = new FileOutputStream(file)) { + try { + Os.posix_fallocate(fos.getFD(), 0, fileSize); + return; + } catch (ErrnoException e) { + // Fall back to ftruncate, which works on all filesystems but is slower + } + } + // Need to reopen the file to get a valid FileDescriptor + try (FileOutputStream fos = new FileOutputStream(file)) { + Os.ftruncate(fos.getFD(), fileSize); + } catch (ErrnoException e2) { + throw new IOException("Failed to truncate: " + file, e2); + } } private static List getOpenFdsForPrefix(String path) throws Exception { diff --git a/luni/src/test/java/libcore/java/io/FileOutputStreamTest.java b/luni/src/test/java/libcore/java/io/FileOutputStreamTest.java index dd600a252..0da85597d 100644 --- a/luni/src/test/java/libcore/java/io/FileOutputStreamTest.java +++ b/luni/src/test/java/libcore/java/io/FileOutputStreamTest.java @@ -17,14 +17,17 @@ package libcore.java.io; import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import junit.framework.TestCase; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public class FileOutputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); -public class FileOutputStreamTest extends TestCase { public void testFileDescriptorOwnership() throws Exception { File tmp = File.createTempFile("FileOutputStreamTest", "tmp"); FileOutputStream fos1 = new FileOutputStream(tmp); diff --git a/luni/src/test/java/libcore/java/io/FileTest.java b/luni/src/test/java/libcore/java/io/FileTest.java index 5d5317a9e..9226e0261 100644 --- a/luni/src/test/java/libcore/java/io/FileTest.java +++ b/luni/src/test/java/libcore/java/io/FileTest.java @@ -16,13 +16,21 @@ package libcore.java.io; +import android.system.ErrnoException; +import android.system.OsConstants; + import java.io.File; import java.io.FileFilter; import java.io.FilenameFilter; import java.io.IOException; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.UUID; import libcore.io.Libcore; +import static android.system.Os.stat; + public class FileTest extends junit.framework.TestCase { static { @@ -304,24 +312,29 @@ public void testFilesWithSurrogatePairs() throws Exception { // http://b/25878034 // - // SELinux prevents stat(2) from working on some parts of the system partition, and there - // isn't currently a CTS test that enforces this for the system partition as a whole (and it - // isn't clear that there can be one). This particular file has a special label - // (see file_contexts) that makes sure it isn't unstattable but then again this file might - // disappear soon or be absent on some devices. - // - // TODO: This isn't a very good test. uncrypt is scheduled to disappear somewhere - // in the near future. Is there a better candidate file ? - public void testExistsOnSystem() { - File sh = new File("/system/bin/uncrypt"); - assertTrue(sh.exists()); + // The test makes sure that #exists doesn't use stat. To implement the same, it installs + // SECCOMP filter. The SECCOMP filter is designed to not allow stat(fstatat64/newfstatat) calls + // and whenever a thread makes the system call, android.system.ErrnoException + // (EPERM - Operation not permitted) will be raised. + public void testExistsOnSystem() throws ErrnoException, IOException { + File tmpFile = File.createTempFile("testExistsOnSystem", ".tmp"); try { - android.system.Os.stat(sh.getAbsolutePath()); - fail(); - } catch (android.system.ErrnoException expected) { + assertEquals("SECCOMP filter is not installed.", 0, installSeccompFilter()); + try { + // Verify that SECCOMP filter obstructs stat. + stat(tmpFile.getAbsolutePath()); + fail(); + } catch (ErrnoException expected) { + assertEquals(OsConstants.EPERM, expected.errno); + } + assertTrue(tmpFile.exists()); + } finally { + tmpFile.delete(); } } + private static native int installSeccompFilter(); + // http://b/25859957 // // OpenJdk is treating empty parent string as a special case, @@ -368,4 +381,16 @@ public void testFileNameNormalization() { assertEquals("/foo/bar", new File("/foo/", "/bar/").getPath()); assertEquals("/foo/bar", new File("/foo", "/bar//").getPath()); } + + public void test_toPath() { + File file = new File("testPath"); + Path filePath = file.toPath(); + assertEquals(Paths.get("testPath"), filePath); + + File file1 = new File("'\u0000'"); + try { + file1.toPath(); + fail(); + } catch (InvalidPathException expected) {} + } } diff --git a/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java b/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/libcore/java/io/OldBufferedReaderTest.java b/luni/src/test/java/libcore/java/io/OldBufferedReaderTest.java index 986c67248..71b9517af 100644 --- a/luni/src/test/java/libcore/java/io/OldBufferedReaderTest.java +++ b/luni/src/test/java/libcore/java/io/OldBufferedReaderTest.java @@ -387,15 +387,43 @@ public void test_8778372() throws Exception { PrintWriter pw = new PrintWriter(new OutputStreamWriter(pos)); pw.print("hello, world\r"); pw.flush(); - try { - Thread.sleep(2*60*1000); - } catch (InterruptedException ex) { - fail(); - } } }; t.start(); BufferedReader br = new BufferedReader(new InputStreamReader(pis)); assertEquals("hello, world", br.readLine()); } + + public void test_closeException() throws Exception { + final IOException testException = new IOException("kaboom!"); + Reader thrower = new Reader() { + @Override + public int read(char cbuf[], int off, int len) throws IOException { + // Not used + return 0; + } + + @Override + public void close() throws IOException { + throw testException; + } + }; + BufferedReader br = new BufferedReader(thrower); + + try { + br.close(); + fail(); + } catch(IOException expected) { + assertSame(testException, expected); + } + + try { + // Pre-openJdk8 BufferedReader#close() with exception wouldn't + // reset the input reader to null. This would still allow ready() + // to succeed. + br.ready(); + fail(); + } catch(IOException expected) { + } + } } diff --git a/luni/src/test/java/libcore/java/io/OldBufferedWriterTest.java b/luni/src/test/java/libcore/java/io/OldBufferedWriterTest.java index ed5b8625f..5e157d207 100644 --- a/luni/src/test/java/libcore/java/io/OldBufferedWriterTest.java +++ b/luni/src/test/java/libcore/java/io/OldBufferedWriterTest.java @@ -17,6 +17,7 @@ package libcore.java.io; +import java.io.Writer; import java.io.BufferedWriter; import java.io.IOException; import tests.support.Support_ASimpleWriter; @@ -300,6 +301,43 @@ public void test_writeLjava_lang_StringII_Exception() throws IOException { } } + public void test_closeException() throws Exception { + final IOException testException = new IOException("kaboom!"); + Writer thrower = new Writer() { + @Override + public void write(char cbuf[], int off, int len) throws IOException { + // Not used + } + + @Override + public void flush() throws IOException { + // Not used + } + + @Override + public void close() throws IOException { + throw testException; + } + }; + BufferedWriter bw = new BufferedWriter(thrower); + + try { + bw.close(); + fail(); + } catch(IOException expected) { + assertSame(testException, expected); + } + + try { + // Pre-openJdk8 BufferedWriter#close() with exception wouldn't + // reset the output writer to null. This would still allow write() + // to succeed. + bw.write(1); + fail(); + } catch(IOException expected) { + } + } + protected void setUp() { sw = new Support_StringWriter(); ssw = new Support_ASimpleWriter(true); diff --git a/luni/src/test/java/libcore/java/io/OldSequenceInputStreamTest.java b/luni/src/test/java/libcore/java/io/OldSequenceInputStreamTest.java index f7d9a49b3..db0fbe6f1 100644 --- a/luni/src/test/java/libcore/java/io/OldSequenceInputStreamTest.java +++ b/luni/src/test/java/libcore/java/io/OldSequenceInputStreamTest.java @@ -17,6 +17,8 @@ package libcore.java.io; +import java.io.InputStream; +import java.util.Vector; import java.io.IOException; import java.io.SequenceInputStream; import tests.support.Support_ASimpleInputStream; @@ -25,8 +27,8 @@ public class OldSequenceInputStreamTest extends junit.framework.TestCase { Support_ASimpleInputStream simple1, simple2; SequenceInputStream si; - String s1 = "Hello"; - String s2 = "World"; + final String s1 = "Hello"; + final String s2 = "World"; public void test_available() throws IOException { assertEquals("Returned incorrect number of bytes!", s1.length(), si.available()); @@ -152,6 +154,54 @@ public void test_read_exc() throws IOException { } } + public void test_readStackOVerflow() throws Exception { + // 2^16 should be enough to overflow + Vector inputs = new Vector<>(); + InputStream emptyInputStream = new Support_ASimpleInputStream(new byte[0]); + for (int i=0;i < 32768; i++) { + inputs.add(emptyInputStream); + } + + SequenceInputStream sequenceInputStream = new SequenceInputStream(inputs.elements()); + assertEquals(-1, sequenceInputStream.read()); + + byte[] buf = new byte[10]; + sequenceInputStream = new SequenceInputStream(inputs.elements()); + assertEquals(-1, sequenceInputStream.read(buf, 0, 10)); + } + + private SequenceInputStream createSequenceInputStreamWithGaps() { + Vector inputs = new Vector<>(); + InputStream emptyInputStream = new Support_ASimpleInputStream(new byte[0]); + inputs.add(emptyInputStream); + inputs.add(simple1); + inputs.add(emptyInputStream); + inputs.add(simple2); + inputs.add(emptyInputStream); + return new SequenceInputStream(inputs.elements()); + } + + public void test_readArraySkipsEmpty() throws Exception { + SequenceInputStream sequenceInputStream1 = createSequenceInputStreamWithGaps(); + byte[] buf = new byte[10]; + assertEquals(s1.length(), sequenceInputStream1.read(buf, 0, s1.length())); + assertEquals(s1, new String(buf, 0, s1.length())); + assertEquals(s2.length(), sequenceInputStream1.read(buf, 0, s2.length())); + assertEquals(s2, new String(buf, 0, s2.length())); + assertEquals(-1, sequenceInputStream1.read(buf, 0, s1.length())); + } + + public void test_readSkipsEmpty() throws Exception { + SequenceInputStream sequenceInputStream1 = createSequenceInputStreamWithGaps(); + for (int i=0;i < s1.length(); i++) { + assertEquals(s1.charAt(i), sequenceInputStream1.read()); + } + for (int i=0;i < s2.length(); i++) { + assertEquals(s2.charAt(i), sequenceInputStream1.read()); + } + assertEquals(-1, sequenceInputStream1.read()); + } + protected void setUp() { simple1 = new Support_ASimpleInputStream(s1); simple2 = new Support_ASimpleInputStream(s2); diff --git a/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java b/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java index 8d9945738..3811cbcd4 100644 --- a/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java +++ b/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java @@ -21,11 +21,15 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.LeakageDetectorRule; +import org.junit.Rule; +import org.junit.rules.TestRule; -import junit.framework.TestCase; -import libcore.java.lang.ref.FinalizationTester; - -public final class RandomAccessFileTest extends TestCase { +public final class RandomAccessFileTest extends TestCaseWithRules { + @Rule + public LeakageDetectorRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule(); private File file; @@ -38,41 +42,40 @@ public final class RandomAccessFileTest extends TestCase { } public void testSeekTooLarge() throws Exception { - RandomAccessFile raf = new RandomAccessFile(file, "rw"); - try { - raf.seek(Long.MAX_VALUE); - fail(); - } catch (IOException expected) { + try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { + try { + raf.seek(Long.MAX_VALUE); + fail(); + } catch (IOException expected) { + } } } public void testSetLengthTooLarge() throws Exception { - RandomAccessFile raf = new RandomAccessFile(file, "rw"); - try { - raf.setLength(Long.MAX_VALUE); - fail(); - } catch (IOException expected) { + try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { + try { + raf.setLength(Long.MAX_VALUE); + fail(); + } catch (IOException expected) { + } } } public void testSetLength64() throws Exception { - RandomAccessFile raf = new RandomAccessFile(file, "rw"); - raf.setLength(0); - assertEquals(0, file.length()); - long moreThanFourGig = ((long) Integer.MAX_VALUE) + 1L; - raf.setLength(moreThanFourGig); - assertEquals(moreThanFourGig, file.length()); + try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { + raf.setLength(0); + assertEquals(0, file.length()); + long moreThanFourGig = ((long) Integer.MAX_VALUE) + 1L; + raf.setLength(moreThanFourGig); + assertEquals(moreThanFourGig, file.length()); + } } // http://b/3015023 public void testRandomAccessFileHasCleanupFinalizer() throws Exception { - // TODO: this always succeeds on the host because our default open file limit is 32Ki. - // Add Libcore.os.getrlimit and use that instead of hard-coding. - int tooManyOpenFiles = 2000; File file = File.createTempFile("RandomAccessFileTest", "tmp"); - for (int i = 0; i < tooManyOpenFiles; i++) { - createRandomAccessFile(file); - FinalizationTester.induceFinalization(); + try (RandomAccessFile accessFile = new RandomAccessFile(file, "rw")) { + resourceLeakageDetectorRule.assertUnreleasedResourceCount(accessFile, 1); } } diff --git a/luni/src/test/java/libcore/java/lang/ByteTest.java b/luni/src/test/java/libcore/java/lang/ByteTest.java index 75d09b062..f051b9128 100644 --- a/luni/src/test/java/libcore/java/lang/ByteTest.java +++ b/luni/src/test/java/libcore/java/lang/ByteTest.java @@ -39,4 +39,22 @@ public void testStaticHashCode() { public void testBYTES() { assertEquals(1, Byte.BYTES); } + + public void testToUnsignedInt() { + for(int i = Byte.MIN_VALUE; i < Byte.MAX_VALUE; i++) { + final byte b = (byte) i; + final int ui = Byte.toUnsignedInt(b); + assertEquals(0, ui >>> Byte.BYTES * 8); + assertEquals(b, Integer.valueOf(b).byteValue()); + } + } + + public void testToUnsignedLong() { + for(int i = Byte.MIN_VALUE; i < Byte.MAX_VALUE; i++) { + final byte b = (byte) i; + final long ul = Byte.toUnsignedLong(b); + assertEquals(0, ul >>> Byte.BYTES * 8); + assertEquals(b, Long.valueOf(b).byteValue()); + } + } } diff --git a/luni/src/test/java/libcore/java/lang/ClassTest.java b/luni/src/test/java/libcore/java/lang/ClassTest.java index 69b7dc3a1..55d19c0ee 100644 --- a/luni/src/test/java/libcore/java/lang/ClassTest.java +++ b/luni/src/test/java/libcore/java/lang/ClassTest.java @@ -23,6 +23,11 @@ import junit.framework.TestCase; import dalvik.system.PathClassLoader; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; +import java.util.TreeMap; +import java.util.function.Function; public class ClassTest extends TestCase { @@ -120,4 +125,190 @@ public void test_getMethod() { fail("Got exception"); } } + + public static class TestGetVirtualMethod_Super { + protected String protectedMethod() { + return "protectedMethod"; + } + + public String publicMethod() { + return "publicMethod"; + } + + /* package */ String packageMethod() { + return "packageMethod"; + } + } + + public static class TestGetVirtualMethod extends TestGetVirtualMethod_Super { + public static void staticMethod(String foo) { + } + + public String publicMethod2() { + return "publicMethod2"; + } + + protected String protectedMethod2() { + return "protectedMethod2"; + } + + private String privateMethod() { + return "privateMethod"; + } + + /* package */ String packageMethod2() { + return "packageMethod2"; + } + } + + public void test_getVirtualMethod() throws Exception { + final Class[] noArgs = new Class[] { }; + + TestGetVirtualMethod instance = new TestGetVirtualMethod(); + TestGetVirtualMethod_Super super_instance = new TestGetVirtualMethod_Super(); + + // Package private methods from the queried class as well as super classes + // must be returned. + Method m = TestGetVirtualMethod.class.getInstanceMethod("packageMethod2", noArgs); + assertNotNull(m); + assertEquals("packageMethod2", m.invoke(instance)); + m = TestGetVirtualMethod.class.getInstanceMethod("packageMethod", noArgs); + assertNotNull(m); + assertEquals("packageMethod", m.invoke(instance)); + + // Protected methods from both the queried class as well as super classes must + // be returned. + m = TestGetVirtualMethod.class.getInstanceMethod("protectedMethod2", noArgs); + assertNotNull(m); + assertEquals("protectedMethod2", m.invoke(instance)); + m = TestGetVirtualMethod.class.getInstanceMethod("protectedMethod", noArgs); + assertNotNull(m); + assertEquals("protectedMethod", m.invoke(instance)); + + // Public methods from the queried classes and all its super classes must be + // returned. + m = TestGetVirtualMethod.class.getInstanceMethod("publicMethod2", noArgs); + assertNotNull(m); + assertEquals("publicMethod2", m.invoke(instance)); + m = TestGetVirtualMethod.class.getInstanceMethod("publicMethod", noArgs); + assertNotNull(m); + assertEquals("publicMethod", m.invoke(instance)); + + m = TestGetVirtualMethod.class.getInstanceMethod("privateMethod", noArgs); + assertNotNull(m); + + assertNull(TestGetVirtualMethod.class.getInstanceMethod("staticMethod", noArgs)); + } + + public void test_toString() throws Exception { + final String outerClassName = getClass().getName(); + final String packageProtectedClassName = PackageProtectedClass.class.getName(); + + assertToString("int", int.class); + assertToString("class [I", int[].class); + assertToString("class java.lang.Object", Object.class); + assertToString("class [Ljava.lang.Object;", Object[].class); + assertToString("class java.lang.Integer", Integer.class); + assertToString("interface java.util.function.Function", Function.class); + assertToString( + "class " + outerClassName + "$PublicStaticInnerClass", + PublicStaticInnerClass.class); + assertToString( + "class " + outerClassName + "$DefaultStaticInnerClass", + DefaultStaticInnerClass.class); + assertToString( + "interface " + outerClassName + "$PublicInnerInterface", + PublicInnerInterface.class); + assertToString( + "class " + packageProtectedClassName, + PackageProtectedClass.class); + assertToString( + "class " + outerClassName + "$PrivateStaticInnerClass", + PrivateStaticInnerClass.class); + assertToString("interface java.lang.annotation.Retention", Retention.class); + assertToString("class java.lang.annotation.RetentionPolicy", RetentionPolicy.class); + assertToString("class java.util.TreeMap", TreeMap.class); + assertToString( + "interface " + outerClassName + "$WildcardInterface", + WildcardInterface.class); + } + + private static void assertToString(String expected, Class clazz) { + assertEquals(expected, clazz.toString()); + } + + public void test_getTypeName() throws Exception { + final String outerClassName = getClass().getName(); + final String packageProtectedClassName = PackageProtectedClass.class.getName(); + + assertGetTypeName("int", int.class); + assertGetTypeName("int[]", int[].class); + assertGetTypeName("java.lang.Object", Object.class); + assertGetTypeName("java.lang.Object[]", Object[].class); + assertGetTypeName("java.lang.Integer", Integer.class); + assertGetTypeName("java.util.function.Function", Function.class); + assertGetTypeName(outerClassName + "$PublicStaticInnerClass", PublicStaticInnerClass.class); + assertGetTypeName( + outerClassName + "$DefaultStaticInnerClass", + DefaultStaticInnerClass.class); + assertGetTypeName(outerClassName + "$PublicInnerInterface", PublicInnerInterface.class); + assertGetTypeName(packageProtectedClassName, PackageProtectedClass.class); + assertGetTypeName( + outerClassName + "$PrivateStaticInnerClass", + PrivateStaticInnerClass.class); + assertGetTypeName("java.lang.annotation.Retention", Retention.class); + assertGetTypeName("java.lang.annotation.RetentionPolicy", RetentionPolicy.class); + assertGetTypeName("java.util.TreeMap", TreeMap.class); + assertGetTypeName(outerClassName + "$WildcardInterface", WildcardInterface.class); + } + + private void assertGetTypeName(String expected, Class clazz) { + assertEquals(expected, clazz.getTypeName()); + } + + public void test_toGenericString() throws Exception { + final String outerClassName = getClass().getName(); + final String packageProtectedClassName = PackageProtectedClass.class.getName(); + + assertToGenericString("int", int.class); + assertToGenericString("public abstract final class [I", int[].class); + assertToGenericString("public class java.lang.Object", Object.class); + assertToGenericString("public abstract final class [Ljava.lang.Object;", Object[].class); + assertToGenericString("public final class java.lang.Integer", Integer.class); + assertToGenericString( + "public abstract interface java.util.function.Function", + Function.class); + assertToGenericString("public static class " + outerClassName + "$PublicStaticInnerClass", + PublicStaticInnerClass.class); + assertToGenericString("static class " + outerClassName + "$DefaultStaticInnerClass", + DefaultStaticInnerClass.class); + assertToGenericString( + "public abstract static interface " + outerClassName + "$PublicInnerInterface", + PublicInnerInterface.class); + assertToGenericString("class " + packageProtectedClassName, PackageProtectedClass.class); + assertToGenericString( + "private static class " + outerClassName + "$PrivateStaticInnerClass", + PrivateStaticInnerClass.class); + assertToGenericString( + "public abstract @interface java.lang.annotation.Retention", Retention.class); + assertToGenericString("public final enum java.lang.annotation.RetentionPolicy", + RetentionPolicy.class); + assertToGenericString("public class java.util.TreeMap", TreeMap.class); + assertToGenericString( + "abstract static interface " + outerClassName + "$WildcardInterface", + WildcardInterface.class); + } + + private static void assertToGenericString(String expected, Class clazz) { + assertEquals(expected, clazz.toGenericString()); + } + + private static class PrivateStaticInnerClass {} + static class DefaultStaticInnerClass {} + public static class PublicStaticInnerClass {} + public interface PublicInnerInterface {} + interface WildcardInterface< + T extends Number, + U extends Function> + extends Comparable {} } diff --git a/luni/src/test/java/libcore/java/lang/IntegerTest.java b/luni/src/test/java/libcore/java/lang/IntegerTest.java index 64449a4ce..79d1acc78 100644 --- a/luni/src/test/java/libcore/java/lang/IntegerTest.java +++ b/luni/src/test/java/libcore/java/lang/IntegerTest.java @@ -157,4 +157,107 @@ public void testSum() { public void testBYTES() { assertEquals(4, Integer.BYTES); } + + public void testCompareUnsigned() { + int[] ordVals = {0, 1, 23, 456, 0x7fff_ffff, 0x8000_0000, 0xffff_ffff}; + for(int i = 0; i < ordVals.length; ++i) { + for(int j = 0; j < ordVals.length; ++j) { + assertEquals(Integer.compare(i, j), + Integer.compareUnsigned(ordVals[i], ordVals[j])); + } + } + } + + public void testDivideAndRemainderUnsigned() { + long[] vals = {1L, 23L, 456L, 0x7fff_ffffL, 0x8000_0000L, 0xffff_ffffL}; + + for(long dividend : vals) { + for(long divisor : vals) { + int uq = Integer.divideUnsigned((int) dividend, (int) divisor); + int ur = Integer.remainderUnsigned((int) dividend, (int) divisor); + assertEquals((int) (dividend / divisor), uq); + assertEquals((int) (dividend % divisor), ur); + assertEquals((int) dividend, uq * (int) divisor + ur); + } + } + + for(long dividend : vals) { + try { + Integer.divideUnsigned((int) dividend, 0); + fail(); + } catch (ArithmeticException expected) { } + try { + Integer.remainderUnsigned((int) dividend, 0); + fail(); + } catch (ArithmeticException expected) { } + } + } + + public void testParseUnsignedInt() { + int[] vals = {0, 1, 23, 456, 0x7fff_ffff, 0x8000_0000, 0xffff_ffff}; + + for(int val : vals) { + // Special radices + assertEquals(val, Integer.parseUnsignedInt(Integer.toBinaryString(val), 2)); + assertEquals(val, Integer.parseUnsignedInt(Integer.toOctalString(val), 8)); + assertEquals(val, Integer.parseUnsignedInt(Integer.toUnsignedString(val))); + assertEquals(val, Integer.parseUnsignedInt(Integer.toHexString(val), 16)); + + for(int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; ++radix) { + assertEquals(val, + Integer.parseUnsignedInt(Integer.toUnsignedString(val, radix), radix)); + } + } + + try { + Integer.parseUnsignedInt("-1"); + fail(); + } catch (NumberFormatException expected) { } + try { + Integer.parseUnsignedInt("123", 2); + fail(); + } catch (NumberFormatException expected) { } + try { + Integer.parseUnsignedInt(null); + fail(); + } catch (NumberFormatException expected) { } + try { + Integer.parseUnsignedInt("0", Character.MAX_RADIX + 1); + fail(); + } catch (NumberFormatException expected) { } + try { + Integer.parseUnsignedInt("0", Character.MIN_RADIX - 1); + fail(); + } catch (NumberFormatException expected) { } + } + + public void testToUnsignedLong() { + int[] vals = {0, 1, 23, 456, 0x7fff_ffff, 0x8000_0000, 0xffff_ffff}; + + for(int val : vals) { + long ul = Integer.toUnsignedLong(val); + assertEquals(0, ul >>> Integer.BYTES * 8); + assertEquals(val, (int) ul); + } + } + + public void testToUnsignedString() { + int[] vals = {0, 1, 23, 456, 0x7fff_ffff, 0x8000_0000, 0xffff_ffff}; + + for(int val : vals) { + // Special radices + assertTrue(Integer.toUnsignedString(val, 2).equals(Integer.toBinaryString(val))); + assertTrue(Integer.toUnsignedString(val, 8).equals(Integer.toOctalString(val))); + assertTrue(Integer.toUnsignedString(val, 10).equals(Integer.toUnsignedString(val))); + assertTrue(Integer.toUnsignedString(val, 16).equals(Integer.toHexString(val))); + + for(int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; ++radix) { + assertTrue(Integer.toUnsignedString(val, radix) + .equals(Long.toString(Integer.toUnsignedLong(val), radix))); + } + + // Behavior is not defined by Java API specification if the radix falls outside of valid + // range, thus we don't test for such cases. + } + } } diff --git a/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java b/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java new file mode 100644 index 000000000..d32fa20f1 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; + +public class LambdaImplementationTest extends TestCase { + + private static final String MSG = "Hello World"; + + public void testNonCapturingLambda() throws Exception { + Callable r1 = () -> MSG; + assertGeneralLambdaClassCharacteristics(r1); + assertLambdaImplementsInterfaces(r1, Callable.class); + assertLambdaMethodCharacteristics(r1, Callable.class); + assertNonSerializableLambdaCharacteristics(r1); + assertCallableBehavior(r1, MSG); + + Callable r2 = () -> MSG; + assertMultipleInstanceCharacteristics(r1, r2); + } + + interface Condition { + boolean check(T arg); + } + + public void testInstanceMethodReferenceLambda() throws Exception { + Condition c = String::isEmpty; + Class lambdaClass = c.getClass(); + assertGeneralLambdaClassCharacteristics(c); + assertLambdaImplementsInterfaces(c, Condition.class); + assertLambdaMethodCharacteristics(c, Condition.class); + assertNonSerializableLambdaCharacteristics(c); + + // Check the behavior of the lambda's method. + assertTrue(c.check("")); + assertFalse(c.check("notEmpty")); + + Method implCallMethod = lambdaClass.getMethod( + "check", Object.class /* type erasure => not String.class */); + assertTrue((Boolean) implCallMethod.invoke(c, "")); + assertFalse((Boolean) implCallMethod.invoke(c, "notEmpty")); + + Method interfaceCallMethod = Condition.class.getDeclaredMethod( + "check", Object.class /* type erasure => not String.class */); + assertTrue((Boolean) interfaceCallMethod.invoke(c, "")); + assertFalse((Boolean) interfaceCallMethod.invoke(c, "notEmpty")); + } + + public void testStaticMethodReferenceLambda() throws Exception { + Callable r1 = LambdaImplementationTest::staticMethod; + assertGeneralLambdaClassCharacteristics(r1); + assertLambdaImplementsInterfaces(r1, Callable.class); + assertLambdaMethodCharacteristics(r1, Callable.class); + assertNonSerializableLambdaCharacteristics(r1); + + assertCallableBehavior(r1, MSG); + + Callable r2 = LambdaImplementationTest::staticMethod; + assertMultipleInstanceCharacteristics(r1, r2); + } + + public void testObjectMethodReferenceLambda() throws Exception { + StringBuilder o = new StringBuilder(MSG); + Callable r1 = o::toString; + assertGeneralLambdaClassCharacteristics(r1); + assertLambdaImplementsInterfaces(r1, Callable.class); + assertLambdaMethodCharacteristics(r1, Callable.class); + assertNonSerializableLambdaCharacteristics(r1); + + assertCallableBehavior(r1, MSG); + + Callable r2 = o::toString; + assertMultipleInstanceCharacteristics(r1, r2); + } + + public void testArgumentCapturingLambda() throws Exception { + String msg = MSG; + Callable r1 = () -> msg; + assertGeneralLambdaClassCharacteristics(r1); + assertLambdaImplementsInterfaces(r1, Callable.class); + assertLambdaMethodCharacteristics(r1, Callable.class); + assertNonSerializableLambdaCharacteristics(r1); + + assertCallableBehavior(r1, MSG); + + Callable r2 = () -> msg; + assertMultipleInstanceCharacteristics(r1, r2); + } + + public void testSerializableLambda_withoutState() throws Exception { + Callable r1 = (Callable & Serializable) () -> MSG; + assertGeneralLambdaClassCharacteristics(r1); + assertLambdaImplementsInterfaces(r1, Callable.class, Serializable.class); + assertLambdaMethodCharacteristics(r1, Callable.class); + assertSerializableLambdaCharacteristics(r1); + + assertCallableBehavior(r1, MSG); + + Callable r2 = (Callable & Serializable) () -> MSG; + assertMultipleInstanceCharacteristics(r1, r2); + } + + public void testSerializableLambda_withState() throws Exception { + final int state = 123; + Callable r1 = (Callable & Serializable) () -> MSG + state; + assertGeneralLambdaClassCharacteristics(r1); + assertLambdaImplementsInterfaces(r1, Callable.class, Serializable.class); + assertLambdaMethodCharacteristics(r1, Callable.class); + assertSerializableLambdaCharacteristics(r1); + + assertCallableBehavior(r1, MSG + state); + + Callable deserializedR1 = roundtripSerialization(r1); + assertEquals(r1.call(), deserializedR1.call()); + } + + public void testBadSerializableLambda() throws Exception { + final Object state = new Object(); // Not Serializable + Callable r1 = (Callable & Serializable) () -> "Hello world: " + state; + assertGeneralLambdaClassCharacteristics(r1); + assertLambdaMethodCharacteristics(r1, Callable.class); + assertLambdaImplementsInterfaces(r1, Callable.class, Serializable.class); + + try { + serializeObject(r1); + fail(); + } catch (NotSerializableException expected) { + } + } + + public void testMultipleInterfaceLambda() throws Exception { + Callable r1 = (Callable & MarkerInterface) () -> MSG; + assertTrue(r1 instanceof MarkerInterface); + assertGeneralLambdaClassCharacteristics(r1); + assertLambdaMethodCharacteristics(r1, Callable.class); + assertLambdaImplementsInterfaces(r1, Callable.class, MarkerInterface.class); + assertNonSerializableLambdaCharacteristics(r1); + + assertCallableBehavior(r1, MSG); + } + + private static void assertSerializableLambdaCharacteristics(Object r1) throws Exception { + assertTrue(r1 instanceof Serializable); + + Object deserializedR1 = roundtripSerialization(r1); + assertFalse(deserializedR1.equals(r1)); + assertNotSame(deserializedR1, r1); + } + + @SuppressWarnings("unchecked") + private static T roundtripSerialization(T r1) throws Exception { + byte[] bytes = serializeObject(r1); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + try (ObjectInputStream is = new ObjectInputStream(bais)) { + return (T) is.readObject(); + } + } + + private static byte[] serializeObject(T r1) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream os = new ObjectOutputStream(baos)) { + os.writeObject(r1); + os.flush(); + } + return baos.toByteArray(); + } + + private static void assertLambdaImplementsInterfaces(T r1, Class... expectedInterfaces) + throws Exception { + Class lambdaClass = r1.getClass(); + + // Check directly implemented interfaces. Ordering is well-defined. + Class[] actualInterfaces = lambdaClass.getInterfaces(); + assertEquals(expectedInterfaces.length, actualInterfaces.length); + List> actual = Arrays.asList(actualInterfaces); + List> expected = Arrays.asList(expectedInterfaces); + assertEquals(expected, actual); + + // Confirm that the only method declared on the lambda's class are those defined by + // interfaces it implements. i.e. there's no additional public contract. + Set declaredMethods = new HashSet<>(); + addNonStaticPublicMethods(lambdaClass, declaredMethods); + Set expectedMethods = new HashSet<>(); + for (Class interfaceClass : expectedInterfaces) { + // Obtain methods declared by super-interfaces too. + while (interfaceClass != null) { + addNonStaticPublicMethods(interfaceClass, expectedMethods); + interfaceClass = interfaceClass.getSuperclass(); + } + } + assertEquals(expectedMethods.size(), declaredMethods.size()); + + // Check the method signatures are compatible. + for (Method expectedMethod : expectedMethods) { + Method actualMethod = + lambdaClass.getMethod(expectedMethod.getName(), + expectedMethod.getParameterTypes()); + assertEquals(expectedMethod.getReturnType(), actualMethod.getReturnType()); + } + } + + private static void addNonStaticPublicMethods(Class clazz, Set methodSet) { + for (Method interfaceMethod : clazz.getDeclaredMethods()) { + int modifiers = interfaceMethod.getModifiers(); + if ((!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { + methodSet.add(interfaceMethod); + } + } + } + + private static void assertNonSerializableLambdaCharacteristics(Object r1) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream os = new ObjectOutputStream(baos)) { + os.writeObject(r1); + os.flush(); + fail(); + } catch (NotSerializableException expected) { + } + } + + private static void assertMultipleInstanceCharacteristics(Object r1, Object r2) + throws Exception { + + // Unclear if any of this is *guaranteed* to be true. + + // Check the objects are not the same and do not equal. This could influence collection + // behavior. + assertNotSame(r1, r2); + assertTrue(!r1.equals(r2)); + + // Confirm the classes differ. + Class lambda1Class = r1.getClass(); + Class lambda2Class = r2.getClass(); + assertNotSame(lambda1Class, lambda2Class); + } + + private static void assertGeneralLambdaClassCharacteristics(Object r1) throws Exception { + Class lambdaClass = r1.getClass(); + + // Lambda objects have classes that have names. + assertNotNull(lambdaClass.getName()); + assertNotNull(lambdaClass.getSimpleName()); + assertNotNull(lambdaClass.getCanonicalName()); + + // Lambda classes are "synthetic classes" that are not arrays. + assertFalse(lambdaClass.isAnnotation()); + assertFalse(lambdaClass.isInterface()); + assertFalse(lambdaClass.isArray()); + assertFalse(lambdaClass.isEnum()); + assertFalse(lambdaClass.isPrimitive()); + assertTrue(lambdaClass.isSynthetic()); + assertNull(lambdaClass.getComponentType()); + + // Expected modifiers + int classModifiers = lambdaClass.getModifiers(); + assertTrue(Modifier.isFinal(classModifiers)); + + // Unexpected modifiers + assertFalse(Modifier.isPrivate(classModifiers)); + assertFalse(Modifier.isPublic(classModifiers)); + assertFalse(Modifier.isProtected(classModifiers)); + assertFalse(Modifier.isStatic(classModifiers)); + assertFalse(Modifier.isSynchronized(classModifiers)); + assertFalse(Modifier.isVolatile(classModifiers)); + assertFalse(Modifier.isTransient(classModifiers)); + assertFalse(Modifier.isNative(classModifiers)); + assertFalse(Modifier.isInterface(classModifiers)); + assertFalse(Modifier.isAbstract(classModifiers)); + assertFalse(Modifier.isStrict(classModifiers)); + + // Check the classloader, inheritance hierarchy and package. + assertSame(LambdaImplementationTest.class.getClassLoader(), lambdaClass.getClassLoader()); + assertSame(Object.class, lambdaClass.getSuperclass()); + assertSame(Object.class, lambdaClass.getGenericSuperclass()); + assertEquals(LambdaImplementationTest.class.getPackage(), lambdaClass.getPackage()); + + // Check the implementation of the non-final public methods that all Objects possess. + assertNotNull(r1.toString()); + assertTrue(r1.equals(r1)); + assertEquals(System.identityHashCode(r1), r1.hashCode()); + } + + private static void assertLambdaMethodCharacteristics(T r1, Class samInterfaceClass) + throws Exception { + // Find the single abstract method on the interface. + Method singleAbstractMethod = null; + for (Method method : samInterfaceClass.getDeclaredMethods()) { + if (Modifier.isAbstract(method.getModifiers())) { + singleAbstractMethod = method; + break; + } + } + assertNotNull(singleAbstractMethod); + + // Confirm the lambda implements the method as expected. + Method implementationMethod = r1.getClass().getMethod( + singleAbstractMethod.getName(), singleAbstractMethod.getParameterTypes()); + assertSame(singleAbstractMethod.getReturnType(), implementationMethod.getReturnType()); + assertSame(r1.getClass(), implementationMethod.getDeclaringClass()); + assertFalse(implementationMethod.isSynthetic()); + assertFalse(implementationMethod.isBridge()); + assertFalse(implementationMethod.isDefault()); + } + + private static String staticMethod() { + return MSG; + } + + private interface MarkerInterface { + } + + private static void assertCallableBehavior(Callable r1, T expectedResult) + throws Exception { + assertEquals(expectedResult, r1.call()); + + Method implCallMethod = r1.getClass().getDeclaredMethod("call"); + assertEquals(expectedResult, implCallMethod.invoke(r1)); + + Method interfaceCallMethod = Callable.class.getDeclaredMethod("call"); + assertEquals(expectedResult, interfaceCallMethod.invoke(r1)); + } +} diff --git a/luni/src/test/java/libcore/java/lang/LongTest.java b/luni/src/test/java/libcore/java/lang/LongTest.java index adf63034c..2937547c7 100644 --- a/luni/src/test/java/libcore/java/lang/LongTest.java +++ b/luni/src/test/java/libcore/java/lang/LongTest.java @@ -16,6 +16,7 @@ package libcore.java.lang; +import java.math.BigInteger; import java.util.Properties; public class LongTest extends junit.framework.TestCase { @@ -163,4 +164,110 @@ public void testSum() { public void testBYTES() { assertEquals(8, Long.BYTES); } + + public void testCompareUnsigned() { + long[] ordVals = {0L, 1L, 23L, 456L, 0x7fff_ffff_ffff_ffffL, 0x8000_0000_0000_0000L, + 0xffff_ffff_ffff_ffffL}; + for(int i = 0; i < ordVals.length; ++i) { + for(int j = 0; j < ordVals.length; ++j) { + assertEquals(Integer.compare(i, j), + Long.compareUnsigned(ordVals[i], ordVals[j])); + } + } + } + + public void testDivideAndRemainderUnsigned() { + BigInteger[] vals = { + BigInteger.ONE, + BigInteger.valueOf(23L), + BigInteger.valueOf(456L), + BigInteger.valueOf(0x7fff_ffff_ffff_ffffL), + BigInteger.valueOf(0x7fff_ffff_ffff_ffffL).add(BigInteger.ONE), + BigInteger.valueOf(2).shiftLeft(63).subtract(BigInteger.ONE) + }; + + for(BigInteger dividend : vals) { + for(BigInteger divisor : vals) { + long uq = Long.divideUnsigned(dividend.longValue(), divisor.longValue()); + long ur = Long.remainderUnsigned(dividend.longValue(), divisor.longValue()); + assertEquals(dividend.divide(divisor).longValue(), uq); + assertEquals(dividend.remainder(divisor).longValue(), ur); + assertEquals(dividend.longValue(), uq * divisor.longValue() + ur); + } + } + + for(BigInteger dividend : vals) { + try { + Long.divideUnsigned(dividend.longValue(), 0); + fail(); + } catch (ArithmeticException expected) { } + try { + Long.remainderUnsigned(dividend.longValue(), 0); + fail(); + } catch (ArithmeticException expected) { } + } + } + + public void testParseUnsignedLong() { + long[] vals = {0L, 1L, 23L, 456L, 0x7fff_ffff_ffff_ffffL, 0x8000_0000_0000_0000L, + 0xffff_ffff_ffff_ffffL}; + + for(long val : vals) { + // Special radices + assertEquals(val, Long.parseUnsignedLong(Long.toBinaryString(val), 2)); + assertEquals(val, Long.parseUnsignedLong(Long.toOctalString(val), 8)); + assertEquals(val, Long.parseUnsignedLong(Long.toUnsignedString(val))); + assertEquals(val, Long.parseUnsignedLong(Long.toHexString(val), 16)); + + for(int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; ++radix) { + assertEquals(val, + Long.parseUnsignedLong(Long.toUnsignedString(val, radix), radix)); + } + } + + try { + Long.parseUnsignedLong("-1"); + fail(); + } catch (NumberFormatException expected) { } + try { + Long.parseUnsignedLong("123", 2); + fail(); + } catch (NumberFormatException expected) { } + try { + Long.parseUnsignedLong(null, 2); + fail(); + } catch (NumberFormatException expected) { } + try { + Long.parseUnsignedLong("0", Character.MAX_RADIX + 1); + fail(); + } catch (NumberFormatException expected) { } + try { + Long.parseUnsignedLong("0", Character.MIN_RADIX - 1); + fail(); + } catch (NumberFormatException expected) { } + } + + public void testToUnsignedString() { + long[] vals = {0L, 1L, 23L, 456L, 0x7fff_ffff_ffff_ffffL, 0x8000_0000_0000_0000L, + 0xffff_ffff_ffff_ffffL}; + + for(long val : vals) { + // Special radices + assertTrue(Long.toUnsignedString(val, 2).equals(Long.toBinaryString(val))); + assertTrue(Long.toUnsignedString(val, 8).equals(Long.toOctalString(val))); + assertTrue(Long.toUnsignedString(val, 10).equals(Long.toUnsignedString(val))); + assertTrue(Long.toUnsignedString(val, 16).equals(Long.toHexString(val))); + + for(int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; ++radix) { + int upper = (int) (val >>> 32), lower = (int) val; + BigInteger b = (BigInteger.valueOf(Integer.toUnsignedLong(upper))).shiftLeft(32). + add(BigInteger.valueOf(Integer.toUnsignedLong(lower))); + + assertTrue(Long.toUnsignedString(val, radix).equals(b.toString(radix))); + } + + // Behavior is not defined by Java API specification if the radix falls outside of valid + // range, thus we don't test for such cases. + } + } } diff --git a/luni/src/test/java/libcore/java/lang/OldAndroidMathTest.java b/luni/src/test/java/libcore/java/lang/OldAndroidMathTest.java index c18f2bd2f..ab6cfe6ed 100644 --- a/luni/src/test/java/libcore/java/lang/OldAndroidMathTest.java +++ b/luni/src/test/java/libcore/java/lang/OldAndroidMathTest.java @@ -20,6 +20,8 @@ import junit.framework.Assert; import junit.framework.TestCase; +import static java.lang.Math.PI; + public class OldAndroidMathTest extends TestCase { private static final double HYP = Math.sqrt(2.0); @@ -101,13 +103,51 @@ public void testAtanD() { double answer = Math.tan(Math.atan(1.0)); assertTrue("Returned incorrect arc tangent: " + answer, answer <= 1.0 && answer >= 9.9999999999999983E-1); + + assertEquals("wrong atan(1)", PI / 4, Math.atan(1d), 0); + assertEquals("wrong atan(-1)", -PI / 4, Math.atan(-1), 0); + assertEquals("wrong atan(+INF)", PI / 2, Math.atan(Double.POSITIVE_INFINITY), 0); + assertEquals("wrong atan(-INF)", -PI / 2, Math.atan(Double.NEGATIVE_INFINITY), 0); + } + + public void testAtanDZeroValues() { + double negativeZero = Math.copySign(0d, -1d); + assertEquals("wrong value for atan(0)", 0, Math.atan(0d), 0); + assertTrue("Wrong sign for atan(0)", Math.copySign(1, Math.atan(0d)) == 1); + + assertEquals("wrong value for atan(-0)", negativeZero, Math.atan(negativeZero), 0); + assertTrue("Wrong sign for atan(-0)", Math.copySign(1, Math.atan(negativeZero)) == -1); } public void testAtan2DD() { - // Test for method double java.lang.Math.atan2(double, double) - double answer = Math.atan(Math.tan(1.0)); - assertTrue("Returned incorrect arc tangent: " + answer, answer <= 1.0 - && answer >= 9.9999999999999983E-1); + // Verify values are put in the correct quadrants. + assertEquals("wrong atan2(0, 1)", 0, Math.atan2(0, 1), 0); + assertEquals("wrong atan2(1, 1)", PI / 4, Math.atan2(1, 1), 0); + assertEquals("wrong atan2(1, 0)", PI / 2, Math.atan2(1, 0), 0); + assertEquals("wrong atan2(1, -1)", 3 * PI / 4, Math.atan2(1, -1), 0); + assertEquals("wrong atan2(0, -1)", PI, Math.atan2(0, -1), 0); + assertEquals("wrong atan2(-1, -1)", -3 * PI / 4, Math.atan2(-1, -1), 0); + assertEquals("wrong atan2(-1, 0)", -PI / 2, Math.atan2(-1, 0), 0); + assertEquals("wrong atan2(-1, 1)", -PI / 4, Math.atan2(-1, 1), 0); + + // Check numeric values. + assertEquals("atan2(42, 42) != atan2(1, 1)", Math.atan2(1, 1), Math.atan2(42, 42), 0); + assertEquals("wrong atan2(2, 1)", Math.atan(2), Math.atan2(2, 1), 0); + assertEquals("wrong atan2(5, 3)", Math.atan(5d / 3), Math.atan2(5, 3), 0); + assertEquals("wrong atan2(9, 10)", Math.atan(9d / 10), Math.atan2(9, 10), 0); + assertEquals("wrong atan2(-10, 5)", Math.atan(-10d / 5), Math.atan2(-10, 5), 0); + } + + public void testAtan2DDZeroValues() { + double negativeZero = Math.copySign(0d, -1d); + assertEquals("wrong atan2(+0, +0)", 0, Math.atan2(+0, +0), 0); + assertTrue("Wrong sign for atan2(+0, +0)", + Math.copySign(1, Math.atan2(+0, +0)) == 1);; + assertEquals("wrong atan2(+0, -0)", PI, Math.atan2(+0, negativeZero), 0); + assertEquals("wrong atan2(-0, +0)", -0, Math.atan2(negativeZero, +0), 0); + assertTrue("Wrong sign for atan2(-0, +0)", + Math.copySign(1, Math.atan2(negativeZero, +0)) == -1); + assertEquals("wrong atan2(-0, -0)", -PI, Math.atan2(negativeZero, negativeZero), 0); } public void testCbrtD() { @@ -560,7 +600,7 @@ public void testRandom() { assertEquals("Wrong value E", 4613303445314885481L, Double.doubleToLongBits(Math.E)); assertEquals("Wrong value PI", - 4614256656552045848L, Double.doubleToLongBits(Math.PI)); + 4614256656552045848L, Double.doubleToLongBits(PI)); for (int i = 500; i >= 0; i--) { double d = Math.random(); diff --git a/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java b/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/libcore/java/lang/OldCharacterTest.java b/luni/src/test/java/libcore/java/lang/OldCharacterTest.java index 8e6f9abb1..4ad50a4a2 100644 --- a/luni/src/test/java/libcore/java/lang/OldCharacterTest.java +++ b/luni/src/test/java/libcore/java/lang/OldCharacterTest.java @@ -59,18 +59,18 @@ public void test_codePointCountLjava_lang_CharArrayII() { public void test_getDirectionality() throws Exception { byte[] directionalities = { - // BEGIN android-changed + // BEGIN Android-changed // Unicode 5.1 defines U+0370 to be Greek capital letter Heta. Character.DIRECTIONALITY_LEFT_TO_RIGHT, - // END android-changed. + // END Android-changed. Character.DIRECTIONALITY_LEFT_TO_RIGHT, Character.DIRECTIONALITY_RIGHT_TO_LEFT, - // BEGIN android-changed + // BEGIN Android-changed // Unicode standard 5.1 changed category of unicode point 0x0600 from AL to AN Character.DIRECTIONALITY_ARABIC_NUMBER, - // END android-changed. + // END Android-changed. Character.DIRECTIONALITY_EUROPEAN_NUMBER, // Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR, @@ -91,15 +91,15 @@ public void test_getDirectionality() throws Exception { }; char[] characters = { - // BEGIN android-changed + // BEGIN Android-changed // Unicode 5.1 defines U+0370 to be Greek capital letter Heta. '\u0370', // 1 - // END android-changed + // END Android-changed '\u00B5', // 0 '\u05BE', // 1 - // BEGIN android-changed + // BEGIN Android-changed '\u0600', // 6 - // END android-changed + // END Android-changed '\u00B2', // 3 // '', // No common char in this group on android and java. '\u00B1', // 5 diff --git a/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java b/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java index 294cea2ed..15119bfad 100644 --- a/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java +++ b/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java @@ -27,6 +27,9 @@ import java.util.Arrays; import java.util.Vector; import tests.support.resource.Support_Resources; +import dalvik.system.VMRuntime; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; public class OldRuntimeTest extends junit.framework.TestCase { @@ -436,18 +439,11 @@ public void test_traceInstructions() { } public void test_traceMethodCalls() { + Runtime.getRuntime().traceMethodCalls(false); try { - Runtime.getRuntime().traceMethodCalls(false); Runtime.getRuntime().traceMethodCalls(true); - Runtime.getRuntime().traceMethodCalls(false); - } catch (RuntimeException ex) { - // Slightly ugly: we default to the SD card, which may or may not - // be there. So we also accept the error case as a success, since - // it means we actually did enable tracing (or tried to). - if (!"file open failed".equals(ex.getMessage())) { - throw ex; - } - } + fail(); + } catch (UnsupportedOperationException expected) {} } @SuppressWarnings("deprecation") @@ -519,4 +515,73 @@ public void test_loadLibrary() { //expected } } + + // b/25859957 + public void test_loadDeprecated() throws Exception { + final int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion(); + try { + try { + // Call Runtime#load(String, ClassLoader) at API level 24 (N). It will fail + // with a UnsatisfiedLinkError because requested library doesn't exits. + VMRuntime.getRuntime().setTargetSdkVersion(24); + Method loadMethod = + Runtime.class.getDeclaredMethod("load", String.class, ClassLoader.class); + loadMethod.setAccessible(true); + loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null); + fail(); + } catch(InvocationTargetException expected) { + assertTrue(expected.getCause() instanceof UnsatisfiedLinkError); + } + + try { + // Call Runtime#load(String, ClassLoader) at API level 25. It will fail + // with a IllegalStateException because it's deprecated. + VMRuntime.getRuntime().setTargetSdkVersion(25); + Method loadMethod = + Runtime.class.getDeclaredMethod("load", String.class, ClassLoader.class); + loadMethod.setAccessible(true); + loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null); + fail(); + } catch(InvocationTargetException expected) { + assertTrue(expected.getCause() instanceof UnsupportedOperationException); + } + } finally { + VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion); + } + } + + // b/25859957 + public void test_loadLibraryDeprecated() throws Exception { + final int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion(); + try { + try { + // Call Runtime#loadLibrary(String, ClassLoader) at API level 24 (N). It will fail + // with a UnsatisfiedLinkError because requested library doesn't exits. + VMRuntime.getRuntime().setTargetSdkVersion(24); + Method loadMethod = + Runtime.class.getDeclaredMethod("loadLibrary", String.class, ClassLoader.class); + loadMethod.setAccessible(true); + loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null); + fail(); + } catch(InvocationTargetException expected) { + assertTrue(expected.getCause() instanceof UnsatisfiedLinkError); + } + + try { + // Call Runtime#load(String, ClassLoader) at API level 25. It will fail + // with a IllegalStateException because it's deprecated. + + VMRuntime.getRuntime().setTargetSdkVersion(25); + Method loadMethod = + Runtime.class.getDeclaredMethod("loadLibrary", String.class, ClassLoader.class); + loadMethod.setAccessible(true); + loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null); + fail(); + } catch(InvocationTargetException expected) { + assertTrue(expected.getCause() instanceof UnsupportedOperationException); + } + } finally { + VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion); + } + } } diff --git a/luni/src/test/java/libcore/java/lang/OldSystemTest.java b/luni/src/test/java/libcore/java/lang/OldSystemTest.java index f7a69a3ec..224e69787 100644 --- a/luni/src/test/java/libcore/java/lang/OldSystemTest.java +++ b/luni/src/test/java/libcore/java/lang/OldSystemTest.java @@ -255,22 +255,9 @@ public void test_clearProperty() { } } - public void test_gc() { - Runtime rt = Runtime.getRuntime(); - Vector vec = new Vector(); - long beforeTest = rt.freeMemory(); - while(rt.freeMemory() < beforeTest * 2/3) { - vec.add(new StringBuffer(1000)); - } - long beforeGC = rt.totalMemory() - rt.freeMemory(); - vec = null; - System.gc(); - System.runFinalization(); - long afterGC = rt.totalMemory() - rt.freeMemory(); - assertTrue("memory was not released after calling System.gc()." + - "before gc: " + beforeGC + "; after gc: " + afterGC, - beforeGC > afterGC); - } + // Android-changed: test_gc() was deleted. PhantomReferenceTest provides basic + // coverage for the fact that System.gc() executes a garbage collection if + // followed by System.runFinalization(). public void test_getenv() { // String[] props = { "PATH", "HOME", "USER"}; @@ -321,15 +308,26 @@ public void test_load() throws Exception { } catch(NullPointerException expected) { } - // Trivial positive test for System.load: Attempt to load a libc.so - it's guaranteed - // to exist and is whitelisted for use from applications. + // Trivial positive test for System.load: Attempt to load a liblog.so - it's guaranteed + // to exist and is whitelisted for use from applications. Also, it's in the library search + // path for host builds. final ClassLoader cl = getClass().getClassLoader(); // ClassLoader.findLibrary has protected access, so it's guaranteed to exist. final Method m = ClassLoader.class.getDeclaredMethod("findLibrary", String.class); assertNotNull(m); - String libPath = (String) m.invoke(cl, "c"); + String libPath = (String) m.invoke(cl, "log"); assertNotNull(libPath); System.load(new File(libPath).getAbsolutePath()); + + // A negative test for a library that exists but isn't specified as an absolute path. + // In other words, a name for which System.loadLibrary(libname) would suceed and + // System.load(libname) would fail. + String libName = new File(libPath).getName(); + try { + System.load(libName); + fail(); + } catch (UnsatisfiedLinkError expected) { + } } public void test_loadLibrary() { diff --git a/luni/src/test/java/libcore/java/lang/OldThreadGroupTest.java b/luni/src/test/java/libcore/java/lang/OldThreadGroupTest.java index d8f153c2e..206fdce79 100644 --- a/luni/src/test/java/libcore/java/lang/OldThreadGroupTest.java +++ b/luni/src/test/java/libcore/java/lang/OldThreadGroupTest.java @@ -67,6 +67,7 @@ public boolean isActivelyRunning(long maxWait) { } private ThreadGroup initialThreadGroup = null; + private List myThreads; public void test_activeGroupCount() { ThreadGroup tg = new ThreadGroup("group count"); @@ -324,6 +325,7 @@ public void uncaughtException(Thread t, Throwable e) { @Override protected void setUp() { + myThreads = new ArrayList<>(); initialThreadGroup = Thread.currentThread().getThreadGroup(); ThreadGroup rootThreadGroup = initialThreadGroup; while (rootThreadGroup.getParent() != null) { @@ -332,11 +334,17 @@ protected void setUp() { } @Override - protected void tearDown() { - try { - // Give the threads a chance to die. - Thread.sleep(50); - } catch (InterruptedException e) { + protected void tearDown() throws Exception { + // Make sure we stop any MyThread threads that may have been left running. + for (MyThread thread : myThreads) { + thread.interrupt(); + } + // Make sure the threads have stopped. + for (MyThread thread : myThreads) { + thread.join(2000); + if (thread.isAlive()) { + fail("Thread " + thread + " did not die as it should have."); + } } } @@ -367,7 +375,9 @@ private List populateGroupsWithThreads(ThreadGroup group, int threadCo private void populateGroupsWithThreads(ThreadGroup group, int threadCount, List out) { for (int i = 0; i < threadCount; i++) { - out.add(new MyThread(group, "MyThread " + i + " of " + threadCount)); + MyThread thread = new MyThread(group, "MyThread " + i + " of " + threadCount); + myThreads.add(thread); + out.add(thread); } // Recursively for subgroups (if any) diff --git a/luni/src/test/java/libcore/java/lang/OldThreadTest.java b/luni/src/test/java/libcore/java/lang/OldThreadTest.java index 03031ac87..963268070 100644 --- a/luni/src/test/java/libcore/java/lang/OldThreadTest.java +++ b/luni/src/test/java/libcore/java/lang/OldThreadTest.java @@ -193,7 +193,7 @@ public void test_sleepJ() { st = new Thread() { public void run() { try { - sleep(10000); + sleep(1000); } catch(InterruptedException ie) { wasInterrupted = true; } @@ -203,7 +203,7 @@ public void run() { st.start(); try { - Thread.sleep(5000); + Thread.sleep(500); } catch(InterruptedException e) { fail("Unexpected InterruptedException was thrown"); } @@ -211,7 +211,7 @@ public void run() { st.interrupt(); try { - Thread.sleep(5000); + Thread.sleep(500); } catch(InterruptedException e) { fail("Unexpected InterruptedException was thrown"); } @@ -240,7 +240,7 @@ public void test_sleepJI() { st = new Thread() { public void run() { try { - sleep(10000, 99999); + sleep(1000, 9999); } catch(InterruptedException ie) { wasInterrupted = true; } @@ -250,7 +250,7 @@ public void run() { st.start(); try { - Thread.sleep(5000, 99999); + Thread.sleep(500, 9999); } catch(InterruptedException e) { fail("Unexpected InterruptedException was thrown"); } @@ -258,7 +258,7 @@ public void run() { st.interrupt(); try { - Thread.sleep(5000); + Thread.sleep(500); } catch(InterruptedException e) { fail("Unexpected InterruptedException was thrown"); } @@ -275,7 +275,7 @@ public void test_yield() { } Counter countersYeld = new Counter(true); try { - Thread.sleep(11000); + Thread.sleep(1100); } catch(InterruptedException ie) {} for(Counter c:countersNotYeld) { @@ -293,7 +293,7 @@ public Counter(boolean isDoYield) { } public void run() { - for(int i = 0; i < 10000; i++) { + for(int i = 0; i < 1000; i++) { if(isDoYield) yield(); counter ++; diff --git a/luni/src/test/java/libcore/java/lang/PackageProtectedClass.java b/luni/src/test/java/libcore/java/lang/PackageProtectedClass.java new file mode 100644 index 000000000..95a253cc6 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/PackageProtectedClass.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2016 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. + */ +package libcore.java.lang; + +/** + * Used by {@link ClassTest}. + */ +class PackageProtectedClass { +} diff --git a/luni/src/test/java/libcore/java/lang/PackageTest.java b/luni/src/test/java/libcore/java/lang/PackageTest.java index 269831fc9..176d0de74 100644 --- a/luni/src/test/java/libcore/java/lang/PackageTest.java +++ b/luni/src/test/java/libcore/java/lang/PackageTest.java @@ -16,6 +16,7 @@ package libcore.java.lang; +import dalvik.system.VMRuntime; import java.util.Arrays; import java.util.List; import junit.framework.TestCase; @@ -39,8 +40,20 @@ public void testGetPackage() { // http://b/28057303 public void test_toString() throws Exception { - Package libcoreJavaLang = Package.getPackage("libcore.java.lang"); - assertEquals("package libcore.java.lang", libcoreJavaLang.toString()); + int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion(); + try { + VMRuntime.getRuntime().setTargetSdkVersion(24); + Package libcoreJavaLang = Package.getPackage("libcore.java.lang"); + assertEquals("package libcore.java.lang", + libcoreJavaLang.toString()); + + VMRuntime.getRuntime().setTargetSdkVersion(25); + libcoreJavaLang = Package.getPackage("libcore.java.lang"); + assertEquals("package libcore.java.lang, Unknown, version 0.0", + libcoreJavaLang.toString()); + } finally { + VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion); + } } // http://b/5171136 diff --git a/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java b/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java index 51aed3897..7b7da9d8b 100644 --- a/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java +++ b/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java @@ -16,28 +16,61 @@ package libcore.java.lang; +import android.system.ErrnoException; +import android.system.Os; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileDescriptor; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.Writer; +import java.lang.ProcessBuilder.Redirect; +import java.lang.ProcessBuilder.Redirect.Type; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; -import libcore.java.util.AbstractResourceLeakageDetectorTestCase; -import static tests.support.Support_Exec.execAndCheckOutput; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import junit.framework.TestCase; +import libcore.io.IoUtils; -public class ProcessBuilderTest extends AbstractResourceLeakageDetectorTestCase { +import static java.lang.ProcessBuilder.Redirect.INHERIT; +import static java.lang.ProcessBuilder.Redirect.PIPE; + +public class ProcessBuilderTest extends TestCase { + private static final String TAG = ProcessBuilderTest.class.getSimpleName(); + + /** + * Returns the path to a command that is in /system/bin/ on Android but + * /bin/ elsewhere. + * + * @param desktopPath the command path outside Android; must start with /bin/. + */ + private static String commandPath(String desktopPath) { + if (!desktopPath.startsWith("/bin/")) { + throw new IllegalArgumentException(desktopPath); + } + String devicePath = System.getenv("ANDROID_ROOT") + desktopPath; + return new File(devicePath).exists() ? devicePath : desktopPath; + } private static String shell() { - String deviceSh = System.getenv("ANDROID_ROOT") + "/bin/sh"; - String desktopSh = "/bin/sh"; - return new File(deviceSh).exists() ? deviceSh : desktopSh; + return commandPath("/bin/sh"); } private static void assertRedirectErrorStream(boolean doRedirect, String expectedOut, String expectedErr) throws Exception { ProcessBuilder pb = new ProcessBuilder(shell(), "-c", "echo out; echo err 1>&2"); pb.redirectErrorStream(doRedirect); - execAndCheckOutput(pb, expectedOut, expectedErr); + checkProcessExecution(pb, ResultCodes.ZERO, + "" /* processInput */, expectedOut, expectedErr); } public void test_redirectErrorStream_true() throws Exception { @@ -48,10 +81,165 @@ public void test_redirectErrorStream_false() throws Exception { assertRedirectErrorStream(false, "out\n", "err\n"); } + public void testRedirectErrorStream_outputAndErrorAreMerged() throws Exception { + Process process = new ProcessBuilder(shell()) + .redirectErrorStream(true) + .start(); + try { + long pid = getChildProcessPid(process); + String path = "/proc/" + pid + "/fd/"; + assertEquals("stdout and stderr should point to the same socket", + Os.stat(path + "1").st_ino, Os.stat(path + "2").st_ino); + } finally { + process.destroy(); + } + } + + /** + * Tests that a child process can INHERIT this parent process's + * stdin / stdout / stderr file descriptors. + */ + public void testRedirectInherit() throws Exception { + // We can't run shell() here because that exits when run with INHERITed + // file descriptors from this process; "sleep" is less picky. + Process process = new ProcessBuilder() + .command(commandPath("/bin/sleep"), "5") // in seconds + .redirectInput(Redirect.INHERIT) + .redirectOutput(Redirect.INHERIT) + .redirectError(Redirect.INHERIT) + .start(); + try { + List parentInodes = Arrays.asList( + Os.fstat(FileDescriptor.in).st_ino, + Os.fstat(FileDescriptor.out).st_ino, + Os.fstat(FileDescriptor.err).st_ino); + long childPid = getChildProcessPid(process); + // Get the inode numbers of the ends of the symlink chains + List childInodes = Arrays.asList( + Os.stat("/proc/" + childPid + "/fd/0").st_ino, + Os.stat("/proc/" + childPid + "/fd/1").st_ino, + Os.stat("/proc/" + childPid + "/fd/2").st_ino); + + assertEquals(parentInodes, childInodes); + } catch (ErrnoException e) { + // Either (a) Os.fstat on our PID, or (b) Os.stat on our child's PID, failed. + throw new AssertionError("stat failed; child process: " + process, e); + } finally { + process.destroy(); + } + } + + public void testRedirectFile_input() throws Exception { + String inputFileContents = "process input for testing\n" + TAG; + File file = File.createTempFile(TAG, "in"); + try (Writer writer = new FileWriter(file)) { + writer.write(inputFileContents); + } + ProcessBuilder pb = new ProcessBuilder(shell(), "-c", "cat").redirectInput(file); + checkProcessExecution(pb, ResultCodes.ZERO, /* processInput */ "", + /* expectedOutput */ inputFileContents, /* expectedError */ ""); + assertTrue(file.delete()); + } + + public void testRedirectFile_output() throws Exception { + File file = File.createTempFile(TAG, "out"); + String processInput = TAG + "\narbitrary string for testing!"; + ProcessBuilder pb = new ProcessBuilder(shell(), "-c", "cat").redirectOutput(file); + checkProcessExecution(pb, ResultCodes.ZERO, processInput, + /* expectedOutput */ "", /* expectedError */ ""); + + String fileContents = new String(IoUtils.readFileAsByteArray( + file.getAbsolutePath())); + assertEquals(processInput, fileContents); + assertTrue(file.delete()); + } + + public void testRedirectFile_error() throws Exception { + File file = File.createTempFile(TAG, "err"); + String processInput = ""; + String missingFilePath = "/test-missing-file-" + TAG; + ProcessBuilder pb = new ProcessBuilder("ls", missingFilePath).redirectError(file); + checkProcessExecution(pb, ResultCodes.NONZERO, processInput, + /* expectedOutput */ "", /* expectedError */ ""); + + String fileContents = new String(IoUtils.readFileAsByteArray(file.getAbsolutePath())); + assertTrue(file.delete()); + // We assume that the path of the missing file occurs in the ls stderr. + assertTrue("Unexpected output: " + fileContents, + fileContents.contains(missingFilePath) && !fileContents.equals(missingFilePath)); + } + + public void testRedirectPipe_inputAndOutput() throws Exception { + //checkProcessExecution(pb, expectedResultCode, processInput, expectedOutput, expectedError) + + String testString = "process input and output for testing\n" + TAG; + { + ProcessBuilder pb = new ProcessBuilder(shell(), "-c", "cat") + .redirectInput(PIPE) + .redirectOutput(PIPE); + checkProcessExecution(pb, ResultCodes.ZERO, testString, testString, ""); + } + + // Check again without specifying PIPE explicitly, since that is the default + { + ProcessBuilder pb = new ProcessBuilder(shell(), "-c", "cat"); + checkProcessExecution(pb, ResultCodes.ZERO, testString, testString, ""); + } + + // Because the above test is symmetric regarding input vs. output, test + // another case where input and output are different. + { + ProcessBuilder pb = new ProcessBuilder("echo", testString); + checkProcessExecution(pb, ResultCodes.ZERO, "", testString + "\n", ""); + } + } + + public void testRedirectPipe_error() throws Exception { + String missingFilePath = "/test-missing-file-" + TAG; + + // Can't use checkProcessExecution() because we don't want to rely on an exact error content + Process process = new ProcessBuilder("ls", missingFilePath) + .redirectError(Redirect.PIPE).start(); + process.getOutputStream().close(); // no process input + int resultCode = process.waitFor(); + ResultCodes.NONZERO.assertMatches(resultCode); + assertEquals("", readAsString(process.getInputStream())); // no process output + String errorString = readAsString(process.getErrorStream()); + // We assume that the path of the missing file occurs in the ls stderr. + assertTrue("Unexpected output: " + errorString, + errorString.contains(missingFilePath) && !errorString.equals(missingFilePath)); + } + + public void testRedirect_nullStreams() throws IOException { + Process process = new ProcessBuilder() + .command(shell()) + .inheritIO() + .start(); + try { + assertNullInputStream(process.getInputStream()); + assertNullOutputStream(process.getOutputStream()); + assertNullInputStream(process.getErrorStream()); + } finally { + process.destroy(); + } + } + + public void testRedirectErrorStream_nullStream() throws IOException { + Process process = new ProcessBuilder() + .command(shell()) + .redirectErrorStream(true) + .start(); + try { + assertNullInputStream(process.getErrorStream()); + } finally { + process.destroy(); + } + } + public void testEnvironment() throws Exception { ProcessBuilder pb = new ProcessBuilder(shell(), "-c", "echo $A"); pb.environment().put("A", "android"); - execAndCheckOutput(pb, "android\n", ""); + checkProcessExecution(pb, ResultCodes.ZERO, "", "android\n", ""); } public void testDestroyClosesEverything() throws IOException { @@ -102,6 +290,270 @@ public void testEnvironmentMapForbidsNulls() throws Exception { fail(); } catch (NullPointerException expected) { } + try { + environment.containsKey(null); + fail("Attempting to check the presence of a null key should throw"); + } catch (NullPointerException expected) { + } + try { + environment.containsValue(null); + fail("Attempting to check the presence of a null value should throw"); + } catch (NullPointerException expected) { + } assertEquals(before, environment); } + + /** + * Tests attempting to query the presence of a non-String key or value + * in the environment map. Since that is a {@code Map}, + * it's hard to imagine this ever breaking, but it's good to have a test + * since it's called out in the documentation. + */ + public void testEnvironmentMapForbidsNonStringKeysAndValues() { + ProcessBuilder pb = new ProcessBuilder("echo", "Hello, world!"); + Map environment = pb.environment(); + Integer nonString = Integer.valueOf(23); + try { + environment.containsKey(nonString); + fail("Attempting to query the presence of a non-String key should throw"); + } catch (ClassCastException expected) { + } + try { + environment.get(nonString); + fail("Attempting to query the presence of a non-String key should throw"); + } catch (ClassCastException expected) { + } + try { + environment.containsValue(nonString); + fail("Attempting to query the presence of a non-String value should throw"); + } catch (ClassCastException expected) { + } + } + + /** + * Checks that INHERIT and PIPE tend to have different hashCodes + * in any particular instance of the runtime. + * We test this by asserting that they use the identity hashCode, + * which is a sufficient but not necessary condition for this. + * If the implementation changes to a different sufficient condition + * in future, this test should be updated accordingly. + */ + public void testRedirect_inheritAndPipeTendToHaveDifferentHashCode() { + assertIdentityHashCode(INHERIT); + assertIdentityHashCode(PIPE); + } + + public void testRedirect_hashCodeDependsOnFile() { + File file = new File("/tmp/file"); + File otherFile = new File("/tmp/some_other_file") { + @Override public int hashCode() { return 1 + file.hashCode(); } + }; + Redirect a = Redirect.from(file); + Redirect b = Redirect.from(otherFile); + assertFalse("Unexpectedly equal hashCode: " + a + " vs. " + b, + a.hashCode() == b.hashCode()); + } + + /** + * Tests that {@link Redirect}'s equals() and hashCode() is sane. + */ + public void testRedirect_equals() { + File fileA = new File("/tmp/fileA"); + File fileB = new File("/tmp/fileB"); + File fileB2 = new File("/tmp/fileB"); + // check that test is set up correctly + assertFalse(fileA.equals(fileB)); + assertEquals(fileB, fileB2); + + assertSymmetricEquals(Redirect.appendTo(fileB), Redirect.appendTo(fileB2)); + assertSymmetricEquals(Redirect.from(fileB), Redirect.from(fileB2)); + assertSymmetricEquals(Redirect.to(fileB), Redirect.to(fileB2)); + + Redirect[] redirects = new Redirect[] { + INHERIT, + PIPE, + Redirect.appendTo(fileA), + Redirect.from(fileA), + Redirect.to(fileA), + Redirect.appendTo(fileB), + Redirect.from(fileB), + Redirect.to(fileB), + }; + for (Redirect a : redirects) { + for (Redirect b : redirects) { + if (a != b) { + assertFalse("Unexpectedly equal: " + a + " vs. " + b, a.equals(b)); + assertFalse("Unexpected asymmetric equality: " + a + " vs. " + b, b.equals(a)); + } + } + } + } + + /** + * Tests the {@link Redirect#type() type} and {@link Redirect#file() file} of + * various Redirects. These guarantees are made in the respective javadocs, + * so we're testing them together here. + */ + public void testRedirect_fileAndType() { + File file = new File("/tmp/fake-file-for/java.lang.ProcessBuilderTest"); + assertRedirectFileAndType(null, Type.INHERIT, INHERIT); + assertRedirectFileAndType(null, Type.PIPE, PIPE); + assertRedirectFileAndType(file, Type.APPEND, Redirect.appendTo(file)); + assertRedirectFileAndType(file, Type.READ, Redirect.from(file)); + assertRedirectFileAndType(file, Type.WRITE, Redirect.to(file)); + } + + private static void assertRedirectFileAndType(File expectedFile, Type expectedType, + Redirect redirect) { + assertEquals(redirect.toString(), expectedFile, redirect.file()); + assertEquals(redirect.toString(), expectedType, redirect.type()); + } + + public void testRedirect_defaultsToPipe() { + assertRedirects(PIPE, PIPE, PIPE, new ProcessBuilder()); + } + + public void testRedirect_setAndGet() { + File file = new File("/tmp/fake-file-for/java.lang.ProcessBuilderTest"); + assertRedirects(Redirect.from(file), PIPE, PIPE, new ProcessBuilder().redirectInput(file)); + assertRedirects(PIPE, Redirect.to(file), PIPE, new ProcessBuilder().redirectOutput(file)); + assertRedirects(PIPE, PIPE, Redirect.to(file), new ProcessBuilder().redirectError(file)); + assertRedirects(Redirect.from(file), INHERIT, Redirect.to(file), + new ProcessBuilder() + .redirectInput(PIPE) + .redirectOutput(INHERIT) + .redirectError(file) + .redirectInput(file)); + + assertRedirects(Redirect.INHERIT, Redirect.INHERIT, Redirect.INHERIT, + new ProcessBuilder().inheritIO()); + } + + public void testCommand_setAndGet() { + List expected = Collections.unmodifiableList( + Arrays.asList("echo", "fake", "command", "for", TAG)); + assertEquals(expected, new ProcessBuilder().command(expected).command()); + assertEquals(expected, new ProcessBuilder().command("echo", "fake", "command", "for", TAG) + .command()); + } + + public void testDirectory_setAndGet() { + File directory = new File("/tmp/fake/directory/for/" + TAG); + assertEquals(directory, new ProcessBuilder().directory(directory).directory()); + assertNull(new ProcessBuilder().directory()); + assertNull(new ProcessBuilder() + .directory(directory) + .directory(null) + .directory()); + } + + /** + * One or more result codes returned by {@link Process#waitFor()}. + */ + enum ResultCodes { + ZERO { @Override void assertMatches(int actualResultCode) { + assertEquals(0, actualResultCode); + } }, + NONZERO { @Override void assertMatches(int actualResultCode) { + assertTrue("Expected resultCode != 0, got 0", actualResultCode != 0); + } }; + + /** asserts that the given code falls within this ResultCodes */ + abstract void assertMatches(int actualResultCode); + } + + /** + * Starts the specified process, writes the specified input to it and waits for the process + * to finish; then, then checks that the result code and output / error are expected. + * + *

This method assumes that the process consumes and produces character data encoded with + * the platform default charset. + */ + private static void checkProcessExecution(ProcessBuilder pb, + ResultCodes expectedResultCode, String processInput, + String expectedOutput, String expectedError) throws Exception { + Process process = pb.start(); + Future processOutput = asyncRead(process.getInputStream()); + Future processError = asyncRead(process.getErrorStream()); + try (OutputStream outputStream = process.getOutputStream()) { + outputStream.write(processInput.getBytes(Charset.defaultCharset())); + } + int actualResultCode = process.waitFor(); + expectedResultCode.assertMatches(actualResultCode); + assertEquals(expectedOutput, processOutput.get()); + assertEquals(expectedError, processError.get()); + } + + /** + * Asserts that inputStream is a null input stream. + */ + private static void assertNullInputStream(InputStream inputStream) throws IOException { + assertEquals(-1, inputStream.read()); + assertEquals(0, inputStream.available()); + inputStream.close(); // should do nothing + } + + /** + * Asserts that outputStream is a null output + * stream. + */ + private static void assertNullOutputStream(OutputStream outputStream) throws IOException { + try { + outputStream.write(42); + fail("NullOutputStream.write(int) must throw IOException: " + outputStream); + } catch (IOException expected) { + // expected + } + outputStream.close(); // should do nothing + } + + private static void assertRedirects(Redirect in, Redirect out, Redirect err, ProcessBuilder pb) { + List expected = Arrays.asList(in, out, err); + List actual = Arrays.asList( + pb.redirectInput(), pb.redirectOutput(), pb.redirectError()); + assertEquals(expected, actual); + } + + private static void assertIdentityHashCode(Redirect redirect) { + assertEquals(System.identityHashCode(redirect), redirect.hashCode()); + } + + private static void assertSymmetricEquals(Redirect a, Redirect b) { + assertEquals(a, b); + assertEquals(b, a); + assertEquals(a.hashCode(), b.hashCode()); + } + + private static long getChildProcessPid(Process process) { + // Hack: UNIXProcess.pid is private; parse toString() instead of reflection + Matcher matcher = Pattern.compile("pid=(\\d+)").matcher(process.toString()); + assertTrue("Can't find PID in: " + process, matcher.find()); + long result = Integer.parseInt(matcher.group(1)); + return result; + } + + static String readAsString(InputStream inputStream) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] data = new byte[1024]; + int numRead; + while ((numRead = inputStream.read(data)) >= 0) { + outputStream.write(data, 0, numRead); + } + return new String(outputStream.toByteArray(), Charset.defaultCharset()); + } + + /** + * Reads the entire specified {@code inputStream} asynchronously. + */ + static FutureTask asyncRead(final InputStream inputStream) { + final FutureTask result = new FutureTask<>(() -> readAsString(inputStream)); + new Thread("read asynchronously from " + inputStream) { + @Override + public void run() { + result.run(); + } + }.start(); + return result; + } + } diff --git a/luni/src/test/java/libcore/java/lang/ShortTest.java b/luni/src/test/java/libcore/java/lang/ShortTest.java index 9a1d5a5ec..a3db71aae 100644 --- a/luni/src/test/java/libcore/java/lang/ShortTest.java +++ b/luni/src/test/java/libcore/java/lang/ShortTest.java @@ -39,4 +39,22 @@ public void testStaticHashCode() { public void testBYTES() { assertEquals(2, Short.BYTES); } + + public void testToUnsignedInt() { + for(int i = Short.MIN_VALUE; i < Short.MAX_VALUE; i++) { + final short b = (short) i; + final int ui = Short.toUnsignedInt(b); + assertEquals(0, ui >>> Short.BYTES * 8); + assertEquals(b, Integer.valueOf(b).shortValue()); + } + } + + public void testToUnsignedLong() { + for(int i = Short.MIN_VALUE; i < Short.MAX_VALUE; i++) { + final short b = (short) i; + final long ul = Short.toUnsignedLong(b); + assertEquals(0, ul >>> Short.BYTES * 8); + assertEquals(b, Long.valueOf(b).shortValue()); + } + } } diff --git a/luni/src/test/java/libcore/java/lang/StringTest.java b/luni/src/test/java/libcore/java/lang/StringTest.java index 5809da326..7e34d4493 100644 --- a/luni/src/test/java/libcore/java/lang/StringTest.java +++ b/luni/src/test/java/libcore/java/lang/StringTest.java @@ -25,6 +25,7 @@ import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; import java.util.Arrays; +import java.util.ArrayList; import java.util.Locale; import junit.framework.TestCase; @@ -547,4 +548,48 @@ public void testCodePoints() { assertEquals((int) low, surrogateCP.codePoints().toArray()[1]); // Unmatched surrogate. assertEquals((int) '0', surrogateCP.codePoints().toArray()[2]); } + + public void testJoin_CharSequenceArray() { + assertEquals("", String.join("-")); + assertEquals("", String.join("-", "")); + assertEquals("foo", String.join("-", "foo")); + assertEquals("foo---bar---boo", String.join("---", "foo", "bar", "boo")); + assertEquals("foobarboo", String.join("", "foo", "bar", "boo")); + assertEquals("null-null", String.join("-", null, null)); + assertEquals("¯\\_(ツ)_/¯", String.join("(ツ)", "¯\\_", "_/¯")); + } + + public void testJoin_CharSequenceArray_NPE() { + try { + String.join(null, "foo", "bar"); + fail(); + } catch (NullPointerException expected) {} + } + + public void testJoin_Iterable() { + ArrayList iterable = new ArrayList<>(); + assertEquals("", String.join("-", iterable)); + + iterable.add("foo"); + assertEquals("foo", String.join("-", iterable)); + + iterable.add("bar"); + assertEquals("foo...bar", String.join("...", iterable)); + + iterable.add("foo"); + assertEquals("foo-bar-foo", String.join("-", iterable)); + assertEquals("foobarfoo", String.join("", iterable)); + } + + public void testJoin_Iterable_NPE() { + try { + String.join(null, new ArrayList()); + fail(); + } catch (NullPointerException expected) {} + + try { + String.join("-", (Iterable)null); + fail(); + } catch (NullPointerException expected) {} + } } diff --git a/luni/src/test/java/libcore/java/lang/SystemTest.java b/luni/src/test/java/libcore/java/lang/SystemTest.java index f16c380a6..48f45914f 100644 --- a/luni/src/test/java/libcore/java/lang/SystemTest.java +++ b/luni/src/test/java/libcore/java/lang/SystemTest.java @@ -16,17 +16,16 @@ package libcore.java.lang; +import junit.framework.TestCase; + import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; -import java.lang.SecurityException; -import java.lang.SecurityManager; import java.util.Formatter; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; -import junit.framework.TestCase; public class SystemTest extends TestCase { @@ -126,8 +125,8 @@ public void testArrayCopyNull() { public void testArrayCopyConcurrentModification() { final AtomicBoolean done = new AtomicBoolean(); - final Object[] source = new Object[1024 * 1024]; - String[] target = new String[1024 * 1024]; + final Object[] source = new Object[512 * 1024]; + String[] target = new String[512 * 1024]; new Thread() { @Override public void run() { @@ -140,7 +139,7 @@ public void testArrayCopyConcurrentModification() { } }.start(); - for (int i = 0; i < 8192; i++) { + for (int i = 0; i < 2048; i++) { try { System.arraycopy(source, 0, target, 0, source.length); assertNull(target[source.length - 1]); // make sure the wrong type didn't sneak in diff --git a/luni/src/test/java/libcore/java/lang/ThreadTest.java b/luni/src/test/java/libcore/java/lang/ThreadTest.java index 10ca9a7ed..6f62dd1c5 100644 --- a/luni/src/test/java/libcore/java/lang/ThreadTest.java +++ b/luni/src/test/java/libcore/java/lang/ThreadTest.java @@ -16,9 +16,17 @@ package libcore.java.lang; +import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; + import junit.framework.Assert; import junit.framework.TestCase; + +import org.mockito.InOrder; +import org.mockito.Mockito; + import libcore.java.lang.ref.FinalizationTester; public final class ThreadTest extends TestCase { @@ -128,6 +136,46 @@ public void testContextClassLoaderIsInherited() { assertSame(Thread.currentThread().getContextClassLoader(), other.getContextClassLoader()); } + public void testUncaughtExceptionPreHandler_calledBeforeDefaultHandler() { + UncaughtExceptionHandler initialHandler = Mockito.mock(UncaughtExceptionHandler.class); + UncaughtExceptionHandler defaultHandler = Mockito.mock(UncaughtExceptionHandler.class); + InOrder inOrder = Mockito.inOrder(initialHandler, defaultHandler); + + UncaughtExceptionHandler originalDefaultHandler + = Thread.getDefaultUncaughtExceptionHandler(); + Thread.setUncaughtExceptionPreHandler(initialHandler); + Thread.setDefaultUncaughtExceptionHandler(defaultHandler); + try { + Thread t = new Thread(); + Throwable e = new Throwable(); + t.dispatchUncaughtException(e); + inOrder.verify(initialHandler).uncaughtException(t, e); + inOrder.verify(defaultHandler).uncaughtException(t, e); + inOrder.verifyNoMoreInteractions(); + } finally { + Thread.setDefaultUncaughtExceptionHandler(originalDefaultHandler); + Thread.setUncaughtExceptionPreHandler(null); + } + } + + public void testUncaughtExceptionPreHandler_noDefaultHandler() { + UncaughtExceptionHandler initialHandler = Mockito.mock(UncaughtExceptionHandler.class); + UncaughtExceptionHandler originalDefaultHandler + = Thread.getDefaultUncaughtExceptionHandler(); + Thread.setUncaughtExceptionPreHandler(initialHandler); + Thread.setDefaultUncaughtExceptionHandler(null); + try { + Thread t = new Thread(); + Throwable e = new Throwable(); + t.dispatchUncaughtException(e); + Mockito.verify(initialHandler).uncaughtException(t, e); + Mockito.verifyNoMoreInteractions(initialHandler); + } finally { + Thread.setDefaultUncaughtExceptionHandler(originalDefaultHandler); + Thread.setUncaughtExceptionPreHandler(null); + } + } + /** * Thread.getStackTrace() is broken. http://b/1252043 */ @@ -137,11 +185,9 @@ public void testGetStackTrace() throws Exception { doSomething(); } public void doSomething() { - for (int i = 0; i < 20;) { - try { - Thread.sleep(100); - } catch (InterruptedException ignored) { - } + try { + Thread.sleep(4000); + } catch (InterruptedException ignored) { } } }; @@ -153,6 +199,7 @@ public void doSomething() { // Expect to find MyThread.doSomething in the trace assertTrue(trace.getClassName().contains("ThreadTest") && trace.getMethodName().equals("doSomething")); + t1.join(); } public void testGetAllStackTracesIncludesAllGroups() throws Exception { @@ -179,6 +226,89 @@ public void testNativeThreadNames() throws Exception { } } + // http://b/29746125 + public void testParkUntilWithUnderflowValue() throws Exception { + final Thread current = Thread.currentThread(); + + // watchdog to unpark the tread in case it will be parked + AtomicBoolean afterPark = new AtomicBoolean(false); + AtomicBoolean wasParkedForLongTime = new AtomicBoolean(false); + Thread watchdog = new Thread() { + @Override public void run() { + try { + sleep(5000); + } catch(InterruptedException expected) {} + + if (!afterPark.get()) { + wasParkedForLongTime.set(true); + current.unpark$(); + } + } + }; + watchdog.start(); + + // b/29746125 is caused by underflow: parkUntilArg - System.currentTimeMillis() > 0. + // parkUntil$ should return immediately for everyargument that's <= + // System.currentTimeMillis(). + current.parkUntil$(Long.MIN_VALUE); + if (wasParkedForLongTime.get()) { + fail("Current thread was parked, but was expected to return immediately"); + } + afterPark.set(true); + watchdog.interrupt(); + watchdog.join(); + } + + /** + * Check that call Thread.start for already started thread + * throws {@code IllegalThreadStateException} + */ + public void testThreadDoubleStart() { + final ReentrantLock lock = new ReentrantLock(); + Thread thread = new Thread() { + public void run() { + // Lock should be acquired by the main thread and + // this thread should block on this operation. + lock.lock(); + } + }; + // Acquire lock to ensure that new thread is not finished + // when we call start() second time. + lock.lock(); + try { + thread.start(); + try { + thread.start(); + fail(); + } catch (IllegalThreadStateException expected) { + } + } finally { + lock.unlock(); + } + try { + thread.join(); + } catch (InterruptedException ignored) { + } + } + + /** + * Check that call Thread.start for already finished thread + * throws {@code IllegalThreadStateException} + */ + public void testThreadRestart() { + Thread thread = new Thread(); + thread.start(); + try { + thread.join(); + } catch (InterruptedException ignored) { + } + try { + thread.start(); + fail(); + } catch (IllegalThreadStateException expected) { + } + } + // This method returns {@code null} if all tests pass, or a non-null String containing // failure details if an error occured. private static native String nativeTestNativeThreadNames(); diff --git a/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java b/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java new file mode 100644 index 000000000..5ba87f9aa --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.lang.invoke; + +import junit.framework.TestCase; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.invoke.MutableCallSite; +import java.lang.invoke.VolatileCallSite; +import java.lang.invoke.WrongMethodTypeException; + +import static java.lang.invoke.MethodHandles.Lookup.*; + +public class CallSitesTest extends TestCase { + public void test_ConstantCallSite() throws Throwable { + final MethodType type = MethodType.methodType(int.class, int.class, int.class); + final MethodHandle mh = + MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type); + final ConstantCallSite site = new ConstantCallSite(mh); + assertEquals(mh, site.getTarget()); + assertEquals(type, site.type()); + + int n = (int) site.dynamicInvoker().invokeExact(7, 37); + assertEquals(44, n); + try { + site.setTarget(mh); + fail(); + } catch (UnsupportedOperationException e) { + } + } + + public void test_EarlyBoundMutableCallSite() throws Throwable { + final MethodType type = MethodType.methodType(int.class, int.class, int.class); + final MethodHandle add2 = + MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type); + MutableCallSite site = new MutableCallSite(type); + commonMutableCallSitesTest(site, add2); + } + + public void test_EarlyBoundVolatileCallSite() throws Throwable { + final MethodType type = MethodType.methodType(int.class, int.class, int.class); + final MethodHandle add2 = + MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type); + VolatileCallSite site = new VolatileCallSite(type); + commonMutableCallSitesTest(site, add2); + } + + public void test_LateBoundMutableCallSite() throws Throwable { + final MethodType type = MethodType.methodType(int.class, int.class, int.class); + MutableCallSite site = new MutableCallSite(type); + assertEquals(type, site.type()); + try { + int dummy = (int) site.getTarget().invokeExact(1, 1); + fail(); + } catch (IllegalStateException e) { + assertEquals("uninitialized call site", e.getMessage()); + } + final MethodHandle add2 = + MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type); + site.setTarget(add2); + commonMutableCallSitesTest(site, add2); + } + + public void test_LateBoundVolatileCallSite() throws Throwable { + final MethodType type = MethodType.methodType(int.class, int.class, int.class); + VolatileCallSite site = new VolatileCallSite(type); + assertEquals(type, site.type()); + try { + int dummy = (int) site.getTarget().invokeExact(1, 1); + fail(); + } catch (IllegalStateException e) { + assertEquals("uninitialized call site", e.getMessage()); + } + final MethodHandle add2 = + MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type); + site.setTarget(add2); + commonMutableCallSitesTest(site, add2); + } + + private static void commonMutableCallSitesTest(CallSite site, + MethodHandle firstTarget) throws Throwable{ + site.setTarget(firstTarget); + site.setTarget(firstTarget); + + int x = (int) firstTarget.invokeExact(2, 6); + assertEquals(8, x); + + int y = (int) site.getTarget().invokeExact(2, 6); + assertEquals(8, y); + + int z = (int) site.dynamicInvoker().invokeExact(2, 6); + assertEquals(8, z); + + try { + site.setTarget(null); + fail(); + } catch (NullPointerException e) { + } + + final MethodHandle other = MethodHandles.lookup().findStatic( + CallSitesTest.class, "add3", + MethodType.methodType(int.class, int.class, int.class, int.class)); + try { + site.setTarget(other); + fail(); + } catch (WrongMethodTypeException e) { + } + assertEquals(firstTarget, site.getTarget()); + + final MethodHandle sub2 = + MethodHandles.lookup().findStatic(CallSitesTest.class, "sub2", firstTarget.type()); + site.setTarget(sub2); + assertEquals(sub2, site.getTarget()); + assertEquals(100, (int) site.dynamicInvoker().invokeExact(147, 47)); + } + + private static int add2(int x, int y) { + return x + y; + } + + private static int add3(int x, int y, int z) { + return x + y + z; + } + + private static int sub2(int x, int y) { + return x - y; + } +} + diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleAccessorsTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleAccessorsTest.java new file mode 100644 index 000000000..a9f680275 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleAccessorsTest.java @@ -0,0 +1,922 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.lang.invoke; + +import junit.framework.TestCase; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.WrongMethodTypeException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class MethodHandleAccessorsTest extends junit.framework.TestCase { + public static class ValueHolder { + public boolean m_z = false; + public byte m_b = 0; + public char m_c = 'a'; + public short m_s = 0; + public int m_i = 0; + public float m_f = 0.0f; + public double m_d = 0.0; + public long m_j = 0; + public String m_l = "a"; + + public static boolean s_z; + public static byte s_b; + public static char s_c; + public static short s_s; + public static int s_i; + public static float s_f; + public static double s_d; + public static long s_j; + public static String s_l; + + public final int m_fi = 0xa5a5a5a5; + public static final int s_fi = 0x5a5a5a5a; + } + + private static enum PrimitiveType { + Boolean, + Byte, + Char, + Short, + Int, + Long, + Float, + Double, + String, + } + + private static enum AccessorType { + IPUT, + SPUT, + IGET, + SGET, + } + + static void setByte(MethodHandle m, ValueHolder v, byte value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void setByte(MethodHandle m, byte value, boolean expectFailure) throws Throwable { + setByte(m, null, value, expectFailure); + } + + static void getByte(MethodHandle m, ValueHolder v, byte value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final byte got; + if (v == null) { + got = (byte)m.invokeExact(); + } else { + got = (byte)m.invokeExact(v); + } + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void getByte(MethodHandle m, byte value, boolean expectFailure) throws Throwable { + getByte(m, null, value, expectFailure); + } + + static void setChar(MethodHandle m, ValueHolder v, char value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void setChar(MethodHandle m, char value, boolean expectFailure) throws Throwable { + setChar(m, null, value, expectFailure); + } + + static void getChar(MethodHandle m, ValueHolder v, char value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final char got; + if (v == null) { + got = (char)m.invokeExact(); + } else { + got = (char)m.invokeExact(v); + } + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void getChar(MethodHandle m, char value, boolean expectFailure) throws Throwable { + getChar(m, null, value, expectFailure); + } + + static void setShort(MethodHandle m, ValueHolder v, short value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void setShort(MethodHandle m, short value, boolean expectFailure) throws Throwable { + setShort(m, null, value, expectFailure); + } + + static void getShort(MethodHandle m, ValueHolder v, short value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final short got = (v == null) ? (short)m.invokeExact() : (short)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void getShort(MethodHandle m, short value, boolean expectFailure) throws Throwable { + getShort(m, null, value, expectFailure); + } + + static void setInt(MethodHandle m, ValueHolder v, int value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void setInt(MethodHandle m, int value, boolean expectFailure) throws Throwable { + setInt(m, null, value, expectFailure); + } + + static void getInt(MethodHandle m, ValueHolder v, int value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final int got = (v == null) ? (int)m.invokeExact() : (int)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void getInt(MethodHandle m, int value, boolean expectFailure) throws Throwable { + getInt(m, null, value, expectFailure); + } + + static void setLong(MethodHandle m, ValueHolder v, long value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void setLong(MethodHandle m, long value, boolean expectFailure) throws Throwable { + setLong(m, null, value, expectFailure); + } + + static void getLong(MethodHandle m, ValueHolder v, long value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final long got = (v == null) ? (long)m.invokeExact() : (long)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void getLong(MethodHandle m, long value, boolean expectFailure) throws Throwable { + getLong(m, null, value, expectFailure); + } + + static void setFloat(MethodHandle m, ValueHolder v, float value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void setFloat(MethodHandle m, float value, boolean expectFailure) throws Throwable { + setFloat(m, null, value, expectFailure); + } + + static void getFloat(MethodHandle m, ValueHolder v, float value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final float got = (v == null) ? (float)m.invokeExact() : (float)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void getFloat(MethodHandle m, float value, boolean expectFailure) throws Throwable { + getFloat(m, null, value, expectFailure); + } + + static void setDouble(MethodHandle m, ValueHolder v, double value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void setDouble(MethodHandle m, double value, boolean expectFailure) + throws Throwable { + setDouble(m, null, value, expectFailure); + } + + static void getDouble(MethodHandle m, ValueHolder v, double value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final double got = (v == null) ? (double)m.invokeExact() : (double)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void getDouble(MethodHandle m, double value, boolean expectFailure) + throws Throwable { + getDouble(m, null, value, expectFailure); + } + + static void setString(MethodHandle m, ValueHolder v, String value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void setString(MethodHandle m, String value, boolean expectFailure) + throws Throwable { + setString(m, null, value, expectFailure); + } + + static void getString(MethodHandle m, ValueHolder v, String value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final String got = (v == null) ? (String)m.invokeExact() : (String)m.invokeExact(v); + assertTrue(got.equals(value)); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void getString(MethodHandle m, String value, boolean expectFailure) + throws Throwable { + getString(m, null, value, expectFailure); + } + + static void setBoolean(MethodHandle m, ValueHolder v, boolean value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void setBoolean(MethodHandle m, boolean value, boolean expectFailure) + throws Throwable { + setBoolean(m, null, value, expectFailure); + } + + static void getBoolean(MethodHandle m, ValueHolder v, boolean value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final boolean got = + (v == null) ? (boolean)m.invokeExact() : (boolean)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertEquals(exceptionThrown, expectFailure); + } + + static void getBoolean(MethodHandle m, boolean value, boolean expectFailure) + throws Throwable { + getBoolean(m, null, value, expectFailure); + } + + static boolean resultFor(PrimitiveType actualType, PrimitiveType expectedType, + AccessorType actualAccessor, + AccessorType expectedAccessor) { + return (actualType != expectedType) || (actualAccessor != expectedAccessor); + } + + static void tryAccessor(MethodHandle methodHandle, + ValueHolder valueHolder, + PrimitiveType primitive, + Object value, + AccessorType accessor) throws Throwable { + boolean booleanValue = + value instanceof Boolean ? ((Boolean)value).booleanValue() : false; + setBoolean(methodHandle, valueHolder, booleanValue, + resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.IPUT)); + setBoolean(methodHandle, booleanValue, + resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.SPUT)); + getBoolean(methodHandle, valueHolder, booleanValue, + resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.IGET)); + getBoolean(methodHandle, booleanValue, + resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.SGET)); + + byte byteValue = value instanceof Byte ? ((Byte)value).byteValue() : (byte)0; + setByte(methodHandle, valueHolder, byteValue, + resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.IPUT)); + setByte(methodHandle, byteValue, + resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.SPUT)); + getByte(methodHandle, valueHolder, byteValue, + resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.IGET)); + getByte(methodHandle, byteValue, + resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.SGET)); + + char charValue = value instanceof Character ? ((Character)value).charValue() : 'z'; + setChar(methodHandle, valueHolder, charValue, + resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.IPUT)); + setChar(methodHandle, charValue, + resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.SPUT)); + getChar(methodHandle, valueHolder, charValue, + resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.IGET)); + getChar(methodHandle, charValue, + resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.SGET)); + + short shortValue = value instanceof Short ? ((Short)value).shortValue() : (short)0; + setShort(methodHandle, valueHolder, shortValue, + resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.IPUT)); + setShort(methodHandle, shortValue, + resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.SPUT)); + getShort(methodHandle, valueHolder, shortValue, + resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.IGET)); + getShort(methodHandle, shortValue, + resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.SGET)); + + int intValue = value instanceof Integer ? ((Integer)value).intValue() : -1; + setInt(methodHandle, valueHolder, intValue, + resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.IPUT)); + setInt(methodHandle, intValue, + resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.SPUT)); + getInt(methodHandle, valueHolder, intValue, + resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.IGET)); + getInt(methodHandle, intValue, + resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.SGET)); + + long longValue = value instanceof Long ? ((Long)value).longValue() : (long)-1; + setLong(methodHandle, valueHolder, longValue, + resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.IPUT)); + setLong(methodHandle, longValue, + resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.SPUT)); + getLong(methodHandle, valueHolder, longValue, + resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.IGET)); + getLong(methodHandle, longValue, + resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.SGET)); + + float floatValue = value instanceof Float ? ((Float)value).floatValue() : -1.0f; + setFloat(methodHandle, valueHolder, floatValue, + resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.IPUT)); + setFloat(methodHandle, floatValue, + resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.SPUT)); + getFloat(methodHandle, valueHolder, floatValue, + resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.IGET)); + getFloat(methodHandle, floatValue, + resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.SGET)); + + double doubleValue = value instanceof Double ? ((Double)value).doubleValue() : -1.0; + setDouble(methodHandle, valueHolder, doubleValue, + resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.IPUT)); + setDouble(methodHandle, doubleValue, + resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.SPUT)); + getDouble(methodHandle, valueHolder, doubleValue, + resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.IGET)); + getDouble(methodHandle, doubleValue, + resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.SGET)); + + String stringValue = value instanceof String ? ((String) value) : "No Spock, no"; + setString(methodHandle, valueHolder, stringValue, + resultFor(primitive, PrimitiveType.String, accessor, AccessorType.IPUT)); + setString(methodHandle, stringValue, + resultFor(primitive, PrimitiveType.String, accessor, AccessorType.SPUT)); + getString(methodHandle, valueHolder, stringValue, + resultFor(primitive, PrimitiveType.String, accessor, AccessorType.IGET)); + getString(methodHandle, stringValue, + resultFor(primitive, PrimitiveType.String, accessor, AccessorType.SGET)); + } + + public void testBooleanSettersAndGetters() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + boolean[] booleans = {false, true, false}; + for (boolean b : booleans) { + Boolean boxed = new Boolean(b); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_z", boolean.class), + valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_z", boolean.class), + valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_z == b); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_z", boolean.class), + valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_z", boolean.class), + valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_z == b); + } + } + + public void testByteSettersAndGetters() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + byte[] bytes = {(byte) 0x73, (byte) 0xfe}; + for (byte b : bytes) { + Byte boxed = new Byte(b); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_b", byte.class), + valueHolder, PrimitiveType.Byte, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_b", byte.class), + valueHolder, PrimitiveType.Byte, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_b == b); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_b", byte.class), + valueHolder, PrimitiveType.Byte, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_b", byte.class), + valueHolder, PrimitiveType.Byte, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_b == b); + } + } + + public void testCharSettersAndGetters() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + char[] chars = {'a', 'b', 'c'}; + for (char c : chars) { + Character boxed = new Character(c); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_c", char.class), + valueHolder, PrimitiveType.Char, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_c", char.class), + valueHolder, PrimitiveType.Char, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_c == c); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_c", char.class), + valueHolder, PrimitiveType.Char, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_c", char.class), + valueHolder, PrimitiveType.Char, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_c == c); + } + } + + public void testShortSettersAndGetters() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + short[] shorts = {(short) 0x1234, (short) 0x4321}; + for (short s : shorts) { + Short boxed = new Short(s); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_s", short.class), + valueHolder, PrimitiveType.Short, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_s", short.class), + valueHolder, PrimitiveType.Short, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_s == s); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_s", short.class), + valueHolder, PrimitiveType.Short, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_s", short.class), + valueHolder, PrimitiveType.Short, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_s == s); + } + } + + public void testIntSettersAndGetters() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + int[] ints = {-100000000, 10000000}; + for (int i : ints) { + Integer boxed = new Integer(i); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_i", int.class), + valueHolder, PrimitiveType.Int, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_i", int.class), + valueHolder, PrimitiveType.Int, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_i == i); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_i", int.class), + valueHolder, PrimitiveType.Int, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_i", int.class), + valueHolder, PrimitiveType.Int, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_i == i); + } + } + + public void testFloatSettersAndGetters() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + float[] floats = {0.99f, -1.23e-17f}; + for (float f : floats) { + Float boxed = new Float(f); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_f", float.class), + valueHolder, PrimitiveType.Float, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_f", float.class), + valueHolder, PrimitiveType.Float, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_f == f); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_f", float.class), + valueHolder, PrimitiveType.Float, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_f", float.class), + valueHolder, PrimitiveType.Float, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_f == f); + } + } + + public void testDoubleSettersAndGetters() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + double[] doubles = {0.44444444444e37, -0.555555555e-37}; + for (double d : doubles) { + Double boxed = new Double(d); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_d", double.class), + valueHolder, PrimitiveType.Double, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_d", double.class), + valueHolder, PrimitiveType.Double, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_d == d); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_d", double.class), + valueHolder, PrimitiveType.Double, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_d", double.class), + valueHolder, PrimitiveType.Double, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_d == d); + } + } + + public void testLongSettersAndGetters() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + long[] longs = {0x0123456789abcdefl, 0xfedcba9876543210l}; + for (long j : longs) { + Long boxed = new Long(j); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_j", long.class), + valueHolder, PrimitiveType.Long, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_j", long.class), + valueHolder, PrimitiveType.Long, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_j == j); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_j", long.class), + valueHolder, PrimitiveType.Long, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_j", long.class), + valueHolder, PrimitiveType.Long, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_j == j); + } + } + + public void testStringSettersAndGetters() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + String [] strings = { "octopus", "crab" }; + for (String s : strings) { + tryAccessor(lookup.findSetter(ValueHolder.class, "m_l", String.class), + valueHolder, PrimitiveType.String, s, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_l", String.class), + valueHolder, PrimitiveType.String, s, AccessorType.IGET); + assertTrue(s.equals(valueHolder.m_l)); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_l", String.class), + valueHolder, PrimitiveType.String, s, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_l", String.class), + valueHolder, PrimitiveType.String, s, AccessorType.SGET); + assertTrue(s.equals(ValueHolder.s_l)); + } + } + + public void testLookup() throws Throwable { + // NB having a static field test here is essential for + // this test. MethodHandles need to ensure the class + // (ValueHolder) is initialized. This happens in the + // invoke-polymorphic dispatch. + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + MethodHandle mh = lookup.findStaticGetter(ValueHolder.class, "s_fi", int.class); + int initialValue = (int)mh.invokeExact(); + System.out.println(initialValue); + } catch (NoSuchFieldException e) { fail(); } + try { + MethodHandle mh = lookup.findStaticSetter(ValueHolder.class, "s_i", int.class); + mh.invokeExact(0); + } catch (NoSuchFieldException e) { fail(); } + try { + lookup.findStaticGetter(ValueHolder.class, "s_fi", byte.class); + fail(); + } catch (NoSuchFieldException e) {} + try { + lookup.findGetter(ValueHolder.class, "s_fi", byte.class); + fail(); + } catch (NoSuchFieldException e) {} + try { + lookup.findStaticSetter(ValueHolder.class, "s_fi", int.class); + fail(); + } catch (IllegalAccessException e) {} + + lookup.findGetter(ValueHolder.class, "m_fi", int.class); + try { + lookup.findGetter(ValueHolder.class, "m_fi", byte.class); + fail(); + } catch (NoSuchFieldException e) {} + try { + lookup.findStaticGetter(ValueHolder.class, "m_fi", byte.class); + fail(); + } catch (NoSuchFieldException e) {} + try { + lookup.findSetter(ValueHolder.class, "m_fi", int.class); + fail(); + } catch (IllegalAccessException e) {} + } + + public void testStaticGetter() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle h0 = lookup.findStaticGetter(ValueHolder.class, "s_fi", int.class); + h0.invoke(); + Number t = (Number)h0.invoke(); + int u = (int)h0.invoke(); + Integer v = (Integer)h0.invoke(); + long w = (long)h0.invoke(); + try { + byte x = (byte)h0.invoke(); + fail(); + } catch (WrongMethodTypeException e) {} + try { + String y = (String)h0.invoke(); + fail(); + } catch (WrongMethodTypeException e) {} + try { + Long z = (Long)h0.invoke(); + fail(); + } catch (WrongMethodTypeException e) {} + } + + public void testMemberGetter() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle h0 = lookup.findGetter(ValueHolder.class, "m_fi", int.class); + h0.invoke(valueHolder); + Number t = (Number)h0.invoke(valueHolder); + int u = (int)h0.invoke(valueHolder); + Integer v = (Integer)h0.invoke(valueHolder); + long w = (long)h0.invoke(valueHolder); + try { + byte x = (byte)h0.invoke(valueHolder); + fail(); + } catch (WrongMethodTypeException e) {} + try { + String y = (String)h0.invoke(valueHolder); + fail(); + } catch (WrongMethodTypeException e) {} + try { + Long z = (Long)h0.invoke(valueHolder); + fail(); + } catch (WrongMethodTypeException e) {} + } + + /*package*/ static Number getDoubleAsNumber() { + return new Double(1.4e77); + } + /*package*/ static Number getFloatAsNumber() { + return new Float(7.77); + } + /*package*/ static Object getFloatAsObject() { + return new Float(-7.77); + } + + public void testMemberSetter() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle h0 = lookup.findSetter(ValueHolder.class, "m_f", float.class); + h0.invoke(valueHolder, 0.22f); + h0.invoke(valueHolder, new Float(1.11f)); + Number floatNumber = getFloatAsNumber(); + h0.invoke(valueHolder, floatNumber); + assertTrue(valueHolder.m_f == floatNumber.floatValue()); + Object objNumber = getFloatAsObject(); + h0.invoke(valueHolder, objNumber); + assertTrue(valueHolder.m_f == ((Float) objNumber).floatValue()); + try { + h0.invoke(valueHolder, (Float)null); + fail(); + } catch (NullPointerException e) {} + + h0.invoke(valueHolder, (byte)1); + h0.invoke(valueHolder, (short)2); + h0.invoke(valueHolder, 3); + h0.invoke(valueHolder, 4l); + + assertTrue(null == (Object) h0.invoke(valueHolder, 33)); + assertTrue(0.0f == (float) h0.invoke(valueHolder, 33)); + assertTrue(0l == (long) h0.invoke(valueHolder, 33)); + + try { + h0.invoke(valueHolder, 0.33); + fail(); + } catch (WrongMethodTypeException e) {} + try { + Number doubleNumber = getDoubleAsNumber(); + h0.invoke(valueHolder, doubleNumber); + fail(); + } catch (ClassCastException e) {} + try { + Number doubleNumber = null; + h0.invoke(valueHolder, doubleNumber); + fail(); + } catch (NullPointerException e) {} + try { + // Mismatched return type - float != void + float tmp = (float)h0.invoke(valueHolder, 0.45f); + assertTrue(tmp == 0.0); + } catch (Exception e) { fail(); } + try { + h0.invoke(valueHolder, "bam"); + fail(); + } catch (WrongMethodTypeException e) {} + try { + String s = null; + h0.invoke(valueHolder, s); + fail(); + } catch (WrongMethodTypeException e) {} + } + + public void testStaticSetter() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle h0 = lookup.findStaticSetter(ValueHolder.class, "s_f", float.class); + h0.invoke(0.22f); + h0.invoke(new Float(1.11f)); + Number floatNumber = new Float(0.88f); + h0.invoke(floatNumber); + assertTrue(ValueHolder.s_f == floatNumber.floatValue()); + + try { + h0.invoke((Float)null); + fail(); + } catch (NullPointerException e) {} + + h0.invoke((byte)1); + h0.invoke((short)2); + h0.invoke(3); + h0.invoke(4l); + + assertTrue(null == (Object) h0.invoke(33)); + assertTrue(0.0f == (float) h0.invoke(33)); + assertTrue(0l == (long) h0.invoke(33)); + + try { + h0.invoke(0.33); + fail(); + } catch (WrongMethodTypeException e) {} + try { + Number doubleNumber = getDoubleAsNumber(); + h0.invoke(doubleNumber); + fail(); + } catch (ClassCastException e) {} + try { + Number doubleNumber = new Double(1.01); + doubleNumber = (doubleNumber.doubleValue() != 0.1) ? null : doubleNumber; + h0.invoke(doubleNumber); + fail(); + } catch (NullPointerException e) {} + try { + // Mismatched return type - float != void + float tmp = (float)h0.invoke(0.45f); + assertTrue(tmp == 0.0); + } catch (Exception e) { fail(); } + try { + h0.invoke("bam"); + fail(); + } catch (WrongMethodTypeException e) {} + try { + String s = null; + h0.invoke(s); + fail(); + } catch (WrongMethodTypeException e) {} + } +} diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java new file mode 100644 index 000000000..9bf02ef56 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java @@ -0,0 +1,1927 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.lang.invoke; + +import java.lang.Thread; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; +import java.util.ArrayList; +import java.util.Arrays; + +import junit.framework.TestCase; + +public class MethodHandleCombinersTest extends TestCase { + + static final int TEST_THREAD_ITERATIONS = 1000; + + public static void testThrowException() throws Throwable { + MethodHandle handle = MethodHandles.throwException(String.class, + IllegalArgumentException.class); + + if (handle.type().returnType() != String.class) { + fail("Unexpected return type for handle: " + handle + + " [ " + handle.type() + "]"); + } + + final IllegalArgumentException iae = new IllegalArgumentException("boo!"); + try { + handle.invoke(iae); + fail("Expected an exception of type: java.lang.IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + if (expected != iae) { + fail("Wrong exception: expected " + iae + " but was " + expected); + } + } + } + + public static void dropArguments_delegate(String message, long message2) { + assertEquals("foo", message); + assertEquals(42l, message2); + } + + public static void testDropArguments() throws Throwable { + MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "dropArguments_delegate", + MethodType.methodType(void.class, new Class[]{String.class, long.class})); + + MethodHandle transform = MethodHandles.dropArguments( + delegate, 0, int.class, Object.class); + + // The transformer will accept two additional arguments at position zero. + try { + transform.invokeExact("foo", 42l); + fail(); + } catch (WrongMethodTypeException expected) { + } + + transform.invokeExact(45, new Object(), "foo", 42l); + transform.invoke(45, new Object(), "foo", 42l); + + // Additional arguments at position 1. + transform = MethodHandles.dropArguments(delegate, 1, int.class, Object.class); + transform.invokeExact("foo", 45, new Object(), 42l); + transform.invoke("foo", 45, new Object(), 42l); + + // Additional arguments at position 2. + transform = MethodHandles.dropArguments(delegate, 2, int.class, Object.class); + transform.invokeExact("foo", 42l, 45, new Object()); + transform.invoke("foo", 42l, 45, new Object()); + + // Note that we still perform argument conversions even for the arguments that + // are subsequently dropped. + try { + transform.invoke("foo", 42l, 45l, new Object()); + fail(); + } catch (WrongMethodTypeException expected) { + } catch (IllegalArgumentException expected) { + // TODO(narayan): We currently throw the wrong type of exception here, + // it's IAE and should be WMTE instead. + } + + // Check that asType works as expected. + transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class); + transform = transform.asType(MethodType.methodType(void.class, + new Class[]{short.class, Object.class, String.class, long.class})); + transform.invokeExact((short) 45, new Object(), "foo", 42l); + + // Invalid argument location, should not be allowed. + try { + MethodHandles.dropArguments(delegate, -1, int.class, Object.class); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Invalid argument location, should not be allowed. + try { + MethodHandles.dropArguments(delegate, 3, int.class, Object.class); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + MethodHandles.dropArguments(delegate, 1, void.class); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public static void testDropArguments_List() throws Throwable { + MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "dropArguments_delegate", + MethodType.methodType(void.class, new Class[]{String.class, long.class})); + + MethodHandle transform = MethodHandles.dropArguments( + delegate, 0, Arrays.asList(int.class, Object.class)); + + transform.invokeExact(45, new Object(), "foo", 42l); + transform.invoke(45, new Object(), "foo", 42l); + + // Check that asType works as expected. + transform = transform.asType(MethodType.methodType(void.class, + new Class[]{short.class, Object.class, String.class, long.class})); + transform.invokeExact((short) 45, new Object(), "foo", 42l); + } + + public static String testCatchException_target(String arg1, long arg2, String exceptionMessage) + throws Throwable { + if (exceptionMessage != null) { + throw new IllegalArgumentException(exceptionMessage); + } + + assertEquals(null, exceptionMessage); + assertEquals(42l, arg2); + return "target"; + } + + public static String testCatchException_handler(IllegalArgumentException iae, String arg1, + long arg2, + String exMsg) { + // Check that the thrown exception has the right message. + assertEquals("exceptionMessage", iae.getMessage()); + // Check the other arguments. + assertEquals("foo", arg1); + assertEquals(42, arg2); + assertEquals("exceptionMessage", exMsg); + + return "handler1"; + } + + public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) { + assertEquals("exceptionMessage", iae.getMessage()); + assertEquals("foo", arg1); + + return "handler2"; + } + + public static void testCatchException() throws Throwable { + MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "testCatchException_target", + MethodType + .methodType(String.class, new Class[]{String.class, long.class, String.class})); + + MethodHandle handler = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "testCatchException_handler", + MethodType.methodType(String.class, new Class[]{IllegalArgumentException.class, + String.class, long.class, String.class})); + + MethodHandle adapter = MethodHandles.catchException(target, IllegalArgumentException.class, + handler); + + String returnVal = null; + + // These two should end up calling the target always. We're passing a null exception + // message here, which means the target will not throw. + returnVal = (String) adapter.invoke("foo", 42, null); + assertEquals("target", returnVal); + returnVal = (String) adapter.invokeExact("foo", 42l, (String) null); + assertEquals("target", returnVal); + + // We're passing a non-null exception message here, which means the target will throw, + // which in turn means that the handler must be called for the next two invokes. + returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); + assertEquals("handler1", returnVal); + returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage"); + assertEquals("handler1", returnVal); + + handler = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "testCatchException_handler2", + MethodType.methodType(String.class, new Class[]{IllegalArgumentException.class, + String.class})); + adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler); + + returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); + assertEquals("handler2", returnVal); + returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage"); + assertEquals("handler2", returnVal); + + // Test that the type of the invoke doesn't matter. Here we call + // IllegalArgumentException.toString() on the exception that was thrown by + // the target. + handler = MethodHandles.lookup().findVirtual(IllegalArgumentException.class, + "toString", MethodType.methodType(String.class)); + adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler); + + returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); + assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal); + returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage"); + assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal); + + // Check that asType works as expected. + adapter = MethodHandles.catchException(target, IllegalArgumentException.class, + handler); + adapter = adapter.asType(MethodType.methodType(String.class, + new Class[]{String.class, int.class, String.class})); + returnVal = (String) adapter.invokeExact("foo", 42, "exceptionMessage"); + assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal); + } + + public static boolean testGuardWithTest_test(String arg1, long arg2) { + return "target".equals(arg1) && 42 == arg2; + } + + public static String testGuardWithTest_target(String arg1, long arg2, int arg3) { + // Make sure that the test passed. + assertTrue(testGuardWithTest_test(arg1, arg2)); + // Make sure remaining arguments were passed through unmodified. + assertEquals(56, arg3); + + return "target"; + } + + public static String testGuardWithTest_fallback(String arg1, long arg2, int arg3) { + // Make sure that the test failed. + assertTrue(!testGuardWithTest_test(arg1, arg2)); + // Make sure remaining arguments were passed through unmodified. + assertEquals(56, arg3); + + return "fallback"; + } + + public static void testGuardWithTest() throws Throwable { + MethodHandle test = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "testGuardWithTest_test", + MethodType.methodType(boolean.class, new Class[]{String.class, long.class})); + + final MethodType type = MethodType.methodType(String.class, + new Class[]{String.class, long.class, int.class}); + + final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "testGuardWithTest_target", type); + final MethodHandle fallback = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "testGuardWithTest_fallback", type); + + MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback); + + String returnVal = null; + + returnVal = (String) adapter.invoke("target", 42, 56); + assertEquals("target", returnVal); + returnVal = (String) adapter.invokeExact("target", 42l, 56); + assertEquals("target", returnVal); + + returnVal = (String) adapter.invoke("fallback", 42l, 56); + assertEquals("fallback", returnVal); + returnVal = (String) adapter.invoke("target", 46l, 56); + assertEquals("fallback", returnVal); + returnVal = (String) adapter.invokeExact("target", 42l, 56); + assertEquals("target", returnVal); + + // Check that asType works as expected. + adapter = adapter.asType(MethodType.methodType(String.class, + new Class[]{String.class, int.class, int.class})); + returnVal = (String) adapter.invokeExact("target", 42, 56); + assertEquals("target", returnVal); + } + + public static void testArrayElementGetter() throws Throwable { + MethodHandle getter = MethodHandles.arrayElementGetter(int[].class); + + { + int[] array = new int[1]; + array[0] = 42; + int value = (int) getter.invoke(array, 0); + assertEquals(42, value); + + try { + value = (int) getter.invoke(array, -1); + fail(); + } catch (ArrayIndexOutOfBoundsException expected) { + } + + try { + value = (int) getter.invoke(null, -1); + fail(); + } catch (NullPointerException expected) { + } + } + + { + getter = MethodHandles.arrayElementGetter(long[].class); + long[] array = new long[1]; + array[0] = 42; + long value = (long) getter.invoke(array, 0); + assertEquals(42l, value); + } + + { + getter = MethodHandles.arrayElementGetter(short[].class); + short[] array = new short[1]; + array[0] = 42; + short value = (short) getter.invoke(array, 0); + assertEquals((short) 42, value); + } + + { + getter = MethodHandles.arrayElementGetter(char[].class); + char[] array = new char[1]; + array[0] = 42; + char value = (char) getter.invoke(array, 0); + assertEquals((char) 42, value); + } + + { + getter = MethodHandles.arrayElementGetter(byte[].class); + byte[] array = new byte[1]; + array[0] = (byte) 0x8; + byte value = (byte) getter.invoke(array, 0); + assertEquals((byte) 0x8, value); + } + + { + getter = MethodHandles.arrayElementGetter(boolean[].class); + boolean[] array = new boolean[1]; + array[0] = true; + boolean value = (boolean) getter.invoke(array, 0); + assertTrue(value); + } + + { + getter = MethodHandles.arrayElementGetter(float[].class); + float[] array = new float[1]; + array[0] = 42.0f; + float value = (float) getter.invoke(array, 0); + assertEquals(42.0f, value); + } + + { + getter = MethodHandles.arrayElementGetter(double[].class); + double[] array = new double[1]; + array[0] = 42.0; + double value = (double) getter.invoke(array, 0); + assertEquals(42.0, value); + } + + { + getter = MethodHandles.arrayElementGetter(String[].class); + String[] array = new String[3]; + array[0] = "42"; + array[1] = "48"; + array[2] = "54"; + String value = (String) getter.invoke(array, 0); + assertEquals("42", value); + value = (String) getter.invoke(array, 1); + assertEquals("48", value); + value = (String) getter.invoke(array, 2); + assertEquals("54", value); + } + } + + public static void testArrayElementSetter() throws Throwable { + MethodHandle setter = MethodHandles.arrayElementSetter(int[].class); + + { + int[] array = new int[2]; + setter.invoke(array, 0, 42); + setter.invoke(array, 1, 43); + + assertEquals(42, array[0]); + assertEquals(43, array[1]); + + try { + setter.invoke(array, -1, 42); + fail(); + } catch (ArrayIndexOutOfBoundsException expected) { + } + + try { + setter.invoke(null, 0, 42); + fail(); + } catch (NullPointerException expected) { + } + } + + { + setter = MethodHandles.arrayElementSetter(long[].class); + long[] array = new long[1]; + setter.invoke(array, 0, 42l); + assertEquals(42l, array[0]); + } + + { + setter = MethodHandles.arrayElementSetter(short[].class); + short[] array = new short[1]; + setter.invoke(array, 0, (short) 42); + assertEquals((short) 42, array[0]); + } + + { + setter = MethodHandles.arrayElementSetter(char[].class); + char[] array = new char[1]; + setter.invoke(array, 0, (char) 42); + assertEquals((char) 42, array[0]); + } + + { + setter = MethodHandles.arrayElementSetter(byte[].class); + byte[] array = new byte[1]; + setter.invoke(array, 0, (byte) 0x8); + assertEquals((byte) 0x8, array[0]); + } + + { + setter = MethodHandles.arrayElementSetter(boolean[].class); + boolean[] array = new boolean[1]; + setter.invoke(array, 0, true); + assertTrue(array[0]); + } + + { + setter = MethodHandles.arrayElementSetter(float[].class); + float[] array = new float[1]; + setter.invoke(array, 0, 42.0f); + assertEquals(42.0f, array[0]); + } + + { + setter = MethodHandles.arrayElementSetter(double[].class); + double[] array = new double[1]; + setter.invoke(array, 0, 42.0); + assertEquals(42.0, array[0]); + } + + { + setter = MethodHandles.arrayElementSetter(String[].class); + String[] array = new String[3]; + setter.invoke(array, 0, "42"); + setter.invoke(array, 1, "48"); + setter.invoke(array, 2, "54"); + assertEquals("42", array[0]); + assertEquals("48", array[1]); + assertEquals("54", array[2]); + } + } + + public static void testIdentity() throws Throwable { + { + MethodHandle identity = MethodHandles.identity(boolean.class); + boolean value = (boolean) identity.invoke(false); + assertFalse(value); + } + + { + MethodHandle identity = MethodHandles.identity(byte.class); + byte value = (byte) identity.invoke((byte) 0x8); + assertEquals((byte) 0x8, value); + } + + { + MethodHandle identity = MethodHandles.identity(char.class); + char value = (char) identity.invoke((char) -56); + assertEquals((char) -56, value); + } + + { + MethodHandle identity = MethodHandles.identity(short.class); + short value = (short) identity.invoke((short) -59); + assertEquals((short) -59, value); + } + + { + MethodHandle identity = MethodHandles.identity(int.class); + int value = (int) identity.invoke(52); + assertEquals((int) 52, value); + } + + { + MethodHandle identity = MethodHandles.identity(long.class); + long value = (long) identity.invoke(-76l); + assertEquals(-76l, value); + } + + { + MethodHandle identity = MethodHandles.identity(float.class); + float value = (float) identity.invoke(56.0f); + assertEquals(56.0f, value); + } + + { + MethodHandle identity = MethodHandles.identity(double.class); + double value = (double) identity.invoke((double) 72.0); + assertEquals(72.0, value); + } + + { + MethodHandle identity = MethodHandles.identity(String.class); + String value = (String) identity.invoke("bazman"); + assertEquals("bazman", value); + } + } + + public static void testConstant() throws Throwable { + // int constants. + { + MethodHandle constant = MethodHandles.constant(int.class, 56); + assertEquals(56, (int) constant.invoke()); + + // short constant values are converted to int. + constant = MethodHandles.constant(int.class, (short) 52); + assertEquals(52, (int) constant.invoke()); + + // char constant values are converted to int. + constant = MethodHandles.constant(int.class, (char) 'b'); + assertEquals('b', (int) constant.invoke()); + + // int constant values are converted to int. + constant = MethodHandles.constant(int.class, (byte) 0x1); + assertEquals(0x1, (int) constant.invoke()); + + // boolean, float, double and long primitive constants are not convertible + // to int, so the handle creation must fail with a CCE. + try { + MethodHandles.constant(int.class, false); + fail(); + } catch (ClassCastException expected) { + } + + try { + MethodHandles.constant(int.class, 0.1f); + fail(); + } catch (ClassCastException expected) { + } + + try { + MethodHandles.constant(int.class, 0.2); + fail(); + } catch (ClassCastException expected) { + } + + try { + MethodHandles.constant(int.class, 73l); + fail(); + } catch (ClassCastException expected) { + } + } + + // long constants. + { + MethodHandle constant = MethodHandles.constant(long.class, 56l); + assertEquals(56l, (long) constant.invoke()); + + constant = MethodHandles.constant(long.class, (int) 56); + assertEquals(56l, (long) constant.invoke()); + } + + // byte constants. + { + MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12); + assertEquals((byte) 0x12, (byte) constant.invoke()); + } + + // boolean constants. + { + MethodHandle constant = MethodHandles.constant(boolean.class, true); + assertTrue((boolean) constant.invoke()); + } + + // char constants. + { + MethodHandle constant = MethodHandles.constant(char.class, 'f'); + assertEquals('f', (char) constant.invoke()); + } + + // short constants. + { + MethodHandle constant = MethodHandles.constant(short.class, (short) 123); + assertEquals((short) 123, (short) constant.invoke()); + } + + // float constants. + { + MethodHandle constant = MethodHandles.constant(float.class, 56.0f); + assertEquals(56.0f, (float) constant.invoke()); + } + + // double constants. + { + MethodHandle constant = MethodHandles.constant(double.class, 256.0); + assertEquals(256.0, (double) constant.invoke()); + } + + // reference constants. + { + MethodHandle constant = MethodHandles.constant(String.class, "256.0"); + assertEquals("256.0", (String) constant.invoke()); + } + } + + public static void testBindTo() throws Throwable { + MethodHandle stringCharAt = MethodHandles.lookup().findVirtual( + String.class, "charAt", MethodType.methodType(char.class, int.class)); + + char value = (char) stringCharAt.invoke("foo", 0); + if (value != 'f') { + fail("Unexpected value: " + value); + } + + MethodHandle bound = stringCharAt.bindTo("foo"); + value = (char) bound.invoke(0); + if (value != 'f') { + fail("Unexpected value: " + value); + } + + try { + stringCharAt.bindTo(new Object()); + fail(); + } catch (ClassCastException expected) { + } + + bound = stringCharAt.bindTo(null); + try { + bound.invoke(0); + fail(); + } catch (NullPointerException expected) { + } + + MethodHandle integerParseInt = MethodHandles.lookup().findStatic( + Integer.class, "parseInt", MethodType.methodType(int.class, String.class)); + + bound = integerParseInt.bindTo("78452"); + int intValue = (int) bound.invoke(); + if (intValue != 78452) { + fail("Unexpected value: " + intValue); + } + } + + public static String filterReturnValue_target(int a) { + return "ReturnValue" + a; + } + + public static boolean filterReturnValue_filter(String value) { + return value.indexOf("42") != -1; + } + + public static int filterReturnValue_intTarget(String a) { + return Integer.parseInt(a); + } + + public static int filterReturnValue_intFilter(int b) { + return b + 1; + } + + public static void filterReturnValue_voidTarget() { + } + + public static int filterReturnValue_voidFilter() { + return 42; + } + + public static void testFilterReturnValue() throws Throwable { + // A target that returns a reference. + { + final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "filterReturnValue_target", MethodType.methodType(String.class, int.class)); + final MethodHandle filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "filterReturnValue_filter", MethodType.methodType(boolean.class, String.class)); + + MethodHandle adapter = MethodHandles.filterReturnValue(target, filter); + + boolean value = (boolean) adapter.invoke((int) 42); + if (!value) { + fail("Unexpected value: " + value); + } + value = (boolean) adapter.invoke((int) 43); + if (value) { + fail("Unexpected value: " + value); + } + } + + // A target that returns a primitive. + { + final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "filterReturnValue_intTarget", MethodType.methodType(int.class, String.class)); + final MethodHandle filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "filterReturnValue_intFilter", MethodType.methodType(int.class, int.class)); + + MethodHandle adapter = MethodHandles.filterReturnValue(target, filter); + + int value = (int) adapter.invoke("56"); + if (value != 57) { + fail("Unexpected value: " + value); + } + } + + // A target that returns void. + { + final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "filterReturnValue_voidTarget", MethodType.methodType(void.class)); + final MethodHandle filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "filterReturnValue_voidFilter", MethodType.methodType(int.class)); + + MethodHandle adapter = MethodHandles.filterReturnValue(target, filter); + + int value = (int) adapter.invoke(); + if (value != 42) { + fail("Unexpected value: " + value); + } + } + } + + public static void permuteArguments_callee(boolean a, byte b, char c, + short d, int e, long f, float g, double h) { + assertTrue(a); + assertEquals((byte) 'b', b); + assertEquals('c', c); + assertEquals((short) 56, d); + assertEquals(78, e); + assertEquals(97l, f); + assertEquals(98.0f, g); + assertEquals(97.0, h); + } + + public static void permuteArguments_boxingCallee(boolean a, Integer b) { + assertTrue(a); + assertEquals(Integer.valueOf(42), b); + } + + public static void testPermuteArguments() throws Throwable { + { + final MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "permuteArguments_callee", + MethodType.methodType(void.class, new Class[]{ + boolean.class, byte.class, char.class, short.class, int.class, + long.class, float.class, double.class})); + + final MethodType newType = MethodType.methodType(void.class, new Class[]{ + double.class, float.class, long.class, int.class, short.class, char.class, + byte.class, boolean.class}); + + final MethodHandle permutation = MethodHandles.permuteArguments( + target, newType, new int[]{7, 6, 5, 4, 3, 2, 1, 0}); + + permutation.invoke((double) 97.0, (float) 98.0f, (long) 97, 78, + (short) 56, 'c', (byte) 'b', (boolean) true); + + // The permutation array was not of the right length. + try { + MethodHandles.permuteArguments(target, newType, + new int[]{7}); + fail(); + } catch (IllegalArgumentException expected) { + } + + // The permutation array has an element that's out of bounds + // (there's no argument with idx == 8). + try { + MethodHandles.permuteArguments(target, newType, + new int[]{8, 6, 5, 4, 3, 2, 1, 0}); + fail(); + } catch (IllegalArgumentException expected) { + } + + // The permutation array maps to an incorrect type. + try { + MethodHandles.permuteArguments(target, newType, + new int[]{7, 7, 5, 4, 3, 2, 1, 0}); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + // Tests for reference arguments as well as permutations that + // repeat arguments. + { + final MethodHandle target = MethodHandles.lookup().findVirtual( + String.class, "concat", MethodType.methodType(String.class, String.class)); + + final MethodType newType = MethodType.methodType(String.class, String.class, + String.class); + + assertEquals("foobar", (String) target.invoke("foo", "bar")); + + MethodHandle permutation = MethodHandles.permuteArguments(target, + newType, new int[]{1, 0}); + assertEquals("barfoo", (String) permutation.invoke("foo", "bar")); + + permutation = MethodHandles.permuteArguments(target, newType, new int[]{0, 0}); + assertEquals("foofoo", (String) permutation.invoke("foo", "bar")); + + permutation = MethodHandles.permuteArguments(target, newType, new int[]{1, 1}); + assertEquals("barbar", (String) permutation.invoke("foo", "bar")); + } + + // Tests for boxing and unboxing. + { + final MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "permuteArguments_boxingCallee", + MethodType.methodType(void.class, new Class[]{boolean.class, Integer.class})); + + final MethodType newType = MethodType.methodType(void.class, + new Class[]{Integer.class, boolean.class}); + + MethodHandle permutation = MethodHandles.permuteArguments(target, + newType, new int[]{1, 0}); + + permutation.invoke(42, true); + permutation.invoke(42, Boolean.TRUE); + permutation.invoke(Integer.valueOf(42), true); + permutation.invoke(Integer.valueOf(42), Boolean.TRUE); + } + } + + private static Object returnBar() { + return "bar"; + } + + public static void testInvokers() throws Throwable { + final MethodType targetType = MethodType.methodType(String.class, String.class); + final MethodHandle target = MethodHandles.lookup().findVirtual( + String.class, "concat", targetType); + + MethodHandle invoker = MethodHandles.invoker(target.type()); + assertEquals("barbar", (String) invoker.invoke(target, "bar", "bar")); + assertEquals("barbar", (String) invoker.invoke(target, (Object) returnBar(), "bar")); + try { + String foo = (String) invoker.invoke(target, "bar", "bar", 24); + fail(); + } catch (WrongMethodTypeException expected) { + } + + MethodHandle exactInvoker = MethodHandles.exactInvoker(target.type()); + assertEquals("barbar", (String) exactInvoker.invoke(target, "bar", "bar")); + try { + String foo = (String) exactInvoker.invoke(target, (Object) returnBar(), "bar"); + fail(); + } catch (WrongMethodTypeException expected) { + } + try { + String foo = (String) exactInvoker.invoke(target, "bar", "bar", 24); + fail(); + } catch (WrongMethodTypeException expected) { + } + } + + public static int spreadReferences(String a, String b, String c) { + assertEquals("a", a); + assertEquals("b", b); + assertEquals("c", c); + return 42; + } + + public static int spreadReferences_Unbox(String a, int b) { + assertEquals("a", a); + assertEquals(43, b); + return 43; + } + + public static void testSpreaders_reference() throws Throwable { + MethodType methodType = MethodType.methodType(int.class, + new Class[]{String.class, String.class, String.class}); + MethodHandle delegate = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "spreadReferences", methodType); + + // Basic checks on array lengths. + // + // Array size = 0 + MethodHandle mhAsSpreader = delegate.asSpreader(String[].class, 0); + int ret = (int) mhAsSpreader.invoke("a", "b", "c", new String[]{}); + assertEquals(42, ret); + // Array size = 1 + mhAsSpreader = delegate.asSpreader(String[].class, 1); + ret = (int) mhAsSpreader.invoke("a", "b", new String[]{"c"}); + assertEquals(42, ret); + // Array size = 2 + mhAsSpreader = delegate.asSpreader(String[].class, 2); + ret = (int) mhAsSpreader.invoke("a", new String[]{"b", "c"}); + assertEquals(42, ret); + // Array size = 3 + mhAsSpreader = delegate.asSpreader(String[].class, 3); + ret = (int) mhAsSpreader.invoke(new String[]{"a", "b", "c"}); + assertEquals(42, ret); + + // Exception case, array size = 4 is illegal. + try { + delegate.asSpreader(String[].class, 4); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Exception case, calling with an arg of the wrong size. + // Array size = 3 + mhAsSpreader = delegate.asSpreader(String[].class, 3); + try { + ret = (int) mhAsSpreader.invoke(new String[]{"a", "b"}); + } catch (IllegalArgumentException expected) { + } + + // Various other hijinks, pass as Object[] arrays, Object etc. + mhAsSpreader = delegate.asSpreader(Object[].class, 2); + ret = (int) mhAsSpreader.invoke("a", new String[]{"b", "c"}); + assertEquals(42, ret); + + mhAsSpreader = delegate.asSpreader(Object[].class, 2); + ret = (int) mhAsSpreader.invoke("a", new Object[]{"b", "c"}); + assertEquals(42, ret); + + mhAsSpreader = delegate.asSpreader(Object[].class, 2); + ret = (int) mhAsSpreader.invoke("a", (Object) new Object[]{"b", "c"}); + assertEquals(42, ret); + + // Test implicit unboxing. + MethodType methodType2 = MethodType.methodType(int.class, + new Class[]{String.class, int.class}); + MethodHandle delegate2 = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "spreadReferences_Unbox", methodType2); + + // .. with an Integer[] array. + mhAsSpreader = delegate2.asSpreader(Integer[].class, 1); + ret = (int) mhAsSpreader.invoke("a", new Integer[]{43}); + assertEquals(43, ret); + + // .. with an Integer[] array declared as an Object[] argument type. + mhAsSpreader = delegate2.asSpreader(Object[].class, 1); + ret = (int) mhAsSpreader.invoke("a", new Integer[]{43}); + assertEquals(43, ret); + + // .. with an Object[] array. + mhAsSpreader = delegate2.asSpreader(Object[].class, 1); + ret = (int) mhAsSpreader.invoke("a", new Object[]{Integer.valueOf(43)}); + assertEquals(43, ret); + + // -- Part 2-- + // Run a subset of these tests on MethodHandles.spreadInvoker, which only accepts + // a trailing argument type of Object[]. + MethodHandle spreadInvoker = MethodHandles.spreadInvoker(methodType2, 1); + ret = (int) spreadInvoker.invoke(delegate2, "a", new Object[]{Integer.valueOf(43)}); + assertEquals(43, ret); + + ret = (int) spreadInvoker.invoke(delegate2, "a", new Integer[]{43}); + assertEquals(43, ret); + + // NOTE: Annoyingly, the second argument here is leadingArgCount and not + // arrayLength. + spreadInvoker = MethodHandles.spreadInvoker(methodType, 3); + ret = (int) spreadInvoker.invoke(delegate, "a", "b", "c", new String[]{}); + assertEquals(42, ret); + + spreadInvoker = MethodHandles.spreadInvoker(methodType, 0); + ret = (int) spreadInvoker.invoke(delegate, new String[]{"a", "b", "c"}); + assertEquals(42, ret); + + // Exact invokes: Double check that the expected parameter type is + // Object[] and not T[]. + try { + spreadInvoker.invokeExact(delegate, new String[]{"a", "b", "c"}); + fail(); + } catch (WrongMethodTypeException expected) { + } + + ret = (int) spreadInvoker.invoke(delegate, new Object[]{"a", "b", "c"}); + assertEquals(42, ret); + } + + public static int spreadBoolean(String a, Boolean b, boolean c) { + assertEquals("a", a); + assertSame(Boolean.TRUE, b); + assertFalse(c); + + return 44; + } + + public static int spreadByte(String a, Byte b, byte c, + short d, int e, long f, float g, double h) { + assertEquals("a", a); + assertEquals(Byte.valueOf((byte) 1), b); + assertEquals((byte) 2, c); + assertEquals((short) 3, d); + assertEquals(4, e); + assertEquals(5l, f); + assertEquals(6.0f, g); + assertEquals(7.0, h); + + return 45; + } + + public static int spreadChar(String a, Character b, char c, + int d, long e, float f, double g) { + assertEquals("a", a); + assertEquals(Character.valueOf('1'), b); + assertEquals('2', c); + assertEquals((short) '3', d); + assertEquals('4', e); + assertEquals((float) '5', f); + assertEquals((double) '6', g); + + return 46; + } + + public static int spreadShort(String a, Short b, short c, + int d, long e, float f, double g) { + assertEquals("a", a); + assertEquals(Short.valueOf((short) 1), b); + assertEquals(2, c); + assertEquals(3, d); + assertEquals(4l, e); + assertEquals(5.0f, f); + assertEquals(6.0, g); + + return 47; + } + + public static int spreadInt(String a, Integer b, int c, + long d, float e, double f) { + assertEquals("a", a); + assertEquals(Integer.valueOf(1), b); + assertEquals(2, c); + assertEquals(3l, d); + assertEquals(4.0f, e); + assertEquals(5.0, f); + + return 48; + } + + public static int spreadLong(String a, Long b, long c, float d, double e) { + assertEquals("a", a); + assertEquals(Long.valueOf(1), b); + assertEquals(2l, c); + assertEquals(3.0f, d); + assertEquals(4.0, e); + + return 49; + } + + public static int spreadFloat(String a, Float b, float c, double d) { + assertEquals("a", a); + assertEquals(Float.valueOf(1.0f), b); + assertEquals(2.0f, c); + assertEquals(3.0, d); + + return 50; + } + + public static int spreadDouble(String a, Double b, double c) { + assertEquals("a", a); + assertEquals(Double.valueOf(1.0), b); + assertEquals(2.0, c); + + return 51; + } + + public static void testSpreaders_primitive() throws Throwable { + // boolean[] + // --------------------- + MethodType type = MethodType.methodType(int.class, + new Class[]{String.class, Boolean.class, boolean.class}); + MethodHandle delegate = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "spreadBoolean", type); + + MethodHandle spreader = delegate.asSpreader(boolean[].class, 2); + int ret = (int) spreader.invokeExact("a", new boolean[]{true, false}); + assertEquals(44, ret); + ret = (int) spreader.invoke("a", new boolean[]{true, false}); + assertEquals(44, ret); + + // boolean can't be cast to String (the first argument to the method). + try { + delegate.asSpreader(boolean[].class, 3); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // int can't be cast to boolean to supply the last argument to the method. + try { + delegate.asSpreader(int[].class, 1); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // byte[] + // --------------------- + type = MethodType.methodType(int.class, + new Class[]{ + String.class, Byte.class, byte.class, + short.class, int.class, long.class, + float.class, double.class}); + delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadByte", type); + + spreader = delegate.asSpreader(byte[].class, 7); + ret = (int) spreader.invokeExact("a", + new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}); + assertEquals(45, ret); + ret = (int) spreader.invoke("a", + new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}); + assertEquals(45, ret); + + // char[] + // --------------------- + type = MethodType.methodType(int.class, + new Class[]{ + String.class, Character.class, char.class, + int.class, long.class, float.class, double.class}); + delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadChar", type); + + spreader = delegate.asSpreader(char[].class, 6); + ret = (int) spreader.invokeExact("a", + new char[]{'1', '2', '3', '4', '5', '6'}); + assertEquals(46, ret); + ret = (int) spreader.invokeExact("a", + new char[]{'1', '2', '3', '4', '5', '6'}); + assertEquals(46, ret); + + // short[] + // --------------------- + type = MethodType.methodType(int.class, + new Class[]{ + String.class, Short.class, short.class, + int.class, long.class, float.class, double.class}); + delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadShort", type); + + spreader = delegate.asSpreader(short[].class, 6); + ret = (int) spreader.invokeExact("a", + new short[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}); + assertEquals(47, ret); + ret = (int) spreader.invoke("a", + new short[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}); + assertEquals(47, ret); + + // int[] + // --------------------- + type = MethodType.methodType(int.class, + new Class[]{ + String.class, Integer.class, int.class, + long.class, float.class, double.class}); + delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadInt", type); + + spreader = delegate.asSpreader(int[].class, 5); + ret = (int) spreader.invokeExact("a", new int[]{1, 2, 3, 4, 5}); + assertEquals(48, ret); + ret = (int) spreader.invokeExact("a", new int[]{1, 2, 3, 4, 5}); + assertEquals(48, ret); + + // long[] + // --------------------- + type = MethodType.methodType(int.class, + new Class[]{ + String.class, Long.class, long.class, float.class, double.class}); + delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadLong", type); + + spreader = delegate.asSpreader(long[].class, 4); + ret = (int) spreader.invokeExact("a", + new long[]{0x1, 0x2, 0x3, 0x4}); + assertEquals(49, ret); + ret = (int) spreader.invoke("a", + new long[]{0x1, 0x2, 0x3, 0x4}); + assertEquals(49, ret); + + // float[] + // --------------------- + type = MethodType.methodType(int.class, + new Class[]{ + String.class, Float.class, float.class, double.class}); + delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadFloat", type); + + spreader = delegate.asSpreader(float[].class, 3); + ret = (int) spreader.invokeExact("a", + new float[]{1.0f, 2.0f, 3.0f}); + assertEquals(50, ret); + ret = (int) spreader.invokeExact("a", + new float[]{1.0f, 2.0f, 3.0f}); + assertEquals(50, ret); + + // double[] + // --------------------- + type = MethodType.methodType(int.class, + new Class[]{String.class, Double.class, double.class}); + delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadDouble", type); + + spreader = delegate.asSpreader(double[].class, 2); + ret = (int) spreader.invokeExact("a", new double[]{1.0, 2.0}); + assertEquals(51, ret); + ret = (int) spreader.invokeExact("a", new double[]{1.0, 2.0}); + assertEquals(51, ret); + } + + public static void testInvokeWithArguments() throws Throwable { + MethodType methodType = MethodType.methodType(int.class, + new Class[]{String.class, String.class, String.class}); + MethodHandle handle = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "spreadReferences", methodType); + + Object ret = handle.invokeWithArguments(new Object[]{"a", "b", "c"}); + assertEquals(42, (int) ret); + ret = handle.invokeWithArguments(new String[]{"a", "b", "c"}); + assertEquals(42, (int) ret); + + // Also test the versions that take a List instead of an array. + ret = handle.invokeWithArguments(Arrays.asList(new Object[] {"a", "b", "c"})); + assertEquals(42, (int) ret); + ret = handle.invokeWithArguments(Arrays.asList(new String[]{"a", "b", "c"})); + assertEquals(42, (int) ret); + + // Pass in an array that's too small. Should throw an IAE. + try { + handle.invokeWithArguments(new Object[]{"a", "b"}); + fail(); + } catch (IllegalArgumentException expected) { + } catch (WrongMethodTypeException expected) { + } + + try { + handle.invokeWithArguments(Arrays.asList(new Object[]{"a", "b"})); + fail(); + } catch (IllegalArgumentException expected) { + } catch (WrongMethodTypeException expected) { + } + + + // Test implicit unboxing. + MethodType methodType2 = MethodType.methodType(int.class, + new Class[]{String.class, int.class}); + MethodHandle handle2 = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "spreadReferences_Unbox", methodType2); + + ret = (int) handle2.invokeWithArguments(new Object[]{"a", 43}); + assertEquals(43, (int) ret); + } + + public static int collectBoolean(String a, boolean[] b) { + assertEquals("a", a); + assertTrue(b[0]); + assertFalse(b[1]); + + return 44; + } + + public static int collectByte(String a, byte[] b) { + assertEquals("a", a); + assertEquals((byte) 1, b[0]); + assertEquals((byte) 2, b[1]); + return 45; + } + + public static int collectChar(String a, char[] b) { + assertEquals("a", a); + assertEquals('a', b[0]); + assertEquals('b', b[1]); + return 46; + } + + public static int collectShort(String a, short[] b) { + assertEquals("a", a); + assertEquals((short) 3, b[0]); + assertEquals((short) 4, b[1]); + + return 47; + } + + public static int collectInt(String a, int[] b) { + assertEquals("a", a); + assertEquals(42, b[0]); + assertEquals(43, b[1]); + + return 48; + } + + public static int collectLong(String a, long[] b) { + assertEquals("a", a); + assertEquals(100l, b[0]); + assertEquals(99l, b[1]); + + return 49; + } + + public static int collectFloat(String a, float[] b) { + assertEquals("a", a); + assertEquals(8.9f, b[0]); + assertEquals(9.1f, b[1]); + + return 50; + } + + public static int collectDouble(String a, double[] b) { + assertEquals("a", a); + assertEquals(6.7, b[0]); + assertEquals(7.8, b[1]); + + return 51; + } + + public static int collectCharSequence(String a, CharSequence[] b) { + assertEquals("a", a); + assertEquals("b", b[0]); + assertEquals("c", b[1]); + return 99; + } + + public static void testAsCollector() throws Throwable { + // Reference arrays. + // ------------------- + MethodHandle trailingRef = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "collectCharSequence", + MethodType.methodType(int.class, String.class, CharSequence[].class)); + + // int[] is not convertible to CharSequence[].class. + try { + trailingRef.asCollector(int[].class, 1); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Object[] is not convertible to CharSequence[].class. + try { + trailingRef.asCollector(Object[].class, 1); + fail(); + } catch (IllegalArgumentException expected) { + } + + // String[].class is convertible to CharSequence.class + MethodHandle collector = trailingRef.asCollector(String[].class, 2); + assertEquals(99, (int) collector.invoke("a", "b", "c")); + + // Too few arguments should fail with a WMTE. + try { + collector.invoke("a", "b"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Too many arguments should fail with a WMTE. + try { + collector.invoke("a", "b", "c", "d"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Sanity checks on other array types. + + MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "collectBoolean", + MethodType.methodType(int.class, String.class, boolean[].class)); + assertEquals(44, (int) target.asCollector(boolean[].class, 2).invoke("a", true, false)); + + target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectByte", + MethodType.methodType(int.class, String.class, byte[].class)); + assertEquals(45, (int) target.asCollector(byte[].class, 2).invoke("a", (byte) 1, (byte) 2)); + + target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectChar", + MethodType.methodType(int.class, String.class, char[].class)); + assertEquals(46, (int) target.asCollector(char[].class, 2).invoke("a", 'a', 'b')); + + target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectShort", + MethodType.methodType(int.class, String.class, short[].class)); + assertEquals(47, (int) target.asCollector(short[].class, 2).invoke("a", (short) 3, (short) 4)); + + target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectInt", + MethodType.methodType(int.class, String.class, int[].class)); + assertEquals(48, (int) target.asCollector(int[].class, 2).invoke("a", 42, 43)); + + target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectLong", + MethodType.methodType(int.class, String.class, long[].class)); + assertEquals(49, (int) target.asCollector(long[].class, 2).invoke("a", 100, 99)); + + target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectFloat", + MethodType.methodType(int.class, String.class, float[].class)); + assertEquals(50, (int) target.asCollector(float[].class, 2).invoke("a", 8.9f, 9.1f)); + + target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectDouble", + MethodType.methodType(int.class, String.class, double[].class)); + assertEquals(51, (int) target.asCollector(double[].class, 2).invoke("a", 6.7, 7.8)); + } + + public static String filter1(char a) { + return String.valueOf(a); + } + + public static char filter2(String b) { + return b.charAt(0); + } + + public static String badFilter1(char a, char b) { + return "bad"; + } + + public static int filterTarget(String a, char b, String c, char d) { + assertEquals("a", a); + assertEquals('b', b); + assertEquals("c", c); + assertEquals('d', d); + return 56; + } + + public static void testFilterArguments() throws Throwable { + MethodHandle filter1 = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "filter1", MethodType.methodType(String.class, char.class)); + MethodHandle filter2 = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "filter2", MethodType.methodType(char.class, String.class)); + + MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "filterTarget", MethodType.methodType(int.class, + String.class, char.class, String.class, char.class)); + + // In all the cases below, the values printed will be 'a', 'b', 'c', 'd'. + + // Filter arguments [0, 1] - all other arguments are passed through + // as is. + MethodHandle adapter = MethodHandles.filterArguments( + target, 0, filter1, filter2); + assertEquals(56, (int) adapter.invokeExact('a', "bXXXX", "c", 'd')); + + // Filter arguments [1, 2]. + adapter = MethodHandles.filterArguments(target, 1, filter2, filter1); + assertEquals(56, (int) adapter.invokeExact("a", "bXXXX", 'c', 'd')); + + // Filter arguments [2, 3]. + adapter = MethodHandles.filterArguments(target, 2, filter1, filter2); + assertEquals(56, (int) adapter.invokeExact("a", 'b', 'c', "dXXXXX")); + + // Try out a few error cases : + + // The return types of the filter doesn't align with the expected argument + // type of the target. + try { + adapter = MethodHandles.filterArguments(target, 2, filter2, filter1); + fail(); + } catch (IllegalArgumentException expected) { + } + + // There are more filters than arguments. + try { + adapter = MethodHandles.filterArguments(target, 3, filter2, filter1); + fail(); + } catch (IllegalArgumentException expected) { + } + + // We pass in an obviously bogus position. + try { + adapter = MethodHandles.filterArguments(target, -1, filter2, filter1); + fail(); + } catch (ArrayIndexOutOfBoundsException expected) { + } + + // We pass in a function that has more than one argument. + MethodHandle badFilter1 = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "badFilter1", + MethodType.methodType(String.class, char.class, char.class)); + + try { + adapter = MethodHandles.filterArguments(target, 0, badFilter1, filter2); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + static void voidFilter(char a, char b) { + } + + static String filter(char a, char b) { + return String.valueOf(a) + "+" + b; + } + + static char badFilter(char a, char b) { + return 0; + } + + static String target(String a, String b, String c) { + return ("a: " + a + ", b: " + b + ", c: " + c); + } + + public static void testCollectArguments() throws Throwable { + // Test non-void filters. + MethodHandle filter = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "filter", + MethodType.methodType(String.class, char.class, char.class)); + + MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "target", + MethodType.methodType(String.class, String.class, String.class, String.class)); + + // Filter at position 0. + MethodHandle adapter = MethodHandles.collectArguments(target, 0, filter); + assertEquals("a: a+b, b: c, c: d", + (String) adapter.invokeExact('a', 'b', "c", "d")); + + // Filter at position 1. + adapter = MethodHandles.collectArguments(target, 1, filter); + assertEquals("a: a, b: b+c, c: d", + (String) adapter.invokeExact("a", 'b', 'c', "d")); + + // Filter at position 2. + adapter = MethodHandles.collectArguments(target, 2, filter); + assertEquals("a: a, b: b, c: c+d", + (String) adapter.invokeExact("a", "b", 'c', 'd')); + + // Test void filters. Note that we're passing in one more argument + // than usual because the filter returns nothing - we have to invoke with + // the full set of filter args and the full set of target args. + filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "voidFilter", + MethodType.methodType(void.class, char.class, char.class)); + adapter = MethodHandles.collectArguments(target, 0, filter); + assertEquals("a: a, b: b, c: c", + (String) adapter.invokeExact('a', 'b', "a", "b", "c")); + + adapter = MethodHandles.collectArguments(target, 1, filter); + assertEquals("a: a, b: b, c: c", + (String) adapter.invokeExact("a", 'a', 'b', "b", "c")); + + // Test out a few failure cases. + filter = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "filter", + MethodType.methodType(String.class, char.class, char.class)); + + // Bogus filter position. + try { + adapter = MethodHandles.collectArguments(target, 3, filter); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + + // Mismatch in filter return type. + filter = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "badFilter", + MethodType.methodType(char.class, char.class, char.class)); + try { + adapter = MethodHandles.collectArguments(target, 0, filter); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + static int insertReceiver(String a, int b, Integer c, String d) { + assertEquals("foo", a); + assertEquals(56, b); + assertEquals(Integer.valueOf(57), c); + assertEquals("bar", d); + + return 73; + } + + public static void testInsertArguments() throws Throwable { + MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "insertReceiver", + MethodType.methodType(int.class, + String.class, int.class, Integer.class, String.class)); + + // Basic single element array inserted at position 0. + MethodHandle adapter = MethodHandles.insertArguments( + target, 0, new Object[]{"foo"}); + assertEquals(73, (int) adapter.invokeExact(56, Integer.valueOf(57), "bar")); + + // Exercise unboxing. + adapter = MethodHandles.insertArguments( + target, 1, new Object[]{Integer.valueOf(56), 57}); + assertEquals(73, (int) adapter.invokeExact("foo", "bar")); + + // Exercise a widening conversion. + adapter = MethodHandles.insertArguments( + target, 1, new Object[]{(short) 56, Integer.valueOf(57)}); + assertEquals(73, (int) adapter.invokeExact("foo", "bar")); + + // Insert an argument at the last position. + adapter = MethodHandles.insertArguments( + target, 3, new Object[]{"bar"}); + assertEquals(73, (int) adapter.invokeExact("foo", 56, Integer.valueOf(57))); + + // Exercise a few error cases. + + // A reference type that can't be cast to another reference type. + try { + MethodHandles.insertArguments(target, 3, new Object[]{new Object()}); + fail(); + } catch (ClassCastException expected) { + } + + // A boxed type that can't be unboxed correctly. + try { + MethodHandles.insertArguments(target, 1, new Object[]{Long.valueOf(56)}); + fail(); + } catch (ClassCastException expected) { + } + } + + public static String foldFilter(char a, char b) { + return String.valueOf(a) + "+" + b; + } + + public static void voidFoldFilter(String e, char a, char b) { + assertEquals("a", e); + assertEquals('c', a); + assertEquals('d', b); + } + + public static String foldTarget(String a, char b, char c, String d) { + return ("a: " + a + " ,b:" + b + " ,c:" + c + " ,d:" + d); + } + + public static void mismatchedVoidFilter(Integer a) { + } + + public static Integer mismatchedNonVoidFilter(char a, char b) { + return null; + } + + public static void testFoldArguments() throws Throwable { + // Test non-void filters. + MethodHandle filter = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "foldFilter", + MethodType.methodType(String.class, char.class, char.class)); + + MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "foldTarget", + MethodType.methodType(String.class, String.class, + char.class, char.class, String.class)); + + // Folder with a non-void type. + MethodHandle adapter = MethodHandles.foldArguments(target, filter); + assertEquals("a: c+d ,b:c ,c:d ,d:e", + (String) adapter.invokeExact('c', 'd', "e")); + + // Folder with a void type. + filter = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "voidFoldFilter", + MethodType.methodType(void.class, String.class, char.class, char.class)); + adapter = MethodHandles.foldArguments(target, filter); + assertEquals("a: a ,b:c ,c:d ,d:e", + (String) adapter.invokeExact("a", 'c', 'd', "e")); + + // Test a few erroneous cases. + + filter = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "mismatchedVoidFilter", + MethodType.methodType(void.class, Integer.class)); + try { + adapter = MethodHandles.foldArguments(target, filter); + fail(); + } catch (IllegalArgumentException expected) { + } + + filter = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "mismatchedNonVoidFilter", + MethodType.methodType(Integer.class, char.class, char.class)); + try { + adapter = MethodHandles.foldArguments(target, filter); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + // An exception thrown on worker threads and re-thrown on the main thread. + static Throwable workerException = null; + + private static void invokeMultiThreaded(final MethodHandle mh) throws Throwable { + // Create enough worker threads to be oversubscribed in bid to force some parallelism. + final int threadCount = Runtime.getRuntime().availableProcessors() + 1; + final Thread threads [] = new Thread [threadCount]; + + // Launch worker threads and iterate invoking method handle. + for (int i = 0; i < threadCount; ++i) { + threads[i] = new Thread(new Runnable() { + @Override + public void run() { + try { + for (int j = 0; j < TEST_THREAD_ITERATIONS; ++j) { + mh.invoke(); + } + } catch (Throwable t) { + workerException = t; + fail("Unexpected exception " + workerException); + } + }}); + threads[i].start(); + } + + // Wait for completion + for (int i = 0; i < threadCount; ++i) { + threads[i].join(); + } + + // Fail on main thread to avoid test appearing to complete successfully. + Throwable t = workerException; + workerException = null; + if (t != null) { + throw t; + } + } + + public static void testDropInsertArgumentsMultithreaded() throws Throwable { + MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "dropArguments_delegate", + MethodType.methodType(void.class, new Class[]{String.class, long.class})); + MethodHandle mh = MethodHandles.dropArguments(delegate, 0, int.class, Object.class); + mh = MethodHandles.insertArguments(mh, 0, 3333, "bogon", "foo", 42); + invokeMultiThreaded(mh); + } + + private static void exceptionHandler_delegate(NumberFormatException e, int x, int y, long z) + throws Throwable { + assertEquals(e.getClass(), NumberFormatException.class); + assertEquals(e.getMessage(), "fake"); + assertEquals(x, 66); + assertEquals(y, 51); + assertEquals(z, 20000000000l); + } + + public static void testThrowCatchExceptionMultiThreaded() throws Throwable { + MethodHandle thrower = MethodHandles.throwException(void.class, + NumberFormatException.class); + thrower = MethodHandles.dropArguments(thrower, 0, int.class, int.class, long.class); + MethodHandle handler = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "exceptionHandler_delegate", + MethodType.methodType(void.class, NumberFormatException.class, + int.class, int.class, long.class)); + MethodHandle catcher = + MethodHandles.catchException(thrower, NumberFormatException.class, handler); + MethodHandle caller = MethodHandles.insertArguments(catcher, 0, 66, 51, 20000000000l, + new NumberFormatException("fake")); + invokeMultiThreaded(caller); + } + + private static void testTargetAndFallback_delegate(MethodHandle mh) throws Throwable { + String actual = (String) mh.invoke("target", 42, 56); + assertEquals("target", actual); + actual = (String) mh.invoke("blah", 41, 56); + assertEquals("fallback", actual); + } + + public static void testGuardWithTestMultiThreaded() throws Throwable { + MethodHandle test = + MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "testGuardWithTest_test", + MethodType.methodType(boolean.class, + new Class[]{String.class, + long.class})); + final MethodType type = MethodType.methodType(String.class, + new Class[]{String.class, long.class, int.class}); + final MethodHandle target = + MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "testGuardWithTest_target", type); + final MethodHandle fallback = + MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, + "testGuardWithTest_fallback", type); + MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback); + MethodHandle tester = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, + "testTargetAndFallback_delegate", + MethodType.methodType(void.class, MethodHandle.class)); + invokeMultiThreaded(MethodHandles.insertArguments(tester, 0, adapter)); + } + + private static void arrayElementSetterGetter_delegate(MethodHandle getter, + MethodHandle setter, + int [] values) + throws Throwable{ + for (int i = 0; i < values.length; ++i) { + int value = i * 13; + setter.invoke(values, i, value); + assertEquals(values[i], value); + assertEquals(getter.invoke(values, i), values[i]); + } + } + + public static void testReferenceArrayGetterMultiThreaded() throws Throwable { + MethodHandle getter = MethodHandles.arrayElementGetter(int[].class); + MethodHandle setter = MethodHandles.arrayElementSetter(int[].class); + MethodHandle mh = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, + "arrayElementSetterGetter_delegate", + MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, int[].class)); + mh = MethodHandles.insertArguments(mh, 0, getter, setter, + new int[] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 }); + invokeMultiThreaded(mh); + } + + private static void checkConstant_delegate(MethodHandle mh, double value) throws Throwable { + assertEquals(mh.invoke(), value); + } + + public static void testConstantMultithreaded() throws Throwable { + final double value = 7.77e77; + MethodHandle constant = MethodHandles.constant(double.class, value); + MethodHandle mh = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "checkConstant_delegate", + MethodType.methodType(void.class, MethodHandle.class, double.class)); + mh = MethodHandles.insertArguments(mh, 0, constant, value); + invokeMultiThreaded(mh); + } + + private static void checkIdentity_delegate(MethodHandle mh, char value) throws Throwable { + assertEquals(mh.invoke(value), value); + } + + public static void testIdentityMultiThreaded() throws Throwable { + final char value = 'z'; + MethodHandle identity = MethodHandles.identity(char.class); + MethodHandle mh = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "checkIdentity_delegate", + MethodType.methodType(void.class, MethodHandle.class, char.class)); + mh = MethodHandles.insertArguments(mh, 0, identity, value); + invokeMultiThreaded(mh); + } + + private static int multiplyByTwo(int x) { return x * 2; } + private static int divideByTwo(int x) { return x / 2; } + private static void assertMethodHandleInvokeEquals(MethodHandle mh, int value) throws Throwable{ + assertEquals(mh.invoke(value), value); + } + + public static void testFilterReturnValueMultiThreaded() throws Throwable { + MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "multiplyByTwo", + MethodType.methodType(int.class, int.class)); + MethodHandle filter = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "divideByTwo", + MethodType.methodType(int.class, int.class)); + MethodHandle filtered = MethodHandles.filterReturnValue(target, filter); + assertEquals(filtered.invoke(33), 33); + MethodHandle mh = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "assertMethodHandleInvokeEquals", + MethodType.methodType(void.class, MethodHandle.class, int.class)); + invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, filtered, 77)); + } + + public static void compareStringAndFloat(String s, float f) { + assertEquals(s, Float.toString(f)); + } + + public static void testPermuteArgumentsMultiThreaded() throws Throwable { + MethodHandle mh = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "compareStringAndFloat", + MethodType.methodType(void.class, String.class, float.class)); + mh = MethodHandles.permuteArguments( + mh, MethodType.methodType(void.class, float.class, String.class), 1, 0); + invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, 2.22f, "2.22")); + } + + public static void testSpreadInvokerMultiThreaded() throws Throwable { + MethodType methodType = MethodType.methodType( + int.class, new Class[]{String.class, String.class, String.class}); + MethodHandle delegate = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "spreadReferences", methodType); + MethodHandle mh = delegate.asSpreader(String[].class, 3); + mh = MethodHandles.insertArguments(mh, 0, new Object[] { new String [] { "a", "b", "c" }}); + invokeMultiThreaded(mh); + } + + public static void testCollectorMultiThreaded() throws Throwable { + MethodHandle trailingRef = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "collectCharSequence", + MethodType.methodType(int.class, String.class, CharSequence[].class)); + MethodHandle mh = trailingRef.asCollector(String[].class, 2); + mh = MethodHandles.insertArguments(mh, 0, "a", "b", "c"); + invokeMultiThreaded(mh); + } + + public static void testFilterArgumentsMultiThreaded() throws Throwable { + MethodHandle filter1 = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "filter1", + MethodType.methodType(String.class, char.class)); + MethodHandle filter2 = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "filter2", + MethodType.methodType(char.class, String.class)); + MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "filterTarget", + MethodType.methodType(int.class, String.class, char.class, String.class, char.class)); + MethodHandle adapter = MethodHandles.filterArguments(target, 2, filter1, filter2); + invokeMultiThreaded(MethodHandles.insertArguments(adapter, 0, "a", 'b', 'c', "dXXXXX")); + } + + private static void checkStringResult_delegate(MethodHandle mh, + String expected) throws Throwable { + assertEquals(mh.invoke(), expected); + } + + public static void testCollectArgumentsMultiThreaded() throws Throwable { + MethodHandle filter = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "filter", + MethodType.methodType(String.class, char.class, char.class)); + MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "target", + MethodType.methodType(String.class, String.class, String.class, String.class)); + MethodHandle collect = MethodHandles.collectArguments(target, 2, filter); + collect = MethodHandles.insertArguments(collect, 0, "a", "b", 'c', 'd'); + MethodHandle mh = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "checkStringResult_delegate", + MethodType.methodType(void.class, MethodHandle.class, String.class)); + invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, collect, "a: a, b: b, c: c+d")); + } + + public static void testFoldArgumentsMultiThreaded() throws Throwable { + MethodHandle target = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "foldTarget", + MethodType.methodType(String.class, String.class, + char.class, char.class, String.class)); + MethodHandle filter = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "foldFilter", + MethodType.methodType(String.class, char.class, char.class)); + MethodHandle adapter = MethodHandles.foldArguments(target, filter); + adapter = MethodHandles.insertArguments(adapter, 0, 'c', 'd', "e"); + MethodHandle mh = MethodHandles.lookup().findStatic( + MethodHandleCombinersTest.class, "checkStringResult_delegate", + MethodType.methodType(void.class, MethodHandle.class, String.class)); + invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, adapter, "a: c+d ,b:c ,c:d ,d:e")); + } +} diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleInfoTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleInfoTest.java new file mode 100644 index 000000000..12b82b05f --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleInfoTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.lang.invoke; + +import junit.framework.TestCase; + +import java.lang.invoke.MethodHandleInfo; +import java.lang.invoke.MethodType; + +import static java.lang.invoke.MethodHandleInfo.*; + +public class MethodHandleInfoTest extends TestCase { + public void test_toString() { + final MethodType type = MethodType.methodType(String.class, String.class); + String string = MethodHandleInfo.toString(REF_invokeVirtual, String.class, "concat", type); + assertEquals("invokeVirtual java.lang.String.concat:(String)String", string); + + try { + MethodHandleInfo.toString(-1, String.class, "concat", type); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + MethodHandleInfo.toString(REF_invokeVirtual, String.class, null, type); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodHandleInfo.toString(REF_invokeVirtual, null, "concat", type); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodHandleInfo.toString(REF_invokeVirtual, String.class, "concat", null); + fail(); + } catch (NullPointerException expected) { + } + } + + public void test_referenceKindToString() { + assertEquals("getField", referenceKindToString(REF_getField)); + assertEquals("getStatic", referenceKindToString(REF_getStatic)); + assertEquals("putField", referenceKindToString(REF_putField)); + assertEquals("putStatic", referenceKindToString(REF_putStatic)); + assertEquals("invokeVirtual", referenceKindToString(REF_invokeVirtual)); + assertEquals("invokeStatic", referenceKindToString(REF_invokeStatic)); + assertEquals("invokeSpecial", referenceKindToString(REF_invokeSpecial)); + assertEquals("newInvokeSpecial", referenceKindToString(REF_newInvokeSpecial)); + assertEquals("invokeInterface", referenceKindToString(REF_invokeInterface)); + + try { + referenceKindToString(-1); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + referenceKindToString(256); + fail(); + } catch (IllegalArgumentException expected) { + } + } +} diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java new file mode 100644 index 000000000..9b3850c5f --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java @@ -0,0 +1,1997 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.lang.invoke; + +import junit.framework.TestCase; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandleInfo; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Vector; + +import static java.lang.invoke.MethodHandles.Lookup.*; + +public class MethodHandlesTest extends TestCase { + private static final int ALL_LOOKUP_MODES = (PUBLIC | PRIVATE | PACKAGE | PROTECTED); + + public void test_publicLookupClassAndModes() { + MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + assertSame(Object.class, publicLookup.lookupClass()); + assertEquals(PUBLIC, publicLookup.lookupModes()); + } + + public void test_defaultLookupClassAndModes() { + MethodHandles.Lookup defaultLookup = MethodHandles.lookup(); + assertSame(MethodHandlesTest.class, defaultLookup.lookupClass()); + assertEquals(ALL_LOOKUP_MODES, defaultLookup.lookupModes()); + } + + public void test_LookupIn() { + MethodHandles.Lookup defaultLookup = MethodHandles.lookup(); + + // A class in the same package loses the privilege to lookup protected and private + // members. + MethodHandles.Lookup siblingLookup = defaultLookup.in(PackageSibling.class); + assertEquals(ALL_LOOKUP_MODES & ~(PROTECTED | PRIVATE), siblingLookup.lookupModes()); + + // The new lookup isn't in the same package, so it loses all its privileges except + // for public. + MethodHandles.Lookup nonSibling = defaultLookup.in(Vector.class); + assertEquals(PUBLIC, nonSibling.lookupModes()); + + // Special case, sibling inner classes in the same parent class + MethodHandles.Lookup inner2 = Inner1.lookup.in(Inner2.class); + assertEquals(PUBLIC | PRIVATE | PACKAGE, inner2.lookupModes()); + + try { + MethodHandles.lookup().in(null); + fail(); + } catch (NullPointerException expected) { + } + + // Callers cannot change the lookup context to anything within the java.lang.invoke package. + try { + MethodHandles.lookup().in(MethodHandle.class); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void test_findStatic() throws Exception { + MethodHandles.Lookup defaultLookup = MethodHandles.lookup(); + + // Handle for String String#valueOf(char[]). + MethodHandle handle = defaultLookup.findStatic(String.class, "valueOf", + MethodType.methodType(String.class, char[].class)); + assertNotNull(handle); + + assertEquals(String.class, handle.type().returnType()); + assertEquals(1, handle.type().parameterCount()); + assertEquals(char[].class, handle.type().parameterArray()[0]); + assertEquals(MethodHandle.INVOKE_STATIC, handle.getHandleKind()); + + MethodHandles.Lookup inUtil = defaultLookup.in(Vector.class); + + // Package private in a public class in a different package from the lookup. + try { + inUtil.findStatic(MethodHandlesTest.class, "packagePrivateStaticMethod", + MethodType.methodType(void.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Protected in a public class in a different package from the lookup. + try { + inUtil.findStatic(MethodHandlesTest.class, "protectedStaticMethod", + MethodType.methodType(void.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Private in a public class in a different package from the lookup. + try { + inUtil.findStatic(MethodHandlesTest.class, "privateStaticMethod", + MethodType.methodType(void.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Public method in a package private class in a different package from the lookup. + try { + inUtil.findStatic(PackageSibling.class, "publicStaticMethod", + MethodType.methodType(void.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Public virtual method should not discoverable via findStatic. + try { + inUtil.findStatic(MethodHandlesTest.class, "publicMethod", + MethodType.methodType(void.class)); + fail(); + } catch (IllegalAccessException expected) { + } + } + + public void test_findConstructor() throws Exception { + MethodHandles.Lookup defaultLookup = MethodHandles.lookup(); + + // Handle for String.(String). The requested type of the constructor declares + // a void return type (to match the bytecode) but the handle that's created will declare + // a return type that's equal to the type being constructed. + MethodHandle handle = defaultLookup.findConstructor(String.class, + MethodType.methodType(void.class, String.class)); + assertNotNull(handle); + + assertEquals(String.class, handle.type().returnType()); + assertEquals(1, handle.type().parameterCount()); + + assertEquals(String.class, handle.type().parameterArray()[0]); + assertEquals(MethodHandle.INVOKE_DIRECT, handle.getHandleKind()); + + MethodHandles.Lookup inUtil = defaultLookup.in(Vector.class); + + // Package private in a public class in a different package from the lookup. + try { + inUtil.findConstructor(ConstructorTest.class, + MethodType.methodType(void.class, String.class, int.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Protected in a public class in a different package from the lookup. + try { + inUtil.findConstructor(ConstructorTest.class, + MethodType.methodType(void.class, String.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Private in a public class in a different package from the lookup. + try { + inUtil.findConstructor(ConstructorTest.class, + MethodType.methodType(void.class, String.class, char.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Protected constructor in a package private class in a different package from the lookup. + try { + inUtil.findConstructor(PackageSibling.class, + MethodType.methodType(void.class, String.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Public constructor in a package private class in a different package from the lookup. + try { + inUtil.findConstructor(PackageSibling.class, + MethodType.methodType(void.class, String.class, char.class)); + fail(); + } catch (IllegalAccessException expected) { + } + } + + public void test_findVirtual() throws Exception { + MethodHandles.Lookup defaultLookup = MethodHandles.lookup(); + + // String.replaceAll(String, String); + MethodHandle handle = defaultLookup.findVirtual(String.class, "replaceAll", + MethodType.methodType(String.class, String.class, String.class)); + assertNotNull(handle); + + assertEquals(String.class, handle.type().returnType()); + // Note that the input type was (String,String)String but the handle's type is + // (String, String, String)String - since it's a non static call, we prepend the + // receiver to the type. + assertEquals(3, handle.type().parameterCount()); + MethodType expectedType = MethodType.methodType(String.class, + new Class[] { String.class, String.class, String.class}); + + assertEquals(expectedType, handle.type()); + assertEquals(MethodHandle.INVOKE_VIRTUAL, handle.getHandleKind()); + + MethodHandles.Lookup inUtil = defaultLookup.in(Vector.class); + + // Package private in a public class in a different package from the lookup. + try { + inUtil.findVirtual(MethodHandlesTest.class, "packagePrivateMethod", + MethodType.methodType(void.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Protected in a public class in a different package from the lookup. + try { + inUtil.findVirtual(MethodHandlesTest.class, "protectedMethod", + MethodType.methodType(void.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Protected in a public class in a different package from the lookup. + try { + inUtil.findVirtual(MethodHandlesTest.class, "privateMethod", + MethodType.methodType(void.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Public method in a package private class in a different package from the lookup. + try { + inUtil.findVirtual(PackageSibling.class, "publicMethod", + MethodType.methodType(void.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Public static method should not discoverable via findVirtual. + try { + inUtil.findVirtual(MethodHandlesTest.class, "publicStaticMethod", + MethodType.methodType(void.class)); + fail(); + } catch (IllegalAccessException expected) { + } + } + + public static class A { + public boolean aCalled; + + public A() {} + + public void foo() { + aCalled = true; + } + + public static final Lookup lookup = MethodHandles.lookup(); + } + + public static class B extends A { + public boolean bCalled; + + public void foo() { + bCalled = true; + } + + public static final Lookup lookup = MethodHandles.lookup(); + } + + public static class C extends B { + public static final Lookup lookup = MethodHandles.lookup(); + } + + public static class D { + public boolean privateDCalled; + + private final void privateRyan() { + privateDCalled = true; + } + + public static final Lookup lookup = MethodHandles.lookup(); + } + + public static class E extends D { + public static final Lookup lookup = MethodHandles.lookup(); + } + + public void testfindSpecial_invokeSuperBehaviour() throws Throwable { + // This is equivalent to an invoke-super instruction where the referrer + // is B.class. + MethodHandle mh1 = B.lookup.findSpecial(A.class /* refC */, "foo", + MethodType.methodType(void.class), B.class /* specialCaller */); + + // This should be as if an invoke-super was called from one of B's methods. + B bInstance = new B(); + mh1.invokeExact(bInstance); + assertTrue(bInstance.aCalled); + + bInstance = new B(); + mh1.invoke(bInstance); + assertTrue(bInstance.aCalled); + + // This should not work. The receiver type in the handle will be suitably + // restricted to B and subclasses. + try { + mh1.invoke(new A()); + fail(); + } catch (ClassCastException expected) { + } + + try { + mh1.invokeExact(new A()); + fail(); + } catch (WrongMethodTypeException expected) { + } + + + // This should *still* be as if an invoke-super was called from one of B's + // methods, despite the fact that we're operating on a C. + C cInstance = new C(); + mh1.invoke(cInstance); + assertTrue(cInstance.aCalled); + + // Now that C is the special caller, the next invoke will call B.foo. + MethodHandle mh2 = C.lookup.findSpecial(A.class /* refC */, "foo", + MethodType.methodType(void.class), C.class /* specialCaller */); + cInstance = new C(); + mh2.invokeExact(cInstance); + assertTrue(cInstance.bCalled); + + // Shouldn't allow invoke-super semantics from an unrelated special caller. + try { + C.lookup.findSpecial(A.class, "foo", + MethodType.methodType(void.class), D.class /* specialCaller */); + fail(); + } catch (IllegalAccessException expected) { + } + + // Check return type matches for find. + try { + B.lookup.findSpecial(A.class /* refC */, "foo", + MethodType.methodType(int.class), B.class /* specialCaller */); + fail(); + } catch (NoSuchMethodException e) {} + // Check constructors + try { + B.lookup.findSpecial(A.class /* refC */, "", + MethodType.methodType(void.class), B.class /* specialCaller */); + fail(); + } catch (NoSuchMethodException e) {} + } + + public void testfindSpecial_invokeDirectBehaviour() throws Throwable { + D dInstance = new D(); + + MethodHandle mh3 = D.lookup.findSpecial(D.class, "privateRyan", + MethodType.methodType(void.class), D.class /* specialCaller */); + mh3.invoke(dInstance); + + // The private method shouldn't be accessible from any special caller except + // itself... + try { + D.lookup.findSpecial(D.class, "privateRyan", MethodType.methodType(void.class), + C.class); + fail(); + } catch (IllegalAccessException expected) { + } + + // ... or from any lookup context except its own. + try { + E.lookup.findSpecial(D.class, "privateRyan", MethodType.methodType(void.class), + E.class); + fail(); + } catch (IllegalAccessException expected) { + } + } + + public void testExceptionDetailMessages() throws Throwable { + MethodHandle handle = MethodHandles.lookup().findVirtual(String.class, "concat", + MethodType.methodType(String.class, String.class)); + + try { + handle.invokeExact("a", new Object()); + fail(); + } catch (WrongMethodTypeException ex) { + assertEquals( + "Expected (java.lang.String, java.lang.String)java.lang.String " + + "but was (java.lang.String, java.lang.Object)void", + ex.getMessage()); + } + } + + public interface Foo { + public String foo(); + } + + public interface Bar extends Foo { + public String bar(); + } + + public static abstract class BarAbstractSuper { + public abstract String abstractSuperPublicMethod(); + } + + public static class BarSuper extends BarAbstractSuper { + public String superPublicMethod() { + return "superPublicMethod"; + } + + protected String superProtectedMethod() { + return "superProtectedMethod"; + } + + String superPackageMethod() { + return "superPackageMethod"; + } + + public String abstractSuperPublicMethod() { + return "abstractSuperPublicMethod"; + } + } + + public static class BarImpl extends BarSuper implements Bar { + public BarImpl() { + } + + @Override + public String foo() { + return "foo"; + } + + @Override + public String bar() { + return "bar"; + } + + public String add(int x, int y) { + return Arrays.toString(new int[] { x, y }); + } + + private String privateMethod() { return "privateMethod"; } + + public static String staticMethod() { return staticString; } + + private static String staticString; + + { + // Static constructor + staticString = Long.toString(System.currentTimeMillis()); + } + + static final MethodHandles.Lookup lookup = MethodHandles.lookup(); + } + + public void testfindVirtual() throws Throwable { + // Virtual lookups on static methods should not succeed. + try { + MethodHandles.lookup().findVirtual( + BarImpl.class, "staticMethod", MethodType.methodType(String.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Virtual lookups on private methods should not succeed, unless the Lookup + // context had sufficient privileges. + try { + MethodHandles.lookup().findVirtual( + BarImpl.class, "privateMethod", MethodType.methodType(String.class)); + fail(); + } catch (IllegalAccessException expected) { + } + + // Virtual lookup on a private method with a context that *does* have sufficient + // privileges. + MethodHandle mh = BarImpl.lookup.findVirtual( + BarImpl.class, "privateMethod", MethodType.methodType(String.class)); + String str = (String) mh.invoke(new BarImpl()); + assertEquals("privateMethod", str); + + // Find virtual must find interface methods defined by interfaces implemented + // by the class. + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "foo", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + assertEquals("foo", str); + + // Find virtual should check rtype. + try { + MethodHandles.lookup().findVirtual(BarImpl.class, "foo", + MethodType.methodType(void.class)); + fail(); + } catch (NoSuchMethodException expected) { + } + + // And ptypes + mh = MethodHandles.lookup().findVirtual( + BarImpl.class, "add", MethodType.methodType(String.class, int.class, int.class)); + try { + MethodHandles.lookup().findVirtual( + BarImpl.class, "add", + MethodType.methodType(String.class, Integer.class, int.class)); + } catch (NoSuchMethodException expected) { + } + + // .. and their super-interfaces. + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + assertEquals("bar", str); + + + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + assertEquals("bar", str); + + mh = MethodHandles.lookup().findVirtual(BarAbstractSuper.class, "abstractSuperPublicMethod", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + assertEquals("abstractSuperPublicMethod", str); + + // We should also be able to lookup public / protected / package methods in + // the super class, given sufficient access privileges. + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPublicMethod", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + assertEquals("superPublicMethod", str); + + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superProtectedMethod", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + assertEquals("superProtectedMethod", str); + + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPackageMethod", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + assertEquals("superPackageMethod", str); + + try { + MethodHandles.lookup().findVirtual(BarImpl.class, "", + MethodType.methodType(void.class)); + fail(); + } catch (NoSuchMethodException expected) { + } + } + + public void testfindStatic() throws Throwable { + MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod", + MethodType.methodType(String.class)); + try { + MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod", + MethodType.methodType(void.class)); + fail(); + } catch (NoSuchMethodException expected) { + } + + try { + MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod", + MethodType.methodType(String.class, int.class)); + fail(); + } catch (NoSuchMethodException expected) { + } + + try { + MethodHandles.lookup().findStatic(BarImpl.class, "", + MethodType.methodType(void.class)); + fail(); + } catch (NoSuchMethodException expected) { + } + + try { + MethodHandles.lookup().findStatic(BarImpl.class, "", + MethodType.methodType(void.class)); + fail(); + } catch (NoSuchMethodException expected) { + } + } + + static class UnreflectTesterBase { + public String overridenMethod() { + return "Base"; + } + } + + static class UnreflectTester extends UnreflectTesterBase { + public String publicField; + private String privateField; + + public static String publicStaticField = "publicStaticValue"; + private static String privateStaticField = "privateStaticValue"; + + private UnreflectTester(String val) { + publicField = val; + privateField = val; + } + + // NOTE: The boolean constructor argument only exists to give this a + // different signature. + public UnreflectTester(String val, boolean unused) { + this(val); + } + + private static String privateStaticMethod() { + return "privateStaticMethod"; + } + + private String privateMethod() { + return "privateMethod"; + } + + public static String publicStaticMethod() { + return "publicStaticMethod"; + } + + public String publicMethod() { + return "publicMethod"; + } + + public String publicVarArgsMethod(String... args) { + return "publicVarArgsMethod"; + } + + @Override + public String overridenMethod() { + return "Override"; + } + + public static final Lookup lookup = MethodHandles.lookup(); + } + + public void testUnreflects_publicMethods() throws Throwable { + UnreflectTester instance = new UnreflectTester("unused"); + Method publicMethod = UnreflectTester.class.getMethod("publicMethod"); + + MethodHandle mh = MethodHandles.lookup().unreflect(publicMethod); + assertEquals("publicMethod", (String) mh.invoke(instance)); + assertEquals("publicMethod", (String) mh.invokeExact(instance)); + + Method publicStaticMethod = UnreflectTester.class.getMethod("publicStaticMethod"); + mh = MethodHandles.lookup().unreflect(publicStaticMethod); + assertEquals("publicStaticMethod", (String) mh.invoke()); + assertEquals("publicStaticMethod", (String) mh.invokeExact()); + } + + public void testUnreflects_privateMethods() throws Throwable { + Method privateMethod = UnreflectTester.class.getDeclaredMethod("privateMethod"); + + try { + MethodHandles.lookup().unreflect(privateMethod); + fail(); + } catch (IllegalAccessException expected) { + } + + UnreflectTester instance = new UnreflectTester("unused"); + MethodHandle mh = UnreflectTester.lookup.unreflectSpecial(privateMethod, + UnreflectTester.class); + assertEquals("privateMethod", (String) mh.invoke(instance)); + assertEquals("privateMethod", (String) mh.invokeExact(instance)); + + privateMethod.setAccessible(true); + mh = MethodHandles.lookup().unreflect(privateMethod); + assertEquals("privateMethod", (String) mh.invoke(instance)); + assertEquals("privateMethod", (String) mh.invokeExact(instance)); + + Method privateStaticMethod = UnreflectTester.class.getDeclaredMethod("privateStaticMethod"); + try { + MethodHandles.lookup().unreflect(privateStaticMethod); + fail(); + } catch (IllegalAccessException expected) { + } + + try { + mh = UnreflectTester.lookup.unreflectSpecial(privateStaticMethod, + UnreflectTester.class); + fail(); + } catch (IllegalAccessException expected) { + } + + privateStaticMethod.setAccessible(true); + mh = MethodHandles.lookup().unreflect(privateStaticMethod); + assertEquals("privateStaticMethod", (String) mh.invoke()); + assertEquals("privateStaticMethod", (String) mh.invokeExact()); + } + + public void testUnreflectSpecial_superCalls() throws Throwable { + Method overridenMethod = UnreflectTester.class.getMethod("overridenMethod"); + UnreflectTester instance = new UnreflectTester("unused"); + MethodHandle mh = UnreflectTester.lookup.unreflectSpecial(overridenMethod, + UnreflectTester.class); + assertEquals("Base", (String) mh.invoke(instance)); + } + + public void testUnreflects_constructors() throws Throwable { + Constructor privateConstructor = UnreflectTester.class.getDeclaredConstructor(String.class); + + try { + MethodHandles.lookup().unreflectConstructor(privateConstructor); + fail(); + } catch (IllegalAccessException expected) { + } + + privateConstructor.setAccessible(true); + MethodHandle mh = MethodHandles.lookup().unreflectConstructor(privateConstructor); + UnreflectTester instance = (UnreflectTester) mh.invokeExact("abc"); + assertEquals("abc", instance.publicField); + instance = (UnreflectTester) mh.invoke("def"); + assertEquals("def", instance.publicField); + Constructor publicConstructor = UnreflectTester.class.getConstructor(String.class, + boolean.class); + mh = MethodHandles.lookup().unreflectConstructor(publicConstructor); + instance = (UnreflectTester) mh.invokeExact("abc", false); + assertEquals("abc", instance.publicField); + instance = (UnreflectTester) mh.invoke("def", true); + assertEquals("def", instance.publicField); + } + + public void testUnreflects_publicFields() throws Throwable { + Field publicField = UnreflectTester.class.getField("publicField"); + MethodHandle mh = MethodHandles.lookup().unreflectGetter(publicField); + UnreflectTester instance = new UnreflectTester("instanceValue"); + assertEquals("instanceValue", (String) mh.invokeExact(instance)); + + mh = MethodHandles.lookup().unreflectSetter(publicField); + instance = new UnreflectTester("instanceValue"); + mh.invokeExact(instance, "updatedInstanceValue"); + assertEquals("updatedInstanceValue", instance.publicField); + + Field publicStaticField = UnreflectTester.class.getField("publicStaticField"); + mh = MethodHandles.lookup().unreflectGetter(publicStaticField); + UnreflectTester.publicStaticField = "updatedStaticValue"; + assertEquals("updatedStaticValue", (String) mh.invokeExact()); + + mh = MethodHandles.lookup().unreflectSetter(publicStaticField); + UnreflectTester.publicStaticField = "updatedStaticValue"; + mh.invokeExact("updatedStaticValue2"); + assertEquals("updatedStaticValue2", UnreflectTester.publicStaticField); + } + + public void testUnreflects_privateFields() throws Throwable { + Field privateField = UnreflectTester.class.getDeclaredField("privateField"); + try { + MethodHandles.lookup().unreflectGetter(privateField); + fail(); + } catch (IllegalAccessException expected) { + } + try { + MethodHandles.lookup().unreflectSetter(privateField); + fail(); + } catch (IllegalAccessException expected) { + } + + privateField.setAccessible(true); + + MethodHandle mh = MethodHandles.lookup().unreflectGetter(privateField); + UnreflectTester instance = new UnreflectTester("instanceValue"); + assertEquals("instanceValue", (String) mh.invokeExact(instance)); + + mh = MethodHandles.lookup().unreflectSetter(privateField); + instance = new UnreflectTester("instanceValue"); + mh.invokeExact(instance, "updatedInstanceValue"); + assertEquals("updatedInstanceValue", instance.privateField); + + Field privateStaticField = UnreflectTester.class.getDeclaredField("privateStaticField"); + try { + MethodHandles.lookup().unreflectGetter(privateStaticField); + fail(); + } catch (IllegalAccessException expected) { + } + try { + MethodHandles.lookup().unreflectSetter(privateStaticField); + fail(); + } catch (IllegalAccessException expected) { + } + + privateStaticField.setAccessible(true); + mh = MethodHandles.lookup().unreflectGetter(privateStaticField); + privateStaticField.set(null, "updatedStaticValue"); + assertEquals("updatedStaticValue", (String) mh.invokeExact()); + + mh = MethodHandles.lookup().unreflectSetter(privateStaticField); + privateStaticField.set(null, "updatedStaticValue"); + mh.invokeExact("updatedStaticValue2"); + assertEquals("updatedStaticValue2", (String) privateStaticField.get(null)); + } + + // This method only exists to fool Jack's handling of types. See b/32536744. + public static CharSequence getSequence() { + return "foo"; + } + + public void testAsType() throws Throwable { + // The type of this handle is (String, String)String. + MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, + "concat", MethodType.methodType(String.class, String.class)); + + // Change it to (CharSequence, String)Object. + MethodHandle asType = mh.asType( + MethodType.methodType(Object.class, CharSequence.class, String.class)); + + Object obj = asType.invokeExact((CharSequence) getSequence(), "bar"); + assertEquals("foobar", (String) obj); + + // Should fail due to a wrong return type. + try { + String str = (String) asType.invokeExact((CharSequence) getSequence(), "bar"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Should fail due to a wrong argument type (String instead of Charsequence). + try { + String str = (String) asType.invokeExact("baz", "bar"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Calls to asType should fail if the types are not convertible. + // + // Bad return type conversion. + try { + mh.asType(MethodType.methodType(int.class, String.class, String.class)); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Bad argument conversion. + try { + mh.asType(MethodType.methodType(String.class, int.class, String.class)); + fail(); + } catch (WrongMethodTypeException expected) { + } + } + + public void testConstructors() throws Throwable { + MethodHandle mh = + MethodHandles.lookup().findConstructor(Float.class, + MethodType.methodType(void.class, + float.class)); + Float value = (Float) mh.invokeExact(0.33f); + assertEquals(0.33f, value); + + value = (Float) mh.invoke(3.34f); + assertEquals(3.34f, value); + + mh = MethodHandles.lookup().findConstructor(Double.class, + MethodType.methodType(void.class, String.class)); + Double d = (Double) mh.invoke("8.45e3"); + assertEquals(8.45e3, d); + + mh = MethodHandles.lookup().findConstructor(Double.class, + MethodType.methodType(void.class, double.class)); + d = (Double) mh.invoke(8.45e3); + assertEquals(8.45e3, d); + + // Primitive type + try { + mh = MethodHandles.lookup().findConstructor(int.class, MethodType.methodType(void.class)); + fail("Unexpected lookup success for primitive constructor"); + } catch (NoSuchMethodException expected) { + } + + // Interface + try { + mh = MethodHandles.lookup().findConstructor(Readable.class, + MethodType.methodType(void.class)); + fail("Unexpected lookup success for interface constructor"); + } catch (NoSuchMethodException expected) { + } + + // Abstract + mh = MethodHandles.lookup().findConstructor(Process.class, MethodType.methodType(void.class)); + try { + mh.invoke(); + fail("Unexpected ability to instantiate an abstract class"); + } catch (InstantiationException expected) { + } + + // Non-existent + try { + MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(String.class, Float.class)); + fail("Unexpected success for non-existent constructor"); + } catch (NoSuchMethodException expected) { + } + + // Non-void constructor search. (I)I instead of (I)V. + try { + MethodHandles.lookup().findConstructor( + Integer.class, MethodType.methodType(Integer.class, Integer.class)); + fail("Unexpected success for non-void type for findConstructor"); + } catch (NoSuchMethodException expected) { + } + + // Array class constructor. + try { + MethodHandles.lookup().findConstructor( + Object[].class, MethodType.methodType(void.class)); + fail("Unexpected success for array class type for findConstructor"); + } catch (NoSuchMethodException expected) { + } + } + + public void testStringConstructors() throws Throwable { + final String testPattern = "The system as we know it is broken"; + + // String() + MethodHandle mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class)); + String s = (String) mh.invokeExact(); + assertEquals("", s); + + // String(String) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, String.class)); + s = (String) mh.invokeExact(testPattern); + assertEquals(testPattern, s); + + + // String(char[]) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, char[].class)); + s = (String) mh.invokeExact(testPattern.toCharArray()); + assertEquals(testPattern, s); + + // String(char[], int, int) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, char[].class, int.class, int.class)); + s = (String) mh.invokeExact(new char [] { 'a', 'b', 'c', 'd', 'e'}, 2, 3); + assertEquals("cde", s); + + // String(int[] codePoints, int offset, int count) + StringBuffer sb = new StringBuffer(testPattern); + int[] codePoints = new int[sb.codePointCount(0, sb.length())]; + for (int i = 0; i < sb.length(); ++i) { + codePoints[i] = sb.codePointAt(i); + } + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, int[].class, int.class, int.class)); + s = (String) mh.invokeExact(codePoints, 0, codePoints.length); + assertEquals(testPattern, s); + + // String(byte ascii[], int hibyte, int offset, int count) + byte [] ascii = testPattern.getBytes(StandardCharsets.US_ASCII); + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, byte[].class, int.class, int.class)); + s = (String) mh.invokeExact(ascii, 0, ascii.length); + assertEquals(testPattern, s); + + // String(byte bytes[], int offset, int length, String charsetName) + mh = MethodHandles.lookup().findConstructor( + String.class, + MethodType.methodType(void.class, byte[].class, int.class, int.class, String.class)); + s = (String) mh.invokeExact(ascii, 0, 5, StandardCharsets.US_ASCII.name()); + assertEquals(testPattern.substring(0, 5), s); + + // String(byte bytes[], int offset, int length, Charset charset) + mh = MethodHandles.lookup().findConstructor( + String.class, + MethodType.methodType(void.class, byte[].class, int.class, int.class, Charset.class)); + s = (String) mh.invokeExact(ascii, 0, 5, StandardCharsets.US_ASCII); + assertEquals(testPattern.substring(0, 5), s); + + // String(byte bytes[], String charsetName) + mh = MethodHandles.lookup().findConstructor( + String.class, + MethodType.methodType(void.class, byte[].class, String.class)); + s = (String) mh.invokeExact(ascii, StandardCharsets.US_ASCII.name()); + assertEquals(testPattern, s); + + // String(byte bytes[], Charset charset) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, byte[].class, Charset.class)); + s = (String) mh.invokeExact(ascii, StandardCharsets.US_ASCII); + assertEquals(testPattern, s); + + // String(byte bytes[], int offset, int length) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, byte[].class, int.class, int.class)); + s = (String) mh.invokeExact(ascii, 1, ascii.length - 2); + s = testPattern.charAt(0) + s + testPattern.charAt(testPattern.length() - 1); + assertEquals(testPattern, s); + + // String(byte bytes[]) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, byte[].class)); + s = (String) mh.invokeExact(ascii); + assertEquals(testPattern, s); + + // String(StringBuffer buffer) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, StringBuffer.class)); + s = (String) mh.invokeExact(sb); + assertEquals(testPattern, s); + } + + public void testReferenceReturnValueConversions() throws Throwable { + MethodHandle mh = MethodHandles.lookup().findStatic( + Float.class, "valueOf", MethodType.methodType(Float.class, String.class)); + + // No conversion + Float f = (Float) mh.invokeExact("1.375"); + assertEquals(1.375f, f); + + f = (Float) mh.invoke("1.875"); + assertEquals(1.875f, f); + + // Bad conversion + try { + int i = (int) mh.invokeExact("7.77"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + try { + int i = (int) mh.invoke("7.77"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Assignment to super-class. + Number n = (Number) mh.invoke("1.11"); + try { + Number o = (Number) mh.invokeExact("1.11"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Assignment to widened boxed primitive class. + try { + Double u = (Double) mh.invoke("1.11"); + fail(); + } catch (ClassCastException expected) { + } + + try { + Double v = (Double) mh.invokeExact("1.11"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Unboxed + float p = (float) mh.invoke("1.11"); + assertEquals(1.11f, p); + + // Unboxed and widened + double d = (double) mh.invoke("2.5"); + assertEquals(2.5, d); + + // Interface + Comparable c = (Comparable) mh.invoke("2.125"); + assertEquals(0, c.compareTo(Float.valueOf(2.125f))); + } + + public void testPrimitiveReturnValueConversions() throws Throwable { + MethodHandle mh = MethodHandles.lookup().findStatic( + Math.class, "min", MethodType.methodType(int.class, int.class, int.class)); + + final int SMALL = -8972; + final int LARGE = 7932529; + + // No conversion + if ((int) mh.invokeExact(LARGE, SMALL) != SMALL) { + fail(); + } else if ((int) mh.invoke(LARGE, SMALL) != SMALL) { + fail(); + } else if ((int) mh.invokeExact(SMALL, LARGE) != SMALL) { + fail(); + } else if ((int) mh.invoke(SMALL, LARGE) != SMALL) { + fail(); + } + + // int -> long + try { + long l = (long) mh.invokeExact(LARGE, SMALL); + fail(); + } catch (WrongMethodTypeException expected) { + } + + assertEquals((long) SMALL, (long) mh.invoke(LARGE, SMALL)); + + // int -> short + try { + short s = (short) mh.invokeExact(LARGE, SMALL); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // int -> Integer + try { + Integer i = (Integer) mh.invokeExact(LARGE, SMALL); + fail(); + } catch (WrongMethodTypeException expected) { + } + + assertEquals(Integer.valueOf(SMALL), (Integer) mh.invoke(LARGE, SMALL)); + + // int -> Long + try { + Long l = (Long) mh.invokeExact(LARGE, SMALL); + fail(); + } catch (WrongMethodTypeException expected) { + } + + try { + Long l = (Long) mh.invoke(LARGE, SMALL); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // int -> Short + try { + Short s = (Short) mh.invokeExact(LARGE, SMALL); + fail(); + } catch (WrongMethodTypeException expected) { + } + + try { + Short s = (Short) mh.invoke(LARGE, SMALL); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // int -> Process + try { + Process p = (Process) mh.invokeExact(LARGE, SMALL); + fail(); + } catch (WrongMethodTypeException expected) { + } + + try { + Process p = (Process) mh.invoke(LARGE, SMALL); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // void -> Object + mh = MethodHandles.lookup().findStatic(System.class, "gc", MethodType.methodType(void.class)); + Object o = (Object) mh.invoke(); + assertNull(o); + + // void -> long + long l = (long) mh.invoke(); + assertEquals(0, l); + + // boolean -> Boolean + mh = MethodHandles.lookup().findStatic(Boolean.class, "parseBoolean", + MethodType.methodType(boolean.class, String.class)); + Boolean z = (Boolean) mh.invoke("True"); + assertTrue(z); + + // boolean -> int + try { + int dummy = (int) mh.invoke("True"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // boolean -> Integer + try { + Integer dummy = (Integer) mh.invoke("True"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Boolean -> boolean + mh = MethodHandles.lookup().findStatic(Boolean.class, "valueOf", + MethodType.methodType(Boolean.class, boolean.class)); + boolean w = (boolean) mh.invoke(false); + assertFalse(w); + + // Boolean -> int + try { + int dummy = (int) mh.invoke(false); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Boolean -> Integer + try { + Integer dummy = (Integer) mh.invoke("True"); + fail(); + } catch (WrongMethodTypeException expected) { + } + } + + public static class BaseVariableArityTester { + public String update(Float f0, Float... floats) { + return "base " + f0 + ", " + Arrays.toString(floats); + } + } + + public static class VariableArityTester extends BaseVariableArityTester { + private String lastResult; + + // Constructors + public VariableArityTester() {} + public VariableArityTester(boolean... booleans) { update(booleans); } + public VariableArityTester(byte... bytes) { update(bytes); } + public VariableArityTester(char... chars) { update(chars); } + public VariableArityTester(short... shorts) { update(shorts); } + public VariableArityTester(int... ints) { update(ints); } + public VariableArityTester(long... longs) { update(longs); } + public VariableArityTester(float... floats) { update(floats); } + public VariableArityTester(double... doubles) { update(doubles); } + public VariableArityTester(Float f0, Float... floats) { update(f0, floats); } + public VariableArityTester(String s0, String... strings) { update(s0, strings); } + public VariableArityTester(char c, Number... numbers) { update(c, numbers); } + @SafeVarargs + public VariableArityTester(ArrayList l0, ArrayList... lists) { + update(l0, lists); + } + public VariableArityTester(List l0, List... lists) { update(l0, lists); } + + // Methods + public String update(boolean... booleans) { return lastResult = tally(booleans); } + public String update(byte... bytes) { return lastResult = tally(bytes); } + public String update(char... chars) { return lastResult = tally(chars); } + public String update(short... shorts) { return lastResult = tally(shorts); } + public String update(int... ints) { + lastResult = tally(ints); + return lastResult; + } + public String update(long... longs) { return lastResult = tally(longs); } + public String update(float... floats) { return lastResult = tally(floats); } + public String update(double... doubles) { return lastResult = tally(doubles); } + @Override + public String update(Float f0, Float... floats) { return lastResult = tally(f0, floats); } + public String update(String s0, String... strings) { return lastResult = tally(s0, strings); } + public String update(char c, Number... numbers) { return lastResult = tally(c, numbers); } + @SafeVarargs + public final String update(ArrayList l0, ArrayList... lists) { + lastResult = tally(l0, lists); + return lastResult; + } + public String update(List l0, List... lists) { return lastResult = tally(l0, lists); } + + public String arrayMethod(Object[] o) { + return Arrays.deepToString(o); + } + + public String lastResult() { return lastResult; } + + // Static Methods + public static String tally(boolean... booleans) { return Arrays.toString(booleans); } + public static String tally(byte... bytes) { return Arrays.toString(bytes); } + public static String tally(char... chars) { return Arrays.toString(chars); } + public static String tally(short... shorts) { return Arrays.toString(shorts); } + public static String tally(int... ints) { return Arrays.toString(ints); } + public static String tally(long... longs) { return Arrays.toString(longs); } + public static String tally(float... floats) { return Arrays.toString(floats); } + public static String tally(double... doubles) { return Arrays.toString(doubles); } + public static String tally(Float f0, Float... floats) { + return f0 + ", " + Arrays.toString(floats); + } + public static String tally(String s0, String... strings) { + return s0 + ", " + Arrays.toString(strings); + } + public static String tally(char c, Number... numbers) { + return c + ", " + Arrays.toString(numbers); + } + @SafeVarargs + public static String tally(ArrayList l0, ArrayList... lists) { + return Arrays.toString(l0.toArray()) + ", " + Arrays.deepToString(lists); + } + public static String tally(List l0, List... lists) { + return Arrays.deepToString(l0.toArray()) + ", " + Arrays.deepToString(lists); + } + public static void foo(int... ints) {} + public static long sumToPrimitive(int... ints) { + long result = 0; + for (int i : ints) result += i; + return result; + } + public static Long sumToReference(int... ints) { + return new Long(sumToPrimitive(ints)); + } + public static MethodHandles.Lookup lookup() { + return MethodHandles.lookup(); + } + } + + // This method only exists to fool Jack's handling of types. See b/32536744. + public static Object getAsObject(String[] strings) { + return (Object) strings; + } + + public void testVariableArity_boolean() throws Throwable { + MethodHandle mh; + VariableArityTester vat = new VariableArityTester(); + + assertEquals("[1]", vat.update(1)); + assertEquals("[1, 1]", vat.update(1, 1)); + assertEquals("[1, 1, 1]", vat.update(1, 1, 1)); + + // Methods - boolean + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, boolean[].class)); + assertTrue(mh.isVarargsCollector()); + assertFalse(mh.asFixedArity().isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[true, false, true]", mh.invoke(vat, true, false, true)); + assertEquals("[true, false, true]", mh.invoke(vat, new boolean[]{true, false, true})); + assertEquals("[false, true]", mh.invoke(vat, Boolean.valueOf(false), Boolean.valueOf(true))); + try { + mh.invoke(vat, true, true, 0); + fail(); + } catch (WrongMethodTypeException e) { + } + try { + assertEquals("[false, true]", mh.invoke(vat, Boolean.valueOf(false), (Boolean) null)); + fail(); + } catch (NullPointerException e) { + } + } + + public void testVariableArity_byte() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + + // Methods - byte + MethodHandle mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, byte[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[32, 64, 97]", mh.invoke(vat, (byte) 32, Byte.valueOf((byte) 64), (byte) 97)); + assertEquals("[32, 64, 97]", mh.invoke(vat, new byte[]{(byte) 32, (byte) 64, (byte) 97})); + try { + mh.invoke(vat, (byte) 1, Integer.valueOf(3), (byte) 0); + fail(); + } catch (WrongMethodTypeException e) { + } + } + + public void testVariableArity_char() throws Throwable { + MethodHandle mh; + VariableArityTester vat = new VariableArityTester(); + + // Methods - char + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, char[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[A, B, C]", mh.invoke(vat, 'A', Character.valueOf('B'), 'C')); + assertEquals("[W, X, Y, Z]", mh.invoke(vat, new char[]{'W', 'X', 'Y', 'Z'})); + } + + public void testVariableArity_short() throws Throwable { + MethodHandle mh; + VariableArityTester vat = new VariableArityTester(); + + // Methods - short + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, short[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[32767, -32768, 0]", + mh.invoke(vat, Short.MAX_VALUE, Short.MIN_VALUE, Short.valueOf((short) 0))); + assertEquals("[1, -1]", mh.invoke(vat, new short[]{(short) 1, (short) -1})); + } + + public void testVariableArity_int() throws Throwable { + MethodHandle mh; + VariableArityTester vat = new VariableArityTester(); + + // Methods - int + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, int[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[0, 2147483647, -2147483648, 0]", + mh.invoke(vat, Integer.valueOf(0), Integer.MAX_VALUE, Integer.MIN_VALUE, 0)); + assertEquals("[0, -1, 1, 0]", mh.invoke(vat, new int[]{0, -1, 1, 0})); + + assertEquals("[5, 4, 3, 2, 1]", (String) mh.invokeExact(vat, new int[]{5, 4, 3, 2, 1})); + try { + assertEquals("[5, 4, 3, 2, 1]", (String) mh.invokeExact(vat, 5, 4, 3, 2, 1)); + fail(); + } catch (WrongMethodTypeException expected) { + } + assertEquals("[5, 4, 3, 2, 1]", (String) mh.invoke(vat, 5, 4, 3, 2, 1)); + } + + public void testVariableArity_long() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + + // Methods - long + MethodHandle mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, long[].class)); + + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[0, 9223372036854775807, -9223372036854775808]", + mh.invoke(vat, Long.valueOf(0), Long.MAX_VALUE, Long.MIN_VALUE)); + assertEquals("[0, -1, 1, 0]", mh.invoke(vat, new long[]{0, -1, 1, 0})); + } + + public void testVariableArity_float() throws Throwable { + MethodHandle mh; + VariableArityTester vat = new VariableArityTester(); + + // Methods - float + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, float[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[0.0, 1.25, -1.25]", + mh.invoke(vat, 0.0f, Float.valueOf(1.25f), Float.valueOf(-1.25f))); + assertEquals("[0.0, -1.0, 1.0, 0.0]", + mh.invoke(vat, new float[]{0.0f, -1.0f, 1.0f, 0.0f})); + } + + public void testVariableArity_double() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + + // Methods - double + MethodHandle mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, double[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[0.0, 1.25, -1.25]", + mh.invoke(vat, 0.0, Double.valueOf(1.25), Double.valueOf(-1.25))); + assertEquals("[0.0, -1.0, 1.0, 0.0]", + mh.invoke(vat, new double[]{0.0, -1.0, 1.0, 0.0})); + mh.invoke(vat, 0.3f, 1.33, 1.33); + } + + public void testVariableArity_String() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + + // Methods - String + MethodHandle mh = MethodHandles.lookup(). + findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, String.class, String[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("Echidna, []", mh.invoke(vat, "Echidna")); + assertEquals("Bongo, [Jerboa, Okapi]", + mh.invoke(vat, "Bongo", "Jerboa", "Okapi")); + } + + public void testVariableArity_Float() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + + // Methods - Float + MethodHandle mh = MethodHandles.lookup(). + findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, Float.class, Float[].class)); + + assertTrue(mh.isVarargsCollector()); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(vat, + Float.valueOf(9.99f), + new Float[]{ Float.valueOf(0.0f), Float.valueOf(0.1f), Float.valueOf(1.1f)})); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(vat, Float.valueOf(9.99f), Float.valueOf(0.0f), + Float.valueOf(0.1f), Float.valueOf(1.1f))); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(vat, Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f)); + try { + assertEquals("9.99, [77.0, 33.0, 64.0]", + (String) mh.invoke(vat, Float.valueOf(9.99f), 77, 33, 64)); + fail(); + } catch (WrongMethodTypeException expected) { + } + + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invokeExact(vat, Float.valueOf(9.99f), + new Float[]{Float.valueOf(0.0f), + Float.valueOf(0.1f), + Float.valueOf(1.1f)})); + assertEquals("9.99, [0.0, null, 1.1]", + (String) mh.invokeExact(vat, Float.valueOf(9.99f), + new Float[]{Float.valueOf(0.0f), + null, + Float.valueOf(1.1f)})); + try { + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invokeExact(vat, Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f)); + fail(); + } catch (WrongMethodTypeException expected) { + } + } + + public void testVariableArity_Number() throws Throwable { + MethodHandle mh; + VariableArityTester vat = new VariableArityTester(); + + // Methods - Number + mh = MethodHandles.lookup(). + findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, char.class, Number[].class)); + assertTrue(mh.isVarargsCollector()); + assertFalse(mh.asFixedArity().isVarargsCollector()); + assertEquals("x, []", (String) mh.invoke(vat, 'x')); + assertEquals("x, [3.141]", (String) mh.invoke(vat, 'x', 3.141)); + assertEquals("x, [null, 3.131, 37]", + (String) mh.invoke(vat, 'x', null, 3.131, new Integer(37))); + try { + assertEquals("x, [null, 3.131, bad, 37]", + (String) mh.invoke(vat, 'x', null, 3.131, "bad", new Integer(37))); + assertTrue(false); + fail(); + } catch (ClassCastException e) { + } + try { + assertEquals("x, [null, 3.131, bad, 37]", + (String) mh.invoke( + vat, 'x', (Process) null, 3.131, "bad", new Integer(37))); + assertTrue(false); + fail(); + } catch (ClassCastException e) { + } + } + + public void testVariableArity_arrayMethod() throws Throwable { + MethodHandle mh; + VariableArityTester vat = new VariableArityTester(); + + // Methods - an array method that is not variable arity. + mh = MethodHandles.lookup().findVirtual( + VariableArityTester.class, "arrayMethod", + MethodType.methodType(String.class, Object[].class)); + assertFalse(mh.isVarargsCollector()); + mh.invoke(vat, new Object[]{"123"}); + try { + assertEquals("-", mh.invoke(vat, new Float(3), new Float(4))); + fail(); + } catch (WrongMethodTypeException e) { + } + mh = mh.asVarargsCollector(Object[].class); + assertTrue(mh.isVarargsCollector()); + assertEquals("[3.0, 4.0]", (String) mh.invoke(vat, new Float(3), new Float(4))); + } + + public void testVariableArity_booleanConstructors() throws Throwable { + // Constructors - default + MethodHandle mh = MethodHandles.lookup().findConstructor( + VariableArityTester.class, MethodType.methodType(void.class)); + assertFalse(mh.isVarargsCollector()); + + // Constructors - boolean + mh = MethodHandles.lookup().findConstructor( + VariableArityTester.class, MethodType.methodType(void.class, boolean[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[true, true, false]", + ((VariableArityTester) mh.invoke(new boolean[]{true, true, false})).lastResult()); + assertEquals("[true, true, false]", + ((VariableArityTester) mh.invoke(true, true, false)).lastResult()); + try { + assertEquals("[true, true, false]", + ((VariableArityTester) mh.invokeExact(true, true, false)).lastResult()); + fail(); + } catch (WrongMethodTypeException expected) { + } + } + + public void testVariableArity_byteConstructors() throws Throwable { + // Constructors - byte + MethodHandle mh = MethodHandles.lookup().findConstructor( + VariableArityTester.class, MethodType.methodType(void.class, byte[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[55, 66, 60]", + ((VariableArityTester) + mh.invoke(new byte[]{(byte) 55, (byte) 66, (byte) 60})).lastResult()); + assertEquals("[55, 66, 60]", + ((VariableArityTester) mh.invoke( + (byte) 55, (byte) 66, (byte) 60)).lastResult()); + try { + assertEquals("[55, 66, 60]", + ((VariableArityTester) mh.invokeExact( + (byte) 55, (byte) 66, (byte) 60)).lastResult()); + fail(); + } catch (WrongMethodTypeException expected) { + } + try { + assertEquals("[3, 3]", + ((VariableArityTester) mh.invoke( + new Number[]{Byte.valueOf((byte) 3), (byte) 3})).lastResult()); + fail(); + } catch (WrongMethodTypeException expected) { + } + } + + public void testVariableArity_stringConstructors() throws Throwable { + // Constructors - String (have a different path than other reference types). + MethodHandle mh = MethodHandles.lookup().findConstructor( + VariableArityTester.class, + MethodType.methodType(void.class, String.class, String[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("x, []", ((VariableArityTester) mh.invoke("x")).lastResult()); + assertEquals("x, [y]", ((VariableArityTester) mh.invoke("x", "y")).lastResult()); + assertEquals("x, [y, z]", + ((VariableArityTester) mh.invoke("x", new String[]{"y", "z"})).lastResult()); + try { + assertEquals("x, [y]", ((VariableArityTester) mh.invokeExact("x", "y")).lastResult()); + fail(); + } catch (WrongMethodTypeException expected) { + } + assertEquals("x, [null, z]", + ((VariableArityTester) mh.invoke("x", new String[]{null, "z"})).lastResult()); + } + + public void testVariableArity_numberConstructors() throws Throwable { + // Constructors - Number + MethodHandle mh = MethodHandles.lookup().findConstructor( + VariableArityTester.class, MethodType.methodType(void.class, char.class, Number[].class)); + assertTrue(mh.isVarargsCollector()); + assertFalse(mh.asFixedArity().isVarargsCollector()); + assertEquals("x, []", ((VariableArityTester) mh.invoke('x')).lastResult()); + assertEquals("x, [3.141]", ((VariableArityTester) mh.invoke('x', 3.141)).lastResult()); + assertEquals("x, [null, 3.131, 37]", + ((VariableArityTester) mh.invoke('x', null, 3.131, new Integer(37))).lastResult()); + try { + assertEquals("x, [null, 3.131, bad, 37]", + ((VariableArityTester) mh.invoke( + 'x', null, 3.131, "bad", new Integer(37))).lastResult()); + fail(); + } catch (ClassCastException expected) { + } + try { + assertEquals("x, [null, 3.131, bad, 37]", + ((VariableArityTester) mh.invoke( + 'x', (Process) null, 3.131, "bad", new Integer(37))).lastResult()); + fail(); + } catch (ClassCastException expected) { + } + } + + public void testVariableArity_floatConstructors() throws Throwable { + // Static Methods - Float + MethodHandle mh = MethodHandles.lookup(). + findStatic(VariableArityTester.class, "tally", + MethodType.methodType(String.class, Float.class, Float[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(Float.valueOf(9.99f), + new Float[]{Float.valueOf(0.0f), + Float.valueOf(0.1f), + Float.valueOf(1.1f)})); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(Float.valueOf(9.99f), Float.valueOf(0.0f), + Float.valueOf(0.1f), Float.valueOf(1.1f))); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f)); + try { + assertEquals("9.99, [77.0, 33.0, 64.0]", + (String) mh.invoke(Float.valueOf(9.99f), 77, 33, 64)); + fail(); + } catch (WrongMethodTypeException expected) { + } + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invokeExact(Float.valueOf(9.99f), + new Float[]{Float.valueOf(0.0f), + Float.valueOf(0.1f), + Float.valueOf(1.1f)})); + assertEquals("9.99, [0.0, null, 1.1]", + (String) mh.invokeExact(Float.valueOf(9.99f), + new Float[]{Float.valueOf(0.0f), + null, + Float.valueOf(1.1f)})); + try { + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invokeExact(Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f)); + fail(); + } catch (WrongMethodTypeException expected) { + } + } + + public void testVariableArity_specialMethods() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + + // Special methods - Float + MethodHandle mh = VariableArityTester.lookup(). + findSpecial(BaseVariableArityTester.class, "update", + MethodType.methodType(String.class, Float.class, Float[].class), + VariableArityTester.class); + assertTrue(mh.isVarargsCollector()); + assertEquals("base 9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(vat, + Float.valueOf(9.99f), + new Float[]{Float.valueOf(0.0f), + Float.valueOf(0.1f), + Float.valueOf(1.1f)})); + assertEquals("base 9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(vat, Float.valueOf(9.99f), Float.valueOf(0.0f), + Float.valueOf(0.1f), Float.valueOf(1.1f))); + } + + public void testVariableArity_returnValueConversions() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + + // Return value conversions. + MethodHandle mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, int[].class)); + assertEquals("[1, 2, 3]", (String) mh.invoke(vat, 1, 2, 3)); + assertEquals("[1, 2, 3]", (Object) mh.invoke(vat, 1, 2, 3)); + try { + assertEquals("[1, 2, 3, 4]", (long) mh.invoke(vat, 1, 2, 3)); + fail(); + } catch (WrongMethodTypeException expected) { + } + assertEquals("[1, 2, 3]", vat.lastResult()); + mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "sumToPrimitive", + MethodType.methodType(long.class, int[].class)); + assertEquals(10l, (long) mh.invoke(1, 2, 3, 4)); + assertEquals(Long.valueOf(10l), (Long) mh.invoke(1, 2, 3, 4)); + mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "sumToReference", + MethodType.methodType(Long.class, int[].class)); + Object o = mh.invoke(1, 2, 3, 4); + long l = (long) mh.invoke(1, 2, 3, 4); + assertEquals(10l, (long) mh.invoke(1, 2, 3, 4)); + assertEquals(Long.valueOf(10l), (Long) mh.invoke(1, 2, 3, 4)); + try { + // WrongMethodTypeException should be raised before invoke here. + assertEquals(Long.valueOf(10l), (Byte) mh.invoke(1, 2, 3, 4)); + fail(); + } catch (ClassCastException expected) { + } + try { + // WrongMethodTypeException should be raised before invoke here. + byte b = (byte) mh.invoke(1, 2, 3, 4); + fail(); + } catch (WrongMethodTypeException expected) { + } + } + + public void testVariableArity_returnVoid() throws Throwable { + // Return void produces 0 / null. + MethodHandle mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "foo", + MethodType.methodType(void.class, int[].class)); + assertEquals(null, (Object) mh.invoke(3, 2, 1)); + assertEquals(0l, (long) mh.invoke(1, 2, 3)); + } + + public void testVariableArity_combinators() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + + // Combinators + MethodHandle mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, boolean[].class)); + assertTrue(mh.isVarargsCollector()); + mh = mh.bindTo(vat); + assertFalse(mh.isVarargsCollector()); + mh = mh.asVarargsCollector(boolean[].class); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke()); + assertEquals("[true, false, true]", mh.invoke(true, false, true)); + assertEquals("[true, false, true]", mh.invoke(new boolean[] { true, false, true})); + assertEquals("[false, true]", mh.invoke(Boolean.valueOf(false), Boolean.valueOf(true))); + try { + mh.invoke(true, true, 0); + fail(); + } catch (WrongMethodTypeException e) {} + } + + // The same tests as the above, except that we use use MethodHandles.bind instead of + // MethodHandle.bindTo. + public void testVariableArity_MethodHandles_bind() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + MethodHandle mh = MethodHandles.lookup().bind(vat, "update", + MethodType.methodType(String.class, boolean[].class)); + assertTrue(mh.isVarargsCollector()); + + assertEquals("[]", mh.invoke()); + assertEquals("[true, false, true]", mh.invoke(true, false, true)); + assertEquals("[true, false, true]", mh.invoke(new boolean[] { true, false, true})); + assertEquals("[false, true]", mh.invoke(Boolean.valueOf(false), Boolean.valueOf(true))); + + try { + mh.invoke(true, true, 0); + fail(); + } catch (WrongMethodTypeException e) {} + } + + public void testRevealDirect() throws Throwable { + // Test with a virtual method : + MethodType type = MethodType.methodType(String.class); + MethodHandle handle = MethodHandles.lookup().findVirtual( + UnreflectTester.class, "publicMethod", type); + + // Comparisons with an equivalent member obtained via reflection : + MethodHandleInfo info = MethodHandles.lookup().revealDirect(handle); + Method meth = UnreflectTester.class.getMethod("publicMethod"); + + assertEquals(MethodHandleInfo.REF_invokeVirtual, info.getReferenceKind()); + assertEquals("publicMethod", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Resolution via a public lookup should fail because the method in question + // isn't public. + try { + info.reflectAs(Method.class, MethodHandles.publicLookup()); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Test with a static method : + handle = MethodHandles.lookup().findStatic(UnreflectTester.class, + "publicStaticMethod", + MethodType.methodType(String.class)); + + info = MethodHandles.lookup().revealDirect(handle); + meth = UnreflectTester.class.getMethod("publicStaticMethod"); + assertEquals(MethodHandleInfo.REF_invokeStatic, info.getReferenceKind()); + assertEquals("publicStaticMethod", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Test with a var-args method : + type = MethodType.methodType(String.class, String[].class); + handle = MethodHandles.lookup().findVirtual(UnreflectTester.class, + "publicVarArgsMethod", type); + + info = MethodHandles.lookup().revealDirect(handle); + meth = UnreflectTester.class.getMethod("publicVarArgsMethod", String[].class); + assertEquals(MethodHandleInfo.REF_invokeVirtual, info.getReferenceKind()); + assertEquals("publicVarArgsMethod", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertTrue(info.isVarArgs()); + assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Test with a constructor : + Constructor cons = UnreflectTester.class.getConstructor(String.class, boolean.class); + type = MethodType.methodType(void.class, String.class, boolean.class); + handle = MethodHandles.lookup().findConstructor(UnreflectTester.class, type); + + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_newInvokeSpecial, info.getReferenceKind()); + assertEquals("", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(cons, info.reflectAs(Constructor.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Test with a static field : + Field field = UnreflectTester.class.getField("publicStaticField"); + + handle = MethodHandles.lookup().findStaticSetter( + UnreflectTester.class, "publicStaticField", String.class); + + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_putStatic, info.getReferenceKind()); + assertEquals("publicStaticField", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(void.class, String.class), info.getMethodType()); + + // Test with a setter on the same field, the type of the handle should change + // but everything else must remain the same. + handle = MethodHandles.lookup().findStaticGetter( + UnreflectTester.class, "publicStaticField", String.class); + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_getStatic, info.getReferenceKind()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(String.class), info.getMethodType()); + + // Test with an instance field : + field = UnreflectTester.class.getField("publicField"); + + handle = MethodHandles.lookup().findSetter( + UnreflectTester.class, "publicField", String.class); + + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_putField, info.getReferenceKind()); + assertEquals("publicField", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(void.class, String.class), info.getMethodType()); + + // Test with a setter on the same field, the type of the handle should change + // but everything else must remain the same. + handle = MethodHandles.lookup().findGetter( + UnreflectTester.class, "publicField", String.class); + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_getField, info.getReferenceKind()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(String.class), info.getMethodType()); + } + + public void testReflectAs() throws Throwable { + // Test with a virtual method : + MethodType type = MethodType.methodType(String.class); + MethodHandle handle = MethodHandles.lookup().findVirtual( + UnreflectTester.class, "publicMethod", type); + + Method reflected = MethodHandles.reflectAs(Method.class, handle); + Method meth = UnreflectTester.class.getMethod("publicMethod"); + assertEquals(meth, reflected); + + try { + MethodHandles.reflectAs(Field.class, handle); + fail(); + } catch (ClassCastException expected) { + } + + try { + MethodHandles.reflectAs(Constructor.class, handle); + fail(); + } catch (ClassCastException expected) { + } + + // Test with a private instance method, unlike the "checked crack" (lol..) API exposed + // by revealDirect, this doesn't perform any access checks. + handle = UnreflectTester.lookup.findSpecial( + UnreflectTester.class, "privateMethod", type, UnreflectTester.class); + meth = UnreflectTester.class.getDeclaredMethod("privateMethod"); + reflected = MethodHandles.reflectAs(Method.class, handle); + assertEquals(meth, reflected); + + // Test with a constructor : + type = MethodType.methodType(void.class, String.class, boolean.class); + handle = MethodHandles.lookup().findConstructor(UnreflectTester.class, type); + + Constructor cons = UnreflectTester.class.getConstructor(String.class, boolean.class); + Constructor reflectedCons = MethodHandles.reflectAs(Constructor.class, handle); + assertEquals(cons, reflectedCons); + + try { + MethodHandles.reflectAs(Method.class, handle); + fail(); + } catch (ClassCastException expected) { + } + + // Test with an instance field : + handle = MethodHandles.lookup().findSetter( + UnreflectTester.class, "publicField", String.class); + + Field field = UnreflectTester.class.getField("publicField"); + Field reflectedField = MethodHandles.reflectAs(Field.class, handle); + assertEquals(field, reflectedField); + + try { + MethodHandles.reflectAs(Method.class, handle); + fail(); + } catch (ClassCastException expected) { + } + + // Test with a non-direct method handle. + try { + MethodHandles.reflectAs(Method.class, MethodHandles.constant(String.class, "foo")); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public static class Inner1 { + public static MethodHandles.Lookup lookup = MethodHandles.lookup(); + } + + public static class Inner2 { + } + + private static void privateStaticMethod() {} + public static void publicStaticMethod() {} + static void packagePrivateStaticMethod() {} + protected static void protectedStaticMethod() {} + + public void publicMethod() {} + private void privateMethod() {} + void packagePrivateMethod() {} + protected void protectedMethod() {} + + public static class ConstructorTest { + ConstructorTest(String unused, int unused2) {} + protected ConstructorTest(String unused) {} + private ConstructorTest(String unused, char unused2) {} + } +} + +class PackageSibling { + public void publicMethod() {} + public static void publicStaticMethod() {} + + protected PackageSibling(String unused) {} + public PackageSibling(String unused, char unused2) {} +} + diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodTypeTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodTypeTest.java new file mode 100644 index 000000000..f83384012 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/invoke/MethodTypeTest.java @@ -0,0 +1,690 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.lang.invoke; + +import junit.framework.TestCase; + +import java.lang.invoke.MethodType; +import java.util.Arrays; +import java.util.List; + +public class MethodTypeTest extends TestCase { + private static final Class[] LARGE_PARAMETER_ARRAY; + + static { + LARGE_PARAMETER_ARRAY = new Class[254]; + for (int i = 0; i < 254; ++i) { + LARGE_PARAMETER_ARRAY[i] = Object.class; + } + } + + public void test_methodType_basicTestsReturnTypeAndParameterClassArray() { + MethodType mt = MethodType.methodType(int.class, + new Class[] { String.class, long.class}); + + assertEquals(int.class, mt.returnType()); + assertParameterTypes(mt, String.class, long.class); + + try { + MethodType.methodType(null, new Class[] { String.class }); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodType.methodType(int.class, (Class[]) null); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodType.methodType(int.class, new Class[] {void.class}); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void test_methodType_basicTestsReturnTypeAndParameterClassList() { + MethodType mt = MethodType.methodType(int.class, Arrays.asList(String.class, long.class)); + + assertEquals(int.class, mt.returnType()); + assertParameterTypes(mt, String.class, long.class); + + try { + MethodType.methodType(null, Arrays.asList(String.class)); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodType.methodType(int.class, (List>) null); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodType.methodType(int.class, Arrays.asList(void.class)); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void test_methodType_basicTestsReturnTypeAndVarargsParameters() { + MethodType mt = MethodType.methodType(int.class, String.class, long.class); + + assertEquals(int.class, mt.returnType()); + assertParameterTypes(mt, String.class, long.class); + + try { + MethodType.methodType(null, String.class); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodType.methodType(int.class, String.class, null); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodType.methodType(int.class, void.class, String.class); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void test_methodType_basicTestsReturnTypeOnly() { + MethodType mt = MethodType.methodType(int.class); + + assertEquals(int.class, mt.returnType()); + assertEquals(0, mt.parameterCount()); + + try { + MethodType.methodType(null); + fail(); + } catch (NullPointerException expected) { + } + } + + public void test_methodType_basicTestsReturnTypeAndSingleParameter() { + MethodType mt = MethodType.methodType(int.class, long.class); + + assertEquals(int.class, mt.returnType()); + assertParameterTypes(mt, long.class); + + try { + MethodType.methodType(null); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodType.methodType(null, String.class); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodType.methodType(int.class, (Class) null); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodType.methodType(int.class, void.class); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void test_methodType_basicTestsReturnTypeAndMethodTypeParameters() { + MethodType mt = MethodType.methodType(int.class, long.class, String.class); + assertEquals(int.class, mt.returnType()); + + MethodType mt2 = MethodType.methodType(long.class, mt); + + assertEquals(long.class, mt2.returnType()); + assertEquals(long.class, mt2.parameterType(0)); + assertEquals(String.class, mt2.parameterType(1)); + + try { + MethodType.methodType(int.class, (MethodType) null); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testGenericMethodType() { + MethodType mt = MethodType.genericMethodType(0); + assertEquals(0, mt.parameterCount()); + assertEquals(Object.class, mt.returnType()); + + mt = MethodType.genericMethodType(3); + assertEquals(Object.class, mt.returnType()); + + assertEquals(3, mt.parameterCount()); + assertParameterTypes(mt, Object.class, Object.class, Object.class); + + try { + MethodType.genericMethodType(-1); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + MethodType.genericMethodType(256); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testGenericMethodTypeWithTrailingArray() { + MethodType mt = MethodType.genericMethodType(3, false /* finalArray */); + assertEquals(Object.class, mt.returnType()); + assertParameterTypes(mt, Object.class, Object.class, Object.class); + + mt = MethodType.genericMethodType(0, true /* finalArray */); + assertEquals(Object.class, mt.returnType()); + assertParameterTypes(mt, Object[].class); + + mt = MethodType.genericMethodType(2, true /* finalArray */); + assertEquals(Object.class, mt.returnType()); + assertParameterTypes(mt, Object.class, Object.class, Object[].class); + + try { + MethodType.genericMethodType(-1, true); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + MethodType.genericMethodType(255, true); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testChangeParameterType() { + // int method(String, Object, List); + MethodType mt = MethodType.methodType(int.class, String.class, Object.class, List.class); + assertEquals(Object.class, mt.parameterType(1)); + + MethodType changed = mt.changeParameterType(1, String.class); + assertEquals(String.class, changed.parameterType(1)); + + // Assert that the return types and the other parameter types haven't changed. + assertEquals(mt.parameterCount(), changed.parameterCount()); + assertEquals(mt.returnType(), changed.returnType()); + assertEquals(mt.parameterType(0), changed.parameterType(0)); + assertEquals(mt.parameterType(2), changed.parameterType(2)); + + try { + mt.changeParameterType(-1, String.class); + fail(); + } catch (ArrayIndexOutOfBoundsException expected) { + } + + try { + mt.changeParameterType(3, String.class); + fail(); + } catch (ArrayIndexOutOfBoundsException expected) { + } + + try { + mt.changeParameterType(1, void.class); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + mt.changeParameterType(1, null); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testInsertParameterTypes_varargs() { + MethodType mt = MethodType.methodType(int.class, String.class, Object.class); + + MethodType insert0 = mt.insertParameterTypes(0, Integer.class, Long.class); + assertEquals(int.class, insert0.returnType()); + assertParameterTypes(insert0, Integer.class, Long.class, String.class, Object.class); + + MethodType insert1 = mt.insertParameterTypes(1, Integer.class, Long.class); + assertParameterTypes(insert1, String.class, Integer.class, Long.class, Object.class); + + MethodType insert2 = mt.insertParameterTypes(2, Integer.class, Long.class); + assertParameterTypes(insert2, String.class, Object.class, Integer.class, Long.class); + + try { + mt.insertParameterTypes(1, LARGE_PARAMETER_ARRAY); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + mt.insertParameterTypes(1, void.class); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + mt.insertParameterTypes(1, (Class) null); + fail(); + } catch (NullPointerException expected) { + } + + try { + mt.insertParameterTypes(-1, String.class); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + + try { + mt.insertParameterTypes(3, String.class); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + } + + public void testInsertParameterTypes_list() { + MethodType mt = MethodType.methodType(int.class, String.class, Object.class); + + MethodType insert0 = mt.insertParameterTypes(0, Arrays.asList(Integer.class, Long.class)); + assertEquals(int.class, insert0.returnType()); + assertParameterTypes(insert0, Integer.class, Long.class, String.class, Object.class); + + MethodType insert1 = mt.insertParameterTypes(1, Arrays.asList(Integer.class, Long.class)); + assertParameterTypes(insert1, String.class, Integer.class, Long.class, Object.class); + + MethodType insert2 = mt.insertParameterTypes(2, Arrays.asList(Integer.class, Long.class)); + assertParameterTypes(insert2, String.class, Object.class, Integer.class, Long.class); + + try { + mt.insertParameterTypes(1, Arrays.asList(LARGE_PARAMETER_ARRAY)); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + mt.insertParameterTypes(1, Arrays.asList(void.class)); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + mt.insertParameterTypes(1, (List>) null); + fail(); + } catch (NullPointerException expected) { + } + + try { + mt.insertParameterTypes(1, Arrays.asList(null)); + fail(); + } catch (NullPointerException expected) { + } + + try { + mt.insertParameterTypes(-1, Arrays.asList(String.class)); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + + try { + mt.insertParameterTypes(3, Arrays.asList(String.class)); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + } + + public void testAppendParameterTypes_varargs() { + MethodType mt = MethodType.methodType(int.class, String.class, String.class); + + MethodType appended = mt.appendParameterTypes(List.class, Integer.class); + assertEquals(int.class, appended.returnType()); + assertParameterTypes(appended, String.class, String.class, List.class, Integer.class); + + try { + mt.appendParameterTypes(LARGE_PARAMETER_ARRAY); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + mt.appendParameterTypes(void.class); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + mt.appendParameterTypes((Class) null); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testAppendParameterTypes_list() { + MethodType mt = MethodType.methodType(int.class, String.class, String.class); + + MethodType appended = mt.appendParameterTypes(Arrays.asList(List.class, Integer.class)); + assertEquals(int.class, appended.returnType()); + assertParameterTypes(appended, String.class, String.class, List.class, Integer.class); + + try { + mt.appendParameterTypes(Arrays.asList(LARGE_PARAMETER_ARRAY)); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + mt.appendParameterTypes(Arrays.asList(void.class)); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + mt.appendParameterTypes((List>) null); + fail(); + } catch (NullPointerException expected) { + } + + try { + mt.appendParameterTypes(Arrays.asList(null)); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testDropParameterTypes() { + MethodType mt = MethodType.methodType(int.class, String.class, List.class, Object.class); + + MethodType dropNone = mt.dropParameterTypes(0, 0); + assertEquals(int.class, dropNone.returnType()); + assertParameterTypes(dropNone, String.class, List.class, Object.class); + + MethodType dropFirst = mt.dropParameterTypes(0, 1); + assertEquals(int.class, dropFirst.returnType()); + assertParameterTypes(dropFirst, List.class, Object.class); + + MethodType dropAll = mt.dropParameterTypes(0, 3); + assertEquals(0, dropAll.parameterCount()); + assertEquals(int.class, dropAll.returnType()); + + try { + mt.dropParameterTypes(-1, 1); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + + try { + mt.dropParameterTypes(1, 4); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + + try { + mt.dropParameterTypes(2, 1); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + } + + public void testChangeReturnType() { + MethodType mt = MethodType.methodType(int.class, String.class); + + MethodType changed = mt.changeReturnType(long.class); + assertEquals(long.class, changed.returnType()); + assertParameterTypes(changed, String.class); + + try { + mt.changeReturnType(null); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testHasPrimitives() { + MethodType mt = MethodType.methodType(Integer.class, Object.class, String.class); + assertFalse(mt.hasPrimitives()); + + mt = MethodType.methodType(int.class, Object.class); + assertTrue(mt.hasPrimitives()); + + mt = MethodType.methodType(Integer.class, long.class); + assertTrue(mt.hasPrimitives()); + + mt = MethodType.methodType(Integer.class, int[].class); + assertFalse(mt.hasPrimitives()); + + mt = MethodType.methodType(void.class); + assertTrue(mt.hasPrimitives()); + } + + public void testHasWrappers() { + MethodType mt = MethodType.methodType(Integer.class); + assertTrue(mt.hasWrappers()); + + mt = MethodType.methodType(String.class, Integer.class); + assertTrue(mt.hasWrappers()); + + mt = MethodType.methodType(int.class, long.class); + assertFalse(mt.hasWrappers()); + } + + public void testErase() { + // String mt(int, String, Object) should be erased to Object mt(int, Object, Object); + MethodType mt = MethodType.methodType(String.class, int.class, String.class, Object.class); + + MethodType erased = mt.erase(); + assertEquals(Object.class, erased.returnType()); + assertParameterTypes(erased, int.class, Object.class, Object.class); + + // Void returns must be left alone. + mt = MethodType.methodType(void.class, int.class); + erased = mt.erase(); + assertEquals(mt, erased); + } + + public void testGeneric() { + // String mt(int, String, Object) should be generified to Object mt(Object, Object, Object). + // In other words, it must be equal to genericMethodType(3 /* parameterCount */); + MethodType mt = MethodType.methodType(String.class, int.class, String.class, Object.class); + + MethodType generic = mt.generic(); + + assertEquals(generic, MethodType.genericMethodType(mt.parameterCount())); + assertEquals(generic, mt.wrap().erase()); + + assertEquals(Object.class, generic.returnType()); + assertParameterTypes(generic, Object.class, Object.class, Object.class); + + // Primitive return types must also become Object. + generic = MethodType.methodType(int.class).generic(); + assertEquals(Object.class, generic.returnType()); + + // void returns get converted to object returns (the same as wrap). + generic = MethodType.methodType(void.class).generic(); + assertEquals(Object.class, generic.returnType()); + } + + public void testWrap() { + // int mt(String, int, long, float, double, short, char, byte) should be wrapped to + // Integer mt(String, Integer, Long, Float, Double, Short, Character, Byte); + MethodType mt = MethodType.methodType(int.class, String.class, int.class, long.class, + float.class, double.class, short.class, char.class, byte.class); + + MethodType wrapped = mt.wrap(); + assertFalse(wrapped.hasPrimitives()); + assertTrue(wrapped.hasWrappers()); + + assertEquals(Integer.class, wrapped.returnType()); + assertParameterTypes(wrapped, String.class, Integer.class, Long.class, Float.class, + Double.class, Short.class, Character.class, Byte.class); + + // (semi) special case - void return types get wrapped to Void. + wrapped = MethodType.methodType(void.class, int.class).wrap(); + assertEquals(Void.class, wrapped.returnType()); + } + + public void testUnwrap() { + // Integer mt(String, Integer, Long, Float, Double, Short, Character, Byte); + // should be unwrapped to : + // int mt(String, int, long, float, double, short, char, byte). + MethodType mt = MethodType.methodType(Integer.class, String.class, Integer.class, + Long.class, Float.class, Double.class, Short.class, Character.class, Byte.class); + + MethodType unwrapped = mt.unwrap(); + assertTrue(unwrapped.hasPrimitives()); + assertFalse(unwrapped.hasWrappers()); + + assertEquals(int.class, unwrapped.returnType()); + assertParameterTypes(unwrapped, String.class, int.class, long.class, float.class, + double.class, short.class, char.class, byte.class); + + // (semi) special case - void return types get wrapped to Void. + unwrapped = MethodType.methodType(Void.class, int.class).unwrap(); + assertEquals(void.class, unwrapped.returnType()); + } + + public void testParameterListAndArray() { + MethodType mt = MethodType.methodType(String.class, int.class, String.class, Object.class); + + List> paramsList = mt.parameterList(); + Class[] paramsArray = mt.parameterArray(); + + assertEquals(3, mt.parameterCount()); + + for (int i = 0; i < 3; ++i) { + Class param = mt.parameterType(i); + assertEquals(param, paramsList.get(i)); + assertEquals(param, paramsArray[i]); + } + + mt = MethodType.methodType(int.class); + assertEquals(0, mt.parameterCount()); + + paramsList = mt.parameterList(); + paramsArray = mt.parameterArray(); + + assertEquals(0, paramsList.size()); + assertEquals(0, paramsArray.length); + } + + public void testEquals() { + MethodType mt = MethodType.methodType(int.class, String.class); + MethodType mt2 = MethodType.methodType(int.class, String.class); + + assertEquals(mt, mt2); + assertEquals(mt, mt); + + assertFalse(mt.equals(null)); + assertFalse(mt.equals(MethodType.methodType(Integer.class, String.class))); + } + + public void testHashCode() { + MethodType mt = MethodType.methodType(int.class, String.class, Object.class); + int hashCode = mt.hashCode(); + + // The hash code should change if we change the return type or any of the parameters, + // or if we add or remove parameters from the list. + assertFalse(hashCode == mt.changeReturnType(long.class).hashCode()); + assertFalse(hashCode == mt.changeParameterType(0, Object.class).hashCode()); + assertFalse(hashCode == mt.appendParameterTypes(List.class).hashCode()); + assertFalse(hashCode == mt.dropParameterTypes(0, 1).hashCode()); + } + + public void testToString() { + assertEquals("(String,Object)int", + MethodType.methodType(int.class, String.class, Object.class).toString()); + assertEquals("()int", MethodType.methodType(int.class).toString()); + assertEquals("()void", MethodType.methodType(void.class).toString()); + assertEquals("()int[]", MethodType.methodType(int[].class).toString()); + } + + public void testFromMethodDescriptorString() { + assertEquals( + MethodType.methodType(int.class, String.class, Object.class), + MethodType.fromMethodDescriptorString("(Ljava/lang/String;Ljava/lang/Object;)I", null)); + + assertEquals(MethodType.fromMethodDescriptorString("()I", null), + MethodType.methodType(int.class)); + assertEquals(MethodType.fromMethodDescriptorString("()[I", null), + MethodType.methodType(int[].class)); + assertEquals(MethodType.fromMethodDescriptorString("([I)V", null), + MethodType.methodType(void.class, int[].class)); + + try { + MethodType.fromMethodDescriptorString(null, null); + fail(); + } catch (NullPointerException expected) { + } + + try { + MethodType.fromMethodDescriptorString("(a/b/c)I", null); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + MethodType.fromMethodDescriptorString("(A)I", null); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + MethodType.fromMethodDescriptorString("(Ljava/lang/String)I", null); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + MethodType.fromMethodDescriptorString("(Ljava/lang/String;)", null); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + MethodType.fromMethodDescriptorString("(Ljava/lang/NonExistentString;)I", null); + fail(); + } catch (TypeNotPresentException expected) { + } + } + + public void testToMethodDescriptorString() { + assertEquals("(Ljava/lang/String;Ljava/lang/Object;)I", MethodType.methodType( + int.class, String.class, Object.class).toMethodDescriptorString()); + + assertEquals("()I", MethodType.methodType(int.class).toMethodDescriptorString()); + assertEquals("()[I", MethodType.methodType(int[].class).toMethodDescriptorString()); + + assertEquals("([I)V", MethodType.methodType(void.class, int[].class) + .toMethodDescriptorString()); + } + + private static void assertParameterTypes(MethodType type, Class... params) { + assertEquals(params.length, type.parameterCount()); + + List> paramsList = type.parameterList(); + for (int i = 0; i < params.length; ++i) { + assertEquals(params[i], type.parameterType(i)); + assertEquals(params[i], paramsList.get(i)); + } + + assertTrue(Arrays.equals(params, type.parameterArray())); + } +} diff --git a/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java b/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java index d71b5b045..696267154 100644 --- a/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java +++ b/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java @@ -72,9 +72,16 @@ static class X {} // Helper function since we do not want a vreg to keep the allocated object live. // For b/25851249 private void exceptionInConstructor() { + boolean thrown = false; try { new ConstructionFails(); + // can't fail() here since AssertionFailedError extends AssertionError, which + // we expect } catch (AssertionError expected) { + thrown = true; + } + if (!thrown) { + fail(); } } @@ -102,11 +109,9 @@ static class ConstructionFails { * to finalize. Check that objects near that limit are okay. */ public void testWatchdogDoesNotFailForObjectsThatAreNearTheDeadline() throws Exception { - CountDownLatch latch = new CountDownLatch(5); + CountDownLatch latch = new CountDownLatch(3); createSlowFinalizer( 1, latch); createSlowFinalizer(1000, latch); - createSlowFinalizer(2000, latch); - createSlowFinalizer(4000, latch); createSlowFinalizer(8000, latch); FinalizationTester.induceFinalization(); latch.await(); diff --git a/luni/src/test/java/libcore/java/lang/reflect/AnnotationsTest.java b/luni/src/test/java/libcore/java/lang/reflect/AnnotationsTest.java deleted file mode 100644 index c9cd3d11a..000000000 --- a/luni/src/test/java/libcore/java/lang/reflect/AnnotationsTest.java +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -package libcore.java.lang.reflect; - -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.annotation.Inherited; -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import junit.framework.TestCase; - -public final class AnnotationsTest extends TestCase { - - public void testClassDirectAnnotations() { - assertAnnotatedElement(Type.class, - AnnotationA.class, AnnotationB.class, RepeatableAnnotation.class); - assertAnnotatedElementDeclared(Type.class, - AnnotationA.class, AnnotationB.class, RepeatableAnnotation.class); - } - - public void testClassInheritedAnnotations() { - assertAnnotatedElement(ExtendsType.class, AnnotationB.class); - assertAnnotatedElementDeclared(ExtendsType.class); - } - - public void testConstructorAnnotations() throws Exception { - Constructor constructor = Type.class.getConstructor(); - assertAnnotatedElement(constructor, AnnotationA.class, AnnotationC.class); - } - - public void testFieldAnnotations() throws Exception { - Field field = Type.class.getField("field"); - assertAnnotatedElement(field, AnnotationA.class, AnnotationD.class); - } - - public void testMethodAnnotations() throws Exception { - Method method = Type.class.getMethod("method", String.class, String.class); - assertAnnotatedElement(method, AnnotationB.class, AnnotationC.class); - } - - public void testParameterAnnotations() throws Exception { - Method method = Type.class.getMethod("method", String.class, String.class); - Annotation[][] noParameterAnnotations = method.getParameterAnnotations(); - assertEquals(2, noParameterAnnotations.length); - assertEquals(set(), annotationsToTypes(noParameterAnnotations[0])); - assertEquals(set(), annotationsToTypes(noParameterAnnotations[1])); - - Method parameters = Type.class.getMethod("parameters", String.class, String.class); - Annotation[][] parameterAnnotations = parameters.getParameterAnnotations(); - assertEquals(2, parameterAnnotations.length); - assertEquals(set(AnnotationB.class, AnnotationD.class), - annotationsToTypes(parameterAnnotations[0])); - assertEquals(set(AnnotationC.class, AnnotationD.class), - annotationsToTypes(parameterAnnotations[1])); - } - - public void testAnnotationDefaults() throws Exception { - assertEquals((byte) 5, defaultValue("a")); - assertEquals((short) 6, defaultValue("b")); - assertEquals(7, defaultValue("c")); - assertEquals(8L, defaultValue("d")); - assertEquals(9.0f, defaultValue("e")); - assertEquals(10.0, defaultValue("f")); - assertEquals('k', defaultValue("g")); - assertEquals(true, defaultValue("h")); - assertEquals(Breakfast.WAFFLES, defaultValue("i")); - assertEquals("@" + AnnotationA.class.getName() + "()", defaultValue("j").toString()); - assertEquals("maple", defaultValue("k")); - assertEquals(AnnotationB.class, defaultValue("l")); - assertEquals("[1, 2, 3]", Arrays.toString((int[]) defaultValue("m"))); - assertEquals("[WAFFLES, PANCAKES]", Arrays.toString((Breakfast[]) defaultValue("n"))); - assertEquals(null, defaultValue("o")); - assertEquals(null, defaultValue("p")); - } - - private Object defaultValue(String name) throws NoSuchMethodException { - return HasDefaultsAnnotation.class.getMethod(name).getDefaultValue(); - } - - public void testGetEnclosingClass() { - assertNull(AnnotationsTest.class.getEnclosingClass()); - assertEquals(AnnotationsTest.class, Foo.class.getEnclosingClass()); - assertEquals(AnnotationsTest.class, HasMemberClassesInterface.class.getEnclosingClass()); - assertEquals(HasMemberClassesInterface.class, - HasMemberClassesInterface.D.class.getEnclosingClass()); - assertEquals(AnnotationsTest.class, Foo.class.getEnclosingClass()); - } - - public void testGetDeclaringClass() { - assertNull(AnnotationsTest.class.getDeclaringClass()); - assertEquals(AnnotationsTest.class, Foo.class.getDeclaringClass()); - assertEquals(AnnotationsTest.class, HasMemberClassesInterface.class.getDeclaringClass()); - assertEquals(HasMemberClassesInterface.class, - HasMemberClassesInterface.D.class.getDeclaringClass()); - } - - public void testGetEnclosingClassIsTransitiveForClassesDefinedInAMethod() { - class C {} - assertEquals(AnnotationsTest.class, C.class.getEnclosingClass()); - } - - public void testGetDeclaringClassIsNotTransitiveForClassesDefinedInAMethod() { - class C {} - assertEquals(null, C.class.getDeclaringClass()); - } - - public void testGetEnclosingMethodIsNotTransitive() { - class C { - class D {} - } - assertEquals(null, C.D.class.getEnclosingMethod()); - } - - public void testStaticFieldAnonymousClass() { - // The class declared in the is enclosed by the 's class. - // http://b/11245138 - assertEquals(AnnotationsTest.class, staticAnonymous.getClass().getEnclosingClass()); - // However, because it is anonymous, it has no declaring class. - // https://code.google.com/p/android/issues/detail?id=61003 - assertNull(staticAnonymous.getClass().getDeclaringClass()); - // Because the class is declared in which is not exposed through reflection, - // it has no enclosing method or constructor. - assertNull(staticAnonymous.getClass().getEnclosingMethod()); - assertNull(staticAnonymous.getClass().getEnclosingConstructor()); - } - - public void testGetEnclosingMethodOfTopLevelClass() { - assertNull(AnnotationsTest.class.getEnclosingMethod()); - } - - public void testGetEnclosingConstructorOfTopLevelClass() { - assertNull(AnnotationsTest.class.getEnclosingConstructor()); - } - - public void testClassEnclosedByConstructor() throws Exception { - Foo foo = new Foo("string"); - assertEquals(Foo.class, foo.c.getEnclosingClass()); - assertEquals(Foo.class.getDeclaredConstructor(String.class), - foo.c.getEnclosingConstructor()); - assertNull(foo.c.getEnclosingMethod()); - assertNull(foo.c.getDeclaringClass()); - } - - public void testClassEnclosedByMethod() throws Exception { - Foo foo = new Foo(); - foo.foo("string"); - assertEquals(Foo.class, foo.c.getEnclosingClass()); - assertNull(foo.c.getEnclosingConstructor()); - assertEquals(Foo.class.getDeclaredMethod("foo", String.class), - foo.c.getEnclosingMethod()); - assertNull(foo.c.getDeclaringClass()); - } - - public void testGetClasses() throws Exception { - // getClasses() doesn't include classes inherited from interfaces! - assertSetEquals(HasMemberClasses.class.getClasses(), - HasMemberClassesSuperclass.B.class, HasMemberClasses.H.class); - } - - public void testGetDeclaredClasses() throws Exception { - assertSetEquals(HasMemberClasses.class.getDeclaredClasses(), - HasMemberClasses.G.class, HasMemberClasses.H.class, HasMemberClasses.I.class, - HasMemberClasses.J.class, HasMemberClasses.K.class, HasMemberClasses.L.class); - } - - public void testConstructorGetExceptions() throws Exception { - assertSetEquals(HasThrows.class.getConstructor().getExceptionTypes(), - IOException.class, InvocationTargetException.class, IllegalStateException.class); - assertSetEquals(HasThrows.class.getConstructor(Void.class).getExceptionTypes()); - } - - public void testClassMethodGetExceptions() throws Exception { - assertSetEquals(HasThrows.class.getMethod("foo").getExceptionTypes(), - IOException.class, InvocationTargetException.class, IllegalStateException.class); - assertSetEquals(HasThrows.class.getMethod("foo", Void.class).getExceptionTypes()); - } - - public void testProxyMethodGetExceptions() throws Exception { - InvocationHandler emptyInvocationHandler = new InvocationHandler() { - @Override public Object invoke(Object proxy, Method method, Object[] args) { - return null; - } - }; - - Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), - new Class[] { ThrowsInterface.class }, emptyInvocationHandler); - assertSetEquals(proxy.getClass().getMethod("foo").getExceptionTypes(), - IOException.class, InvocationTargetException.class, IllegalStateException.class); - assertSetEquals(proxy.getClass().getMethod("foo", Void.class).getExceptionTypes()); - } - - public void testClassModifiers() { - int modifiers = AnnotationsTest.class.getModifiers(); - assertTrue(Modifier.isPublic(modifiers)); - assertFalse(Modifier.isProtected(modifiers)); - assertFalse(Modifier.isPrivate(modifiers)); - assertFalse(Modifier.isAbstract(modifiers)); - assertFalse(Modifier.isStatic(modifiers)); - assertTrue(Modifier.isFinal(modifiers)); - assertFalse(Modifier.isStrict(modifiers)); - } - - public void testInnerClassModifiers() { - int modifiers = Foo.class.getModifiers(); - assertFalse(Modifier.isPublic(modifiers)); - assertFalse(Modifier.isProtected(modifiers)); - assertTrue(Modifier.isPrivate(modifiers)); - assertFalse(Modifier.isAbstract(modifiers)); - assertTrue(Modifier.isStatic(modifiers)); - assertFalse(Modifier.isFinal(modifiers)); - assertFalse(Modifier.isStrict(modifiers)); - } - - public void testAnonymousClassModifiers() { - int modifiers = staticAnonymous.getClass().getModifiers(); - assertFalse(Modifier.isPublic(modifiers)); - assertFalse(Modifier.isProtected(modifiers)); - assertFalse(Modifier.isPrivate(modifiers)); - assertFalse(Modifier.isAbstract(modifiers)); - assertTrue(Modifier.isStatic(modifiers)); - assertFalse(Modifier.isFinal(modifiers)); - assertFalse(Modifier.isStrict(modifiers)); - } - - public void testInnerClassName() { - assertEquals("AnnotationsTest", AnnotationsTest.class.getSimpleName()); - assertEquals("Foo", Foo.class.getSimpleName()); - assertEquals("", staticAnonymous.getClass().getSimpleName()); - } - - public void testIsAnonymousClass() { - assertFalse(AnnotationsTest.class.isAnonymousClass()); - assertFalse(Foo.class.isAnonymousClass()); - assertTrue(staticAnonymous.getClass().isAnonymousClass()); - } - - public void testRepeatableAnnotation() { - RepeatableAnnotation[] annotations = TypeWithMultipleRepeatableAnnotations.class - .getDeclaredAnnotationsByType(RepeatableAnnotation.class); - assertNotNull(annotations); - assertEquals(2, annotations.length); - - // The non-"WithType" methods will see the wrapper annotation - assertAnnotatedElement(TypeWithMultipleRepeatableAnnotations.class, - RepeatableAnnotations.class); - assertAnnotatedElementDeclared(TypeWithMultipleRepeatableAnnotations.class, - RepeatableAnnotations.class); - } - - public void testRepeatableAnnotationExplicit() { - RepeatableAnnotation[] annotations = TypeWithExplicitRepeatableAnnotations.class - .getDeclaredAnnotationsByType(RepeatableAnnotation.class); - assertNotNull(annotations); - assertEquals(2, annotations.length); - - // The non-"WithType" methods will see the wrapper annotation - assertAnnotatedElement(TypeWithExplicitRepeatableAnnotations.class, - RepeatableAnnotations.class); - assertAnnotatedElementDeclared(TypeWithExplicitRepeatableAnnotations.class, - RepeatableAnnotations.class); - } - - public void testRepeatableAnnotationOnPackage() { - Package aPackage = AnnotationsTest.class.getPackage(); - RepeatableAnnotation[] annotations = aPackage - .getDeclaredAnnotationsByType(RepeatableAnnotation.class); - assertNotNull(annotations); - assertEquals(2, annotations.length); - - // The non-"WithType" methods will see the wrapper annotation - assertPresent(true, aPackage, RepeatableAnnotations.class); - assertDeclared(true, aPackage, RepeatableAnnotations.class); - } - - public void testRetentionPolicy() { - assertNull(RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class)); - assertNotNull(RetentionAnnotations.class.getAnnotation(RuntimeRetentionAnnotation.class)); - assertNull(RetentionAnnotations.class.getAnnotation(SourceRetentionAnnotation.class)); - } - - private static final Object staticAnonymous = new Object() {}; - - private static class Foo { - Class c; - private Foo() { - } - private Foo(String s) { - c = new Object() {}.getClass(); - } - private Foo(int i) { - c = new Object() {}.getClass(); - } - private void foo(String s) { - c = new Object() {}.getClass(); - } - private void foo(int i) { - c = new Object() {}.getClass(); - } - } - - @Retention(RetentionPolicy.RUNTIME) - public @interface AnnotationA {} - - @Inherited - @Retention(RetentionPolicy.RUNTIME) - public @interface AnnotationB {} - - @Retention(RetentionPolicy.RUNTIME) - public @interface AnnotationC {} - - @Retention(RetentionPolicy.RUNTIME) - public @interface AnnotationD {} - - @Retention(RetentionPolicy.RUNTIME) - @Repeatable(RepeatableAnnotations.class) - public @interface RepeatableAnnotation {} - - @Retention(RetentionPolicy.RUNTIME) - public @interface RepeatableAnnotations { - RepeatableAnnotation[] value(); - } - - @Retention(RetentionPolicy.CLASS) - public @interface ClassRetentionAnnotation {} - - @Retention(RetentionPolicy.RUNTIME) - public @interface RuntimeRetentionAnnotation {} - - @Retention(RetentionPolicy.SOURCE) - public @interface SourceRetentionAnnotation {} - - @AnnotationA @AnnotationB @RepeatableAnnotation - public static class Type { - @AnnotationA @AnnotationC public Type() {} - @AnnotationA @AnnotationD public String field; - @AnnotationB @AnnotationC public void method(String parameter1, String parameter2) {} - @AnnotationB @AnnotationC public void parameters(@AnnotationB @AnnotationD String parameter1, - @AnnotationC @AnnotationD String parameter2) {} - } - - public static class ExtendsType extends Type {} - - @RepeatableAnnotation - @RepeatableAnnotation - public static class TypeWithMultipleRepeatableAnnotations {} - - @RepeatableAnnotations({ @RepeatableAnnotation, @RepeatableAnnotation}) - public static class TypeWithExplicitRepeatableAnnotations {} - - @ClassRetentionAnnotation @RuntimeRetentionAnnotation @SourceRetentionAnnotation - public static class RetentionAnnotations {} - - static enum Breakfast { WAFFLES, PANCAKES } - - @Retention(RetentionPolicy.RUNTIME) - public @interface HasDefaultsAnnotation { - byte a() default 5; - short b() default 6; - int c() default 7; - long d() default 8; - float e() default 9.0f; - double f() default 10.0; - char g() default 'k'; - boolean h() default true; - Breakfast i() default Breakfast.WAFFLES; - AnnotationA j() default @AnnotationA(); - String k() default "maple"; - Class l() default AnnotationB.class; - int[] m() default { 1, 2, 3 }; - Breakfast[] n() default { Breakfast.WAFFLES, Breakfast.PANCAKES }; - Breakfast o(); - int p(); - } - - static class HasMemberClassesSuperclass { - class A {} - public class B {} - static class C {} - } - - public interface HasMemberClassesInterface { - class D {} - public class E {} - static class F {} - } - - public static class HasMemberClasses extends HasMemberClassesSuperclass - implements HasMemberClassesInterface { - class G {} - public class H {} - static class I {} - enum J {} - interface K {} - @interface L {} - } - - public static class HasThrows { - public HasThrows() throws IOException, InvocationTargetException, IllegalStateException {} - public HasThrows(Void v) {} - public void foo() throws IOException, InvocationTargetException, IllegalStateException {} - public void foo(Void v) {} - } - - public static interface ThrowsInterface { - void foo() throws IOException, InvocationTargetException, IllegalStateException; - void foo(Void v); - } - - private void assertAnnotatedElement( - AnnotatedElement element, Class... expectedAnnotations) { - Set> actualTypes = annotationsToTypes(element.getAnnotations()); - Set> expectedTypes = set(expectedAnnotations); - assertEquals(expectedTypes, actualTypes); - - // getAnnotations() should be consistent with isAnnotationPresent() and getAnnotation() - assertPresent(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class); - assertPresent(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class); - assertPresent(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class); - assertPresent(expectedTypes.contains(RepeatableAnnotation.class), - element, RepeatableAnnotation.class); - - try { - element.isAnnotationPresent(null); - fail(); - } catch (NullPointerException expected) { - } - - try { - element.getAnnotation(null); - fail(); - } catch (NullPointerException expected) { - } - } - - private void assertAnnotatedElementDeclared( - AnnotatedElement element, - Class... expectedDeclaredAnnotations) { - Set> actualTypes = annotationsToTypes(element.getDeclaredAnnotations()); - Set> expectedTypes = set(expectedDeclaredAnnotations); - assertEquals(expectedTypes, actualTypes); - - assertDeclared(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class); - assertDeclared(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class); - assertDeclared(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class); - assertDeclared(expectedTypes.contains(RepeatableAnnotation.class), - element, RepeatableAnnotation.class); - - try { - element.getDeclaredAnnotation(null); - fail(); - } catch (NullPointerException expected) { - } - } - - private Set> annotationsToTypes(Annotation[] annotations) { - Set> result = new HashSet>(); - for (Annotation annotation : annotations) { - result.add(annotation.annotationType()); - } - return result; - } - - private void assertPresent(boolean present, AnnotatedElement element, - Class annotation) { - if (present) { - assertNotNull(element.getAnnotation(annotation)); - assertTrue(element.isAnnotationPresent(annotation)); - } else { - assertNull(element.getAnnotation(annotation)); - assertFalse(element.isAnnotationPresent(annotation)); - } - } - - private void assertDeclared(boolean present, AnnotatedElement element, - Class annotation) { - if (present) { - assertNotNull(element.getDeclaredAnnotation(annotation)); - } else { - assertNull(element.getDeclaredAnnotation(annotation)); - } - } - - private Set set(T... instances) { - return new HashSet(Arrays.asList(instances)); - } - - private void assertSetEquals(Object[] actual, Object... expected) { - Set actualSet = new HashSet(Arrays.asList(actual)); - Set expectedSet = new HashSet(Arrays.asList(expected)); - assertEquals(expectedSet, actualSet); - } -} diff --git a/luni/src/test/java/libcore/java/lang/reflect/ConstructorTest.java b/luni/src/test/java/libcore/java/lang/reflect/ConstructorTest.java index 51ddfc010..8d7ed714f 100644 --- a/luni/src/test/java/libcore/java/lang/reflect/ConstructorTest.java +++ b/luni/src/test/java/libcore/java/lang/reflect/ConstructorTest.java @@ -17,6 +17,8 @@ package libcore.java.lang.reflect; import java.lang.reflect.Constructor; +import java.lang.reflect.Parameter; + import junit.framework.TestCase; public final class ConstructorTest extends TestCase { @@ -33,8 +35,12 @@ public void test_getExceptionTypes() throws Exception { } public void test_getParameterTypes() throws Exception { - Class[] expectedParameters = new Class[] { Object.class }; + Class[] expectedParameters = new Class[0]; Constructor constructor = ConstructorTestHelper.class.getConstructor(expectedParameters); + assertEquals(0, constructor.getParameterTypes().length); + + expectedParameters = new Class[] { Object.class }; + constructor = ConstructorTestHelper.class.getConstructor(expectedParameters); Class[] parameters = constructor.getParameterTypes(); assertEquals(1, parameters.length); assertEquals(expectedParameters[0], parameters[0]); @@ -45,6 +51,38 @@ public void test_getParameterTypes() throws Exception { assertEquals(expectedParameters[0], parameters[0]); } + public void test_getParameterCount() throws Exception { + Class[] expectedParameters = new Class[0]; + Constructor constructor = ConstructorTestHelper.class.getConstructor(expectedParameters); + assertEquals(0, constructor.getParameterCount()); + + expectedParameters = new Class[] { Object.class }; + constructor = ConstructorTestHelper.class.getConstructor(expectedParameters); + int count = constructor.getParameterCount(); + assertEquals(1, count); + } + + public void test_getParameters() throws Exception { + Class[] expectedParameters = new Class[0]; + Constructor constructor = ConstructorTestHelper.class.getConstructor(expectedParameters); + assertEquals(0, constructor.getParameters().length); + + expectedParameters = new Class[] { Object.class }; + constructor = ConstructorTestHelper.class.getConstructor(expectedParameters); + + // Test the information available via other Constructor methods. See ParameterTest and + // annotations.ParameterTest for more in-depth Parameter testing. + Parameter[] parameters = constructor.getParameters(); + assertEquals(1, parameters.length); + assertEquals(Object.class, parameters[0].getType()); + + // Check that corrupting our array doesn't affect other callers. + parameters[0] = null; + parameters = constructor.getParameters(); + assertEquals(1, parameters.length); + assertEquals(Object.class, parameters[0].getType()); + } + public void testGetConstructorWithNullArgumentsArray() throws Exception { Constructor constructor = ConstructorTestHelper.class.getConstructor((Class[]) null); assertEquals(0, constructor.getParameterTypes().length); @@ -90,9 +128,69 @@ public void testDifferentConstructorEqualsAndHashCode() throws Exception { assertFalse(c1.equals(c2)); } + public void testToString() throws Exception { + checkToString( + "public libcore.java.lang.reflect.ConstructorTest$ConstructorTestHelper() throws java.lang.IndexOutOfBoundsException", + ConstructorTestHelper.class); + checkToString( + "public libcore.java.lang.reflect.ConstructorTest$ConstructorTestHelper(java.lang.Object)", + ConstructorTestHelper.class, Object.class); + checkToString( + "private libcore.java.lang.reflect.ConstructorTest$ConstructorTestHelper(java.lang.Object,java.lang.Object)", + ConstructorTestHelper.class, Object.class, Object.class); + checkToString( + "public libcore.java.lang.reflect.ConstructorTest$GenericConstructorTestHelper() throws java.lang.Exception", + GenericConstructorTestHelper.class); + checkToString( + "public libcore.java.lang.reflect.ConstructorTest$GenericConstructorTestHelper(java.lang.String)", + GenericConstructorTestHelper.class, String.class); + checkToString( + "public libcore.java.lang.reflect.ConstructorTest$GenericConstructorTestHelper(java.lang.String,java.lang.Integer)", + GenericConstructorTestHelper.class, String.class, Integer.class); + } + + private static void checkToString(String expected, Class clazz, Class... constructorArgTypes) + throws Exception { + Constructor c = clazz.getDeclaredConstructor(constructorArgTypes); + assertEquals(expected, c.toString()); + } + + public void testToGenericString() throws Exception { + checkToGenericString( + "public libcore.java.lang.reflect.ConstructorTest$ConstructorTestHelper() throws java.lang.IndexOutOfBoundsException", + ConstructorTestHelper.class); + checkToGenericString( + "public libcore.java.lang.reflect.ConstructorTest$ConstructorTestHelper(java.lang.Object)", + ConstructorTestHelper.class, Object.class); + checkToGenericString( + "private libcore.java.lang.reflect.ConstructorTest$ConstructorTestHelper(java.lang.Object,java.lang.Object)", + ConstructorTestHelper.class, Object.class, Object.class); + checkToGenericString( + "public libcore.java.lang.reflect.ConstructorTest$GenericConstructorTestHelper() throws E", + GenericConstructorTestHelper.class); + checkToGenericString( + "public libcore.java.lang.reflect.ConstructorTest$GenericConstructorTestHelper(A)", + GenericConstructorTestHelper.class, String.class); + checkToGenericString( + "public libcore.java.lang.reflect.ConstructorTest$GenericConstructorTestHelper(A,B)", + GenericConstructorTestHelper.class, String.class, Integer.class); + } + + private static void checkToGenericString(String expected, Class clazz, + Class... constructorArgTypes) throws Exception { + Constructor c = clazz.getDeclaredConstructor(constructorArgTypes); + assertEquals(expected, c.toGenericString()); + } + static class ConstructorTestHelper { public ConstructorTestHelper() throws IndexOutOfBoundsException { } public ConstructorTestHelper(Object o) { } private ConstructorTestHelper(Object a, Object b) { } } + + static class GenericConstructorTestHelper { + public GenericConstructorTestHelper() throws E { } + public GenericConstructorTestHelper(A a) { } + public GenericConstructorTestHelper(A a, B b) { } + } } diff --git a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java index a3f9065ae..315a885bc 100644 --- a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java +++ b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java @@ -16,7 +16,15 @@ package libcore.java.lang.reflect; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.function.Function; import junit.framework.TestCase; @@ -34,8 +42,12 @@ public void test_getExceptionTypes() throws Exception { } public void test_getParameterTypes() throws Exception { - Class[] expectedParameters = new Class[] { Object.class }; - Method method = MethodTestHelper.class.getMethod("m2", expectedParameters); + Class[] expectedParameters = new Class[0]; + Method method = MethodTestHelper.class.getMethod("m1", expectedParameters); + assertEquals(0, method.getParameterTypes().length); + + expectedParameters = new Class[] { Object.class }; + method = MethodTestHelper.class.getMethod("m2", expectedParameters); Class[] parameters = method.getParameterTypes(); assertEquals(1, parameters.length); assertEquals(expectedParameters[0], parameters[0]); @@ -46,6 +58,38 @@ public void test_getParameterTypes() throws Exception { assertEquals(expectedParameters[0], parameters[0]); } + public void test_getParameterCount() throws Exception { + Class[] expectedParameters = new Class[0]; + Method method = MethodTestHelper.class.getMethod("m1", expectedParameters); + assertEquals(0, method.getParameterCount()); + + expectedParameters = new Class[] { Object.class }; + method = MethodTestHelper.class.getMethod("m2", expectedParameters); + int count = method.getParameterCount(); + assertEquals(1, count); + } + + public void test_getParameters() throws Exception { + Class[] expectedParameters = new Class[0]; + Method method = MethodTestHelper.class.getMethod("m1", expectedParameters); + assertEquals(0, method.getParameters().length); + + expectedParameters = new Class[] { Object.class }; + method = MethodTestHelper.class.getMethod("m2", expectedParameters); + + // Test the information available via other Method methods. See ParameterTest and + // annotations.ParameterTest for more in-depth Parameter testing. + Parameter[] parameters = method.getParameters(); + assertEquals(1, parameters.length); + assertEquals(Object.class, parameters[0].getType()); + + // Check that corrupting our array doesn't affect other callers. + parameters[0] = null; + parameters = method.getParameters(); + assertEquals(1, parameters.length); + assertEquals(Object.class, parameters[0].getType()); + } + public void testGetMethodWithPrivateMethodAndInterfaceMethod() throws Exception { assertEquals(InterfaceA.class, Sub.class.getMethod("a").getDeclaringClass()); } @@ -184,24 +228,88 @@ public void testDifferentMethodEqualsAndHashCode() throws Exception { // http://b/1045939 public void testMethodToString() throws Exception { - assertEquals("public final native void java.lang.Object.notify()", - Object.class.getMethod("notify", new Class[] { }).toString()); - assertEquals("public java.lang.String java.lang.Object.toString()", - Object.class.getMethod("toString", new Class[] { }).toString()); - assertEquals("public final native void java.lang.Object.wait(long,int)" + checkToString("public final native void java.lang.Object.notify()", + Object.class, "notify"); + checkToString("public java.lang.String java.lang.Object.toString()", + Object.class, "toString"); + checkToString("public final native void java.lang.Object.wait(long,int)" + " throws java.lang.InterruptedException", - Object.class.getMethod("wait", new Class[] { long.class, int.class }).toString()); - assertEquals("public boolean java.lang.Object.equals(java.lang.Object)", - Object.class.getMethod("equals", new Class[] { Object.class }).toString()); - assertEquals("public static java.lang.String java.lang.String.valueOf(char[])", - String.class.getMethod("valueOf", new Class[] { char[].class }).toString()); - assertEquals( "public java.lang.Process java.lang.Runtime.exec(java.lang.String[])" + Object.class, "wait", long.class, int.class); + checkToString("public boolean java.lang.Object.equals(java.lang.Object)", + Object.class, "equals", Object.class); + checkToString("public static java.lang.String java.lang.String.valueOf(char[])", + String.class, "valueOf", char[].class); + checkToString( "public java.lang.Process java.lang.Runtime.exec(java.lang.String[])" + " throws java.io.IOException", - Runtime.class.getMethod("exec", new Class[] { String[].class }).toString()); + Runtime.class, "exec", String[].class); // http://b/18488857 - assertEquals( + checkToString( "public int java.lang.String.compareTo(java.lang.Object)", - String.class.getMethod("compareTo", Object.class).toString()); + String.class, "compareTo", Object.class); + + // Generic methods + checkToString( + "public abstract java.lang.Object java.util.List.get(int)", + List.class, "get", int.class); + checkToString( + "public abstract boolean java.util.List.add(java.lang.Object)", + List.class, "add", Object.class); + checkToString( + "public static void java.util.Collections.sort(java.util.List,java.util.Comparator)", + Collections.class, "sort", List.class, Comparator.class); + + // Java 8 language addition: default interface method. + checkToString( + "public default java.util.function.Function java.util.function.Function.compose(java.util.function.Function)", + Function.class, "compose", Function.class); + // Java 8 language addition: static interface method. + checkToString( + "public static java.util.function.Function java.util.function.Function.identity()", + Function.class, "identity"); + } + + private static void checkToString(String expected, Class clazz, String methodName, + Class... methodArgTypes) throws Exception { + Method m = clazz.getMethod(methodName, methodArgTypes); + assertEquals(expected, m.toString()); + } + + public void testMethodToGenericString() throws Exception { + // Non-generic methods. + checkToGenericString("public final native void java.lang.Object.notify()", + Object.class, "notify"); + checkToGenericString("public java.lang.String java.lang.Object.toString()", + Object.class, "toString"); + checkToGenericString("public final native void java.lang.Object.wait(long,int)" + + " throws java.lang.InterruptedException", + Object.class, "wait", long.class, int.class); + + // Generic methods + checkToGenericString( + "public abstract E java.util.List.get(int)", + List.class, "get", int.class); + checkToGenericString( + "public abstract boolean java.util.List.add(E)", + List.class, "add", Object.class); + checkToGenericString( + "public static void java.util.Collections.sort(java.util.List,java.util.Comparator)", + Collections.class, "sort", List.class, Comparator.class); + + + // Java 8 language addition: default interface method. + checkToGenericString( + "public default java.util.function.Function java.util.function.Function.compose(java.util.function.Function)", + Function.class, "compose", Function.class); + // Java 8 language addition: static interface method. + checkToGenericString( + "public static java.util.function.Function java.util.function.Function.identity()", + Function.class, "identity"); + } + + private static void checkToGenericString(String expected, Class clazz, String methodName, + Class... methodArgTypes) throws Exception { + Method m = clazz.getMethod(methodName, methodArgTypes); + assertEquals(expected, m.toGenericString()); } // Tests that the "varargs" modifier is handled correctly. @@ -226,14 +334,320 @@ public static class Super { private void a() {} public static void b() {} } - public static interface InterfaceA { + public interface InterfaceA { void a(); } public static abstract class Sub extends Super implements InterfaceA { } - public static interface InterfaceB extends InterfaceA {} - public static interface InterfaceC extends InterfaceB {} + public interface InterfaceB extends InterfaceA {} + public interface InterfaceC extends InterfaceB {} public static abstract class ImplementsC implements InterfaceC {} public static abstract class ExtendsImplementsC extends ImplementsC {} + + // Static interface method reflection. + + public interface InterfaceWithStatic { + static String staticMethod() { + return identifyCaller(); + } + } + + public void testStaticInterfaceMethod_getMethod() throws Exception { + Method method = InterfaceWithStatic.class.getMethod("staticMethod"); + assertFalse(method.isDefault()); + assertEquals(Modifier.PUBLIC | Modifier.STATIC, method.getModifiers()); + assertEquals(InterfaceWithStatic.class, method.getDeclaringClass()); + } + + public void testStaticInterfaceMethod_getDeclaredMethod() throws Exception { + Method declaredMethod = InterfaceWithStatic.class.getDeclaredMethod("staticMethod"); + assertFalse(declaredMethod.isDefault()); + assertEquals(Modifier.PUBLIC | Modifier.STATIC, declaredMethod.getModifiers()); + assertEquals(InterfaceWithStatic.class, declaredMethod.getDeclaringClass()); + } + + public void testStaticInterfaceMethod_invoke() throws Exception { + String interfaceWithStaticClassName = InterfaceWithStatic.class.getName(); + assertEquals(interfaceWithStaticClassName, InterfaceWithStatic.staticMethod()); + + Method method = InterfaceWithStatic.class.getMethod("staticMethod"); + assertEquals(interfaceWithStaticClassName, method.invoke(null)); + assertEquals(interfaceWithStaticClassName, method.invoke(new InterfaceWithStatic() {})); + } + + public void testStaticInterfaceMethod_setAccessible() throws Exception { + String interfaceWithStaticClassName = InterfaceWithStatic.class.getName(); + Method method = InterfaceWithStatic.class.getMethod("staticMethod"); + method.setAccessible(false); + // No effect expected. + assertEquals(interfaceWithStaticClassName, method.invoke(null)); + } + + // Default method reflection. + + public interface InterfaceWithDefault { + default String defaultMethod() { + return identifyCaller(); + } + } + + public static class ImplementationWithDefault implements InterfaceWithDefault { + } + + public void testDefaultMethod_getDeclaredMethod_interface() throws Exception { + Class interfaceWithDefaultClass = InterfaceWithDefault.class; + Method defaultMethod = interfaceWithDefaultClass.getDeclaredMethod("defaultMethod"); + assertEquals(InterfaceWithDefault.class, defaultMethod.getDeclaringClass()); + assertTrue(defaultMethod.isDefault()); + } + + public void testDefaultMethod_inheritance() throws Exception { + Class interfaceWithDefaultClass = InterfaceWithDefault.class; + String interfaceWithDefaultClassName = interfaceWithDefaultClass.getName(); + Method defaultMethod = interfaceWithDefaultClass.getDeclaredMethod("defaultMethod"); + + InterfaceWithDefault anon = new InterfaceWithDefault() {}; + Class anonClass = anon.getClass(); + Method inheritedDefaultMethod = anonClass.getMethod("defaultMethod"); + assertEquals(inheritedDefaultMethod, defaultMethod); + + // Check invocation behavior. + assertEquals(interfaceWithDefaultClassName, defaultMethod.invoke(anon)); + assertEquals(interfaceWithDefaultClassName, inheritedDefaultMethod.invoke(anon)); + assertEquals(interfaceWithDefaultClassName, anon.defaultMethod()); + + // Check other method properties. + assertEquals(InterfaceWithDefault.class, inheritedDefaultMethod.getDeclaringClass()); + assertTrue(inheritedDefaultMethod.isDefault()); + + // Confirm the method is not considered declared on the anonymous class. + assertNull(getDeclaredMethodOrNull(anonClass, "defaultMethod")); + } + + public void testDefaultMethod_override() throws Exception { + Class interfaceWithDefaultClass = InterfaceWithDefault.class; + Method defaultMethod = interfaceWithDefaultClass.getDeclaredMethod("defaultMethod"); + + InterfaceWithDefault anon = new InterfaceWithDefault() { + @Override public String defaultMethod() { + return identifyCaller(); + } + }; + + Class anonClass = anon.getClass(); + String anonymousClassName = anonClass.getName(); + + Method overriddenDefaultMethod = getDeclaredMethodOrNull(anonClass, "defaultMethod"); + assertNotNull(overriddenDefaultMethod); + assertFalse(overriddenDefaultMethod.equals(defaultMethod)); + + // Check invocation behavior. + assertEquals(anonymousClassName, defaultMethod.invoke(anon)); + assertEquals(anonymousClassName, overriddenDefaultMethod.invoke(anon)); + assertEquals(anonymousClassName, anon.defaultMethod()); + + // Check other method properties. + assertEquals(anonClass, overriddenDefaultMethod.getDeclaringClass()); + assertFalse(overriddenDefaultMethod.isDefault()); + } + + public void testDefaultMethod_setAccessible() throws Exception { + InterfaceWithDefault anon = new InterfaceWithDefault() {}; + + Method defaultMethod = anon.getClass().getMethod("defaultMethod"); + defaultMethod.setAccessible(false); + // setAccessible(false) should have no effect. + assertEquals(InterfaceWithDefault.class.getName(), defaultMethod.invoke(anon)); + + InterfaceWithDefault anon2 = new InterfaceWithDefault() { + @Override public String defaultMethod() { + return identifyCaller(); + } + }; + + Class anon2Class = anon2.getClass(); + Method overriddenDefaultMethod = anon2Class.getDeclaredMethod("defaultMethod"); + overriddenDefaultMethod.setAccessible(false); + // setAccessible(false) should have no effect. + assertEquals(anon2Class.getName(), overriddenDefaultMethod.invoke(anon2)); + } + + interface InterfaceWithReAbstractedMethod extends InterfaceWithDefault { + // Re-abstract a default method. + @Override String defaultMethod(); + } + + public void testDefaultMethod_reabstracted() throws Exception { + Class subclass = InterfaceWithReAbstractedMethod.class; + + Method reabstractedDefaultMethod = subclass.getMethod("defaultMethod"); + assertFalse(reabstractedDefaultMethod.isDefault()); + assertEquals(reabstractedDefaultMethod, subclass.getDeclaredMethod("defaultMethod")); + assertEquals(subclass, reabstractedDefaultMethod.getDeclaringClass()); + } + + public void testDefaultMethod_reimplementedInClass() throws Exception { + InterfaceWithDefault impl = new InterfaceWithReAbstractedMethod() { + // Implement a reabstracted default method. + @Override public String defaultMethod() { + return identifyCaller(); + } + }; + Class implClass = impl.getClass(); + String implClassName = implClass.getName(); + + Method implClassDefaultMethod = getDeclaredMethodOrNull(implClass, "defaultMethod"); + assertEquals(implClassDefaultMethod, implClass.getMethod("defaultMethod")); + + // Check invocation behavior. + assertEquals(implClassName, impl.defaultMethod()); + assertEquals(implClassName, implClassDefaultMethod.invoke(impl)); + + // Check other method properties. + assertEquals(implClass, implClassDefaultMethod.getDeclaringClass()); + assertFalse(implClassDefaultMethod.isDefault()); + } + + interface InterfaceWithRedefinedMethods extends InterfaceWithReAbstractedMethod { + // Reimplement an abstracted default method. + @Override default String defaultMethod() { + return identifyCaller(); + } + } + + public void testDefaultMethod_reimplementInInterface() throws Exception { + Class interfaceClass = InterfaceWithRedefinedMethods.class; + String interfaceClassName = interfaceClass.getName(); + + // NOTE: The line below defines an anonymous class that implements + // InterfaceWithReDefinedMethods (and does not need to provide any declarations). + // See the {}. + InterfaceWithDefault impl = new InterfaceWithRedefinedMethods() {}; + Class implClass = impl.getClass(); + + Method implClassDefaultMethod = implClass.getMethod("defaultMethod"); + assertNull(getDeclaredMethodOrNull(implClass, "defaultMethod")); + + // Check invocation behavior. + assertEquals(interfaceClassName, impl.defaultMethod()); + assertEquals(interfaceClassName, implClassDefaultMethod.invoke(impl)); + + // Check other method properties. + assertEquals(interfaceClass, implClassDefaultMethod.getDeclaringClass()); + assertTrue(implClassDefaultMethod.isDefault()); + } + + public void testDefaultMethod_invoke() throws Exception { + InterfaceWithDefault impl1 = new InterfaceWithRedefinedMethods() {}; + InterfaceWithDefault impl2 = new InterfaceWithReAbstractedMethod() { + @Override public String defaultMethod() { + return identifyCaller(); + } + }; + InterfaceWithDefault impl3 = new InterfaceWithDefault() {}; + + Class[] classes = { + InterfaceWithRedefinedMethods.class, + impl1.getClass(), + InterfaceWithReAbstractedMethod.class, + impl2.getClass(), + InterfaceWithDefault.class, + impl3.getClass(), + }; + Object[] instances = { impl1, impl2, impl3 }; + + // Attempt to invoke all declarations of defaultMethod() on a selection of instances. + for (Class clazz : classes) { + Method method = clazz.getMethod("defaultMethod"); + for (Object instance : instances) { + if (method.getDeclaringClass().isAssignableFrom(instance.getClass())) { + Method trueMethod = instance.getClass().getMethod("defaultMethod"); + // All implementations of defaultMethod return the class where the method is + // declared, enabling us to tell if the correct implementation has been called. + Class declaringClass = trueMethod.getDeclaringClass(); + assertEquals(declaringClass.getName(), method.invoke(instance)); + } else { + try { + method.invoke(instance); + fail(); + } catch (IllegalArgumentException expected) { + } + } + } + } + } + + interface OtherInterfaceWithDefault { + default String defaultMethod() { + return identifyCaller(); + } + } + + public void testDefaultMethod_superSyntax() throws Exception { + class ImplementationSuperUser implements InterfaceWithDefault, OtherInterfaceWithDefault { + @Override public String defaultMethod() { + return identifyCaller() + ":" + + InterfaceWithDefault.super.defaultMethod() + ":" + + OtherInterfaceWithDefault.super.defaultMethod(); + } + } + + String implementationSuperUserClassName = ImplementationSuperUser.class.getName(); + String interfaceWithDefaultClassName = InterfaceWithDefault.class.getName(); + String otherInterfaceWithDefaultClassName = OtherInterfaceWithDefault.class.getName(); + String expectedReturnValue = implementationSuperUserClassName + ":" + + interfaceWithDefaultClassName + ":" + otherInterfaceWithDefaultClassName; + ImplementationSuperUser obj = new ImplementationSuperUser(); + assertEquals(expectedReturnValue, obj.defaultMethod()); + + Method defaultMethod = ImplementationSuperUser.class.getMethod("defaultMethod"); + assertEquals(expectedReturnValue, defaultMethod.invoke(obj)); + } + + public void testProxyWithDefaultMethods() throws Exception { + InvocationHandler invocationHandler = new InvocationHandler() { + @Override public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + assertSame(InterfaceWithDefault.class, method.getDeclaringClass()); + return identifyCaller(); + } + }; + + InterfaceWithDefault proxyWithDefaultMethod = (InterfaceWithDefault) Proxy.newProxyInstance( + Thread.currentThread().getContextClassLoader(), + new Class[] { InterfaceWithDefault.class }, + invocationHandler); + String invocationHandlerClassName = invocationHandler.getClass().getName(); + + // Check the proxy implements the default method. + Class proxyClass = proxyWithDefaultMethod.getClass(); + Method defaultMethod = proxyClass.getMethod("defaultMethod"); + assertEquals(proxyClass, defaultMethod.getDeclaringClass()); + assertFalse(defaultMethod.isDefault()); + + // The default method is intercepted like anything else. + assertEquals(invocationHandlerClassName, proxyWithDefaultMethod.defaultMethod()); + } + + private static Method getDeclaredMethodOrNull(Class clazz, String methodName) { + try { + Method m = clazz.getDeclaredMethod(methodName); + assertNotNull(m); + return m; + } catch (NoSuchMethodException e) { + return null; + } + } + + /** + * Keep this package-protected or public to avoid the introduction of synthetic methods that + * throw off the offset. + */ + static String identifyCaller() { + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + int i = 0; + while (!stack[i++].getMethodName().equals("identifyCaller")) {} + return stack[i].getClassName(); + } } diff --git a/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java b/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java index 0505f2f7e..2d395c535 100644 --- a/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java +++ b/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java @@ -39,6 +39,10 @@ public void test_methodModifiers() { assertEquals(0xd3f, Modifier.methodModifiers()); } + public void test_parameterModifiers() { + assertEquals(0x10, Modifier.parameterModifiers()); + } + public void test_isAbstractI() { assertTrue(Modifier.isAbstract(Modifier.ABSTRACT)); assertTrue(!Modifier.isAbstract(-1 & ~Modifier.ABSTRACT)); diff --git a/luni/src/test/java/libcore/java/lang/reflect/ParameterTest.java b/luni/src/test/java/libcore/java/lang/reflect/ParameterTest.java new file mode 100644 index 000000000..18fce55a3 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/ParameterTest.java @@ -0,0 +1,1110 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect; + +import junit.framework.TestCase; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.MalformedParametersException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.text.NumberFormat; +import java.util.Arrays; +import java.util.concurrent.Callable; +import java.util.function.Function; +import libcore.io.Streams; + +import dalvik.system.PathClassLoader; + +/** + * Tests for {@link Parameter}. For annotation-related tests see + * {@link libcore.java.lang.reflect.annotations.AnnotatedElementParameterTest} and + * {@link libcore.java.lang.reflect.annotations.ExecutableParameterTest}. + * + *

Tests suffixed with _withMetadata() require parameter metadata compiled in to work properly. + * These are handled by loading pre-compiled .dex files. + * See also {@link DependsOnParameterMetadata}. + */ +public class ParameterTest extends TestCase { + + /** + * A ClassLoader that can be used to load the + * libcore.java.lang.reflect.parameter.ParameterMetadataTestClasses class and its nested + * classes. The loaded classes has valid metadata that could be created by a valid Android + * compiler. + */ + private ClassLoader classesWithMetadataClassLoader; + + /** + * A ClassLoader that can be used to load the + * libcore.java.lang.reflect.parameter.MetadataVariations class. + * The loaded class has invalid metadata that could not be created by a valid Android + * compiler. + */ + private ClassLoader metadataVariationsClassLoader; + + @Override + public void setUp() throws Exception { + super.setUp(); + File dexDir = File.createTempFile("dexDir", ""); + assertTrue(dexDir.delete()); + assertTrue(dexDir.mkdirs()); + + classesWithMetadataClassLoader = + createClassLoaderForDexResource(dexDir, "parameter_metadata_test_classes.dex"); + metadataVariationsClassLoader = + createClassLoaderForDexResource(dexDir, "metadata_variations.dex"); + } + + /** + * A source annotation used to mark code below with behavior that is highly dependent on + * parameter metadata. It is intended to bring readers here for the following: + * + *

Unless the compiler supports (and is configured to enable) storage of metadata + * for parameters, the runtime does not have access to the parameter name from the source and + * some modifier information like "implicit" (AKA "mandated"), "synthetic" and "final". + * + *

This test class is expected to be compiled without requesting that the metadata + * be compiled in. dex files that contains classes with metadata are loaded in setUp() and + * used from the tests suffixed with "_withMetadata". + */ + @Retention(RetentionPolicy.SOURCE) + @Target(ElementType.METHOD) + private @interface DependsOnParameterMetadata {} + + private static class SingleParameter { + @SuppressWarnings("unused") + SingleParameter(String p0) {} + + @SuppressWarnings("unused") + void oneParameter(String p0) {} + } + + public void testSingleParameterConstructor() throws Exception { + Constructor constructor = SingleParameter.class.getDeclaredConstructor(String.class); + checkSingleStringParameter(constructor); + } + + public void testSingleParameterMethod() throws Exception { + Method method = SingleParameter.class.getDeclaredMethod("oneParameter", String.class); + checkSingleStringParameter(method); + } + + private static void checkSingleStringParameter(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.String arg0]") + .checkParametersMetadataNotAvailable() + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(String.class) + .checkGetParameterizedType("class java.lang.String"); + } + + public void testSingleParameterConstructor_withMetadata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("SingleParameter"); + Constructor constructor = clazz.getDeclaredConstructor(String.class); + checkSingleStringParameter_withMetadata(constructor); + } + + public void testSingleParameterMethod_withMetadata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("SingleParameter"); + Method method = clazz.getDeclaredMethod("oneParameter", String.class); + checkSingleStringParameter_withMetadata(method); + } + + private static void checkSingleStringParameter_withMetadata(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.String p0]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(String.class) + .checkName(true /* expectedNameIsPresent */, "p0") + .checkModifiers(0) + .checkImplicitAndSynthetic(false, false) + .checkGetParameterizedType("class java.lang.String"); + } + + private static class GenericParameter { + @SuppressWarnings("unused") + GenericParameter(Function p0) {} + + @SuppressWarnings("unused") + void genericParameter(Function p0) {} + } + + public void testGenericParameterConstructor() throws Exception { + Constructor constructor = GenericParameter.class.getDeclaredConstructor(Function.class); + checkGenericParameter(constructor); + } + + public void testGenericParameterMethod() throws Exception { + Method method = GenericParameter.class.getDeclaredMethod( + "genericParameter", Function.class); + checkGenericParameter(method); + } + + private static void checkGenericParameter(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString( + "[java.util.function.Function arg0]") + .checkParametersMetadataNotAvailable() + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(Function.class) + .checkGetParameterizedType( + "java.util.function.Function"); + } + + public void testGenericParameterConstructor_withMetadata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("GenericParameter"); + Constructor constructor = clazz.getDeclaredConstructor(Function.class); + checkGenericParameter_withMetadata(constructor); + } + + public void testGenericParameterMethod_withMetadata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("GenericParameter"); + Method method = clazz.getDeclaredMethod("genericParameter", Function.class); + checkGenericParameter_withMetadata(method); + } + + private static void checkGenericParameter_withMetadata(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString( + "[java.util.function.Function p0]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(Function.class) + .checkName(true /* expectedNameIsPresent */, "p0") + .checkModifiers(0) + .checkImplicitAndSynthetic(false, false) + .checkGetParameterizedType( + "java.util.function.Function"); + } + + private static class TwoParameters { + @SuppressWarnings("unused") + TwoParameters(String p0, Integer p1) {} + @SuppressWarnings("unused") + void twoParameters(String p0, Integer p1) {} + } + + public void testTwoParameterConstructor() throws Exception { + Constructor constructor = + TwoParameters.class.getDeclaredConstructor(String.class, Integer.class); + checkTwoParameters(constructor); + } + + public void testTwoParameterMethod() throws Exception { + Method method = TwoParameters.class.getDeclaredMethod( + "twoParameters", String.class, Integer.class); + checkTwoParameters(method); + } + + private static void checkTwoParameters(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.String arg0, java.lang.Integer arg1]") + .checkParametersMetadataNotAvailable() + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(String.class) + .checkGetParameterizedType("class java.lang.String"); + + helper.getParameterTestHelper(1) + .checkGetType(Integer.class) + .checkGetParameterizedType("class java.lang.Integer"); + } + + public void testTwoParameterConstructor_withMetadata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("TwoParameters"); + Constructor constructor = clazz.getDeclaredConstructor(String.class, Integer.class); + checkTwoParameters_withMetadata(constructor); + } + + public void testTwoParameterMethod_withMetadata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("TwoParameters"); + Method method = clazz.getDeclaredMethod("twoParameters", String.class, Integer.class); + checkTwoParameters_withMetadata(method); + } + + private static void checkTwoParameters_withMetadata(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.String p0, java.lang.Integer p1]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(String.class) + .checkName(true /* expectedNameIsPresent */, "p0") + .checkModifiers(0) + .checkImplicitAndSynthetic(false, false) + .checkGetParameterizedType("class java.lang.String"); + + helper.getParameterTestHelper(1) + .checkGetType(Integer.class) + .checkName(true /* expectedNameIsPresent */, "p1") + .checkModifiers(0) + .checkImplicitAndSynthetic(false, false) + .checkGetParameterizedType("class java.lang.Integer"); + } + + private static class FinalParameter { + @SuppressWarnings("unused") + FinalParameter(final String p0) {} + @SuppressWarnings("unused") + void finalParameter(final String p0) {} + } + + public void testFinalParameterConstructor() throws Exception { + Constructor constructor = FinalParameter.class.getDeclaredConstructor(String.class); + checkFinalParameter(constructor); + } + + public void testFinalParameterMethod() throws Exception { + Method method = FinalParameter.class.getDeclaredMethod("finalParameter", String.class); + checkFinalParameter(method); + } + + private static void checkFinalParameter(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.String arg0]") + .checkParametersMetadataNotAvailable() + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(String.class) + .checkGetParameterizedType("class java.lang.String"); + } + + public void testFinalParameterConstructor_withMetdata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("FinalParameter"); + Constructor constructor = clazz.getDeclaredConstructor(String.class); + checkFinalParameter_withMetadata(constructor); + } + + public void testFinalParameterMethod_withMetdata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("FinalParameter"); + Method method = clazz.getDeclaredMethod("finalParameter", String.class); + checkFinalParameter_withMetadata(method); + } + + private static void checkFinalParameter_withMetadata(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString("[final java.lang.String p0]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(String.class) + .checkName(true /* expectedNameIsPresent */, "p0") + .checkModifiers(Modifier.FINAL) + .checkImplicitAndSynthetic(false, false) + .checkGetParameterizedType("class java.lang.String"); + } + + /** + * An inner class, used for checking compiler-inserted parameters: The first parameter is an + * instance of the surrounding class. + */ + private class InnerClass { + @SuppressWarnings("unused") + public InnerClass() {} + @SuppressWarnings("unused") + public InnerClass(String p1) {} + @SuppressWarnings("unused") + public InnerClass(Function p1) {} + } + + public void testInnerClassSingleParameter() throws Exception { + Class outerClass = ParameterTest.class; + Class innerClass = InnerClass.class; + Constructor constructor = innerClass.getDeclaredConstructor(outerClass); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString("[" + outerClass.getName() + " arg0]") + .checkParametersMetadataNotAvailable() + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(outerClass) + .checkGetParameterizedType("class " + outerClass.getName() + ""); + } + + public void testInnerClassSingleParameter_withMetadata() throws Exception { + Class outerClass = loadTestOuterClassWithMetadata(); + Class innerClass = loadTestInnerClassWithMetadata("InnerClass"); + Constructor constructor = innerClass.getDeclaredConstructor(outerClass); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString("[final " + outerClass.getName() + " this$0]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(outerClass) + .checkName(true /* expectedNameIsPresent */, "this$0") + .checkModifiers(32784) // 32784 == Modifier.MANDATED & Modifier.FINAL + .checkImplicitAndSynthetic(true, false) + .checkGetParameterizedType("class " + outerClass.getName()); + } + + public void testInnerClassTwoParameters() throws Exception { + Class outerClass = ParameterTest.class; + Class innerClass = InnerClass.class; + Constructor constructor = innerClass.getDeclaredConstructor(outerClass, String.class); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString( + "[" + outerClass.getName() + " arg0, java.lang.String arg1]") + .checkParametersMetadataNotAvailable() + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(outerClass) + .checkGetParameterizedType("class " + outerClass.getName()); + + helper.getParameterTestHelper(1) + .checkGetType(String.class) + .checkGetParameterizedType("class java.lang.String"); + } + + public void testInnerClassTwoParameters_withMetadata() throws Exception { + Class outerClass = loadTestOuterClassWithMetadata(); + Class innerClass = loadTestInnerClassWithMetadata("InnerClass"); + Constructor constructor = innerClass.getDeclaredConstructor(outerClass, String.class); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString( + "[final " + outerClass.getName() + " this$0, java.lang.String p1]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkName(true /* expectedNameIsPresent */, "this$0") + .checkModifiers(32784) // 32784 == Modifier.MANDATED & Modifier.FINAL + .checkImplicitAndSynthetic(true, false) + .checkGetType(outerClass) + .checkGetParameterizedType("class " + outerClass.getName() + ""); + + helper.getParameterTestHelper(1) + .checkName(true /* expectedNameIsPresent */, "p1") + .checkModifiers(0) + .checkImplicitAndSynthetic(false, false) + .checkGetType(String.class) + .checkGetParameterizedType("class java.lang.String"); + } + + public void testInnerClassGenericParameter() throws Exception { + Class outerClass = ParameterTest.class; + Class innerClass = InnerClass.class; + Constructor constructor = innerClass.getDeclaredConstructor(outerClass, Function.class); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString( + "[" + outerClass.getName() + " arg0, java.util.function.Function arg1]") + .checkParametersMetadataNotAvailable() + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(outerClass) + .checkGetParameterizedType("class " + outerClass.getName() + ""); + + helper.getParameterTestHelper(1) + .checkGetType(Function.class) + .checkGetParameterizedType("interface java.util.function.Function"); + + // The non-genericised string above is probably the result of a spec bug due to a mismatch + // between the generic signature for the constructor (which suggests a single parameter) + // and the actual parameters (which suggests two). In the absence of parameter metadata + // to identify the synthetic parameter the code reverts to using non-Signature (type erased) + // information. + } + + public void testInnerClassGenericParameter_withMetadata() throws Exception { + Class outerClass = loadTestOuterClassWithMetadata(); + Class innerClass = loadTestInnerClassWithMetadata("InnerClass"); + Constructor constructor = innerClass.getDeclaredConstructor(outerClass, Function.class); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString("[final " + outerClass.getName() + " this$0, " + + "java.util.function.Function p1]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkName(true /* expectedNameIsPresent */, "this$0") + .checkModifiers(32784) // 32784 == Modifier.MANDATED & Modifier.FINAL + .checkImplicitAndSynthetic(true, false) + .checkGetType(outerClass) + .checkGetParameterizedType("class " + outerClass.getName() + ""); + + helper.getParameterTestHelper(1) + .checkName(true /* expectedNameIsPresent */, "p1") + .checkModifiers(0) + .checkImplicitAndSynthetic(false, false) + .checkGetType(Function.class) + .checkGetParameterizedType( + "java.util.function.Function"); + } + + @SuppressWarnings("unused") + enum TestEnum { ONE, TWO } + + /** + * Enums are a documented example of a type of class with synthetic constructor parameters and + * generated methods. This test may be brittle as it may rely on the compiler's implementation + * of enums. + */ + public void testEnumConstructor() throws Exception { + Constructor constructor = TestEnum.class.getDeclaredConstructor(String.class, int.class); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.String arg0, int arg1]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(String.class) + .checkGetParameterizedType("class java.lang.String"); + + helper.getParameterTestHelper(1) + .checkGetType(int.class) + .checkGetParameterizedType("int"); + } + + public void testEnumConstructor_withMetadata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("TestEnum"); + Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + // The extra spaces below are the result of a trivial upstream bug in + // Parameter.toString() due to Modifier.toString(int) outputting nothing for + // "SYNTHETIC". + .checkParametersToString("[ java.lang.String $enum$name, int $enum$ordinal]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkName(true /* expectedNameIsPresent */, "$enum$name") + .checkModifiers(4096) // 4096 == Modifier.SYNTHETIC + .checkImplicitAndSynthetic(false, true) + .checkGetType(String.class) + .checkGetParameterizedType("class java.lang.String"); + + helper.getParameterTestHelper(1) + .checkName(true /* expectedNameIsPresent */, "$enum$ordinal") + .checkModifiers(4096) // 4096 == Modifier.SYNTHETIC + .checkImplicitAndSynthetic(false, true) + .checkGetType(int.class) + .checkGetParameterizedType("int"); + } + + public void testEnumValueOf() throws Exception { + Method method = TestEnum.class.getDeclaredMethod("valueOf", String.class); + + ExecutableTestHelper helper = new ExecutableTestHelper(method); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.String arg0]") + .checkParametersMetadataNotAvailable() + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(String.class) + .checkGetParameterizedType("class java.lang.String"); + } + + public void testEnumValueOf_withMetadata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("TestEnum"); + Method method = clazz.getDeclaredMethod("valueOf", String.class); + + ExecutableTestHelper helper = new ExecutableTestHelper(method); + helper.checkStandardParametersBehavior() + // The extra space below are the result of a trivial upstream bug in + // Parameter.toString() due to Modifier.toString(int) outputting nothing for + // "MANDATED". + .checkParametersToString("[ java.lang.String name]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkName(true /* expectedNameIsPresent */, "name") + .checkModifiers(32768) // 32768 == Modifier.MANDATED + .checkImplicitAndSynthetic(true, false) + .checkGetType(String.class) + .checkGetParameterizedType("class java.lang.String"); + } + + private static class SingleVarArgs { + @SuppressWarnings("unused") + SingleVarArgs(String... p0) {} + + @SuppressWarnings("unused") + void varArgs(String... p0) {} + } + + public void testSingleVarArgsConstructor() throws Exception { + Constructor constructor = SingleVarArgs.class.getDeclaredConstructor(String[].class); + checkSingleVarArgsParameter(constructor); + } + + public void testSingleVarArgsMethod() throws Exception { + Method method = SingleVarArgs.class.getDeclaredMethod("varArgs", String[].class); + checkSingleVarArgsParameter(method); + } + + private static void checkSingleVarArgsParameter(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.String... arg0]") + .checkParametersMetadataNotAvailable(); + + helper.getParameterTestHelper(0) + .checkGetType(String[].class) + .checkIsVarArg(true) + .checkGetParameterizedType("class [Ljava.lang.String;"); + } + + public void testSingleVarArgsConstructor_withMetadata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("SingleVarArgs"); + Constructor constructor = clazz.getDeclaredConstructor(String[].class); + checkSingleVarArgsParameter_withMetadata(constructor); + } + + public void testSingleVarArgsMethod_withMetadata() throws Exception { + Class clazz = loadTestInnerClassWithMetadata("SingleVarArgs"); + Method method = clazz.getDeclaredMethod("varArgs", String[].class); + checkSingleVarArgsParameter_withMetadata(method); + } + + private static void checkSingleVarArgsParameter_withMetadata(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.String... p0]"); + + helper.getParameterTestHelper(0) + .checkName(true /* expectedNameIsPresent */, "p0") + .checkModifiers(0) + .checkImplicitAndSynthetic(false, false) + .checkGetType(String[].class) + .checkIsVarArg(true) + .checkGetParameterizedType("class [Ljava.lang.String;"); + } + + private static class MixedVarArgs { + @SuppressWarnings("unused") + MixedVarArgs(Integer[] p0, String... p1) {} + @SuppressWarnings("unused") + void both(Integer[] p0, String... p1) {} + } + + public void testMixedVarArgsConstructor() throws Exception { + Constructor constructor = + MixedVarArgs.class.getDeclaredConstructor(Integer[].class, String[].class); + checkMixedVarArgsParameter(constructor); + } + + public void testMixedVarArgsMethod() throws Exception { + Method method = MixedVarArgs.class.getDeclaredMethod("both", Integer[].class, String[].class); + checkMixedVarArgsParameter(method); + } + + private static void checkMixedVarArgsParameter(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.Integer[] arg0, java.lang.String... arg1]") + .checkParametersMetadataNotAvailable(); + + helper.getParameterTestHelper(0) + .checkGetType(Integer[].class) + .checkIsVarArg(false) + .checkGetParameterizedType("class [Ljava.lang.Integer;"); + + helper.getParameterTestHelper(1) + .checkGetType(String[].class) + .checkIsVarArg(true) + .checkGetParameterizedType("class [Ljava.lang.String;"); + } + + private static class NonVarArgs { + @SuppressWarnings("unused") + NonVarArgs(Integer[] p0) {} + @SuppressWarnings("unused") + void notVarArgs(Integer[] p0) {} + } + + public void testNonVarsArgsConstructor() throws Exception { + Constructor constructor = NonVarArgs.class.getDeclaredConstructor(Integer[].class); + checkNonVarsArgsParameter(constructor); + } + + public void testNonVarsArgsMethod() throws Exception { + Method method = NonVarArgs.class.getDeclaredMethod("notVarArgs", Integer[].class); + checkNonVarsArgsParameter(method); + } + + private static void checkNonVarsArgsParameter(Executable executable) { + ExecutableTestHelper helper = new ExecutableTestHelper(executable); + helper.checkStandardParametersBehavior() + .checkParametersToString("[java.lang.Integer[] arg0]") + .checkParametersMetadataNotAvailable(); + + helper.getParameterTestHelper(0) + .checkGetType(Integer[].class) + .checkIsVarArg(false) + .checkGetParameterizedType("class [Ljava.lang.Integer;"); + } + + public void testAnonymousClassConstructor() throws Exception { + Class outerClass = ParameterTest.class; + Class innerClass = getAnonymousClassWith1ParameterConstructor(); + Constructor constructor = innerClass.getDeclaredConstructor(outerClass); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString("[" + outerClass.getName() + " arg0]") + .checkParametersMetadataNotAvailable() + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(outerClass) + .checkGetParameterizedType("class " + outerClass.getName() + ""); + } + + private Class getAnonymousClassWith1ParameterConstructor() { + // Deliberately not implemented with a lambda. Do not refactor. + Callable anonymousClassObject = new Callable() { + @Override + public String call() throws Exception { + return ParameterTest.this.outerClassMethod(); + } + }; + return anonymousClassObject.getClass(); + } + + public void testAnonymousClassConstructor_withMetadata() throws Exception { + Class outerClass = loadTestOuterClassWithMetadata(); + Object outer = outerClass.newInstance(); + Class innerClass = (Class) outerClass.getDeclaredMethod( + "getAnonymousClassWith1ParameterConstructor").invoke(outer); + Constructor constructor = innerClass.getDeclaredConstructor(outerClass); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString("[final " + outerClass.getName() + " this$0]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkName(true /* expectedNameIsPresent */, "this$0") + .checkModifiers(32784) // 32784 == Modifier.MANDATED & Modifier.FINAL + .checkImplicitAndSynthetic(true, false) + .checkGetType(outerClass) + .checkGetParameterizedType("class " + outerClass.getName() + ""); + } + + public void testMethodClassConstructor() throws Exception { + Class outerClass = ParameterTest.class; + Class innerClass = getMethodClassWith1ImplicitParameterConstructor(); + Constructor constructor = innerClass.getDeclaredConstructor(outerClass); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString("[" + outerClass.getName() + " arg0]") + .checkParametersMetadataNotAvailable() + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkGetType(outerClass) + .checkGetParameterizedType("class " + outerClass.getName() + ""); + } + + private Class getMethodClassWith1ImplicitParameterConstructor() { + class MethodClass { + MethodClass() { + ParameterTest.this.outerClassMethod(); + } + } + return MethodClass.class; + } + + public void testMethodClassConstructor_withMetadata() throws Exception { + Class outerClass = loadTestOuterClassWithMetadata(); + Object outer = outerClass.newInstance(); + Class innerClass = (Class) outerClass.getDeclaredMethod( + "getMethodClassWith1ImplicitParameterConstructor").invoke(outer); + Constructor constructor = innerClass.getDeclaredConstructor(outerClass); + + ExecutableTestHelper helper = new ExecutableTestHelper(constructor); + helper.checkStandardParametersBehavior() + .checkParametersToString("[final " + outerClass.getName() + " this$0]") + .checkParametersNoVarArgs(); + + helper.getParameterTestHelper(0) + .checkName(true /* expectedNameIsPresent */, "this$0") + .checkModifiers(32784) // 32784 == Modifier.MANDATED & Modifier.FINAL + .checkImplicitAndSynthetic(true, false) + .checkGetType(outerClass) + .checkGetParameterizedType("class " + outerClass.getName() + ""); + } + + private static class NonIdenticalParameters { + @SuppressWarnings("unused") + void method0(String p0) {} + @SuppressWarnings("unused") + void method1(String p0) {} + } + + public void testEquals_checksExecutable() throws Exception { + Method method0 = NonIdenticalParameters.class.getDeclaredMethod("method0", String.class); + Method method1 = NonIdenticalParameters.class.getDeclaredMethod("method1", String.class); + Parameter method0P0 = method0.getParameters()[0]; + Parameter method1P0 = method1.getParameters()[0]; + assertFalse(method0P0.equals(method1P0)); + assertFalse(method1P0.equals(method0P0)); + assertTrue(method0P0.equals(method0P0)); + } + + public void testManyParameters_withMetadata() throws Exception { + int expectedParameterCount = 300; + Class[] parameterTypes = new Class[expectedParameterCount]; + Arrays.fill(parameterTypes, int.class); + Method method = getMetadataVariationsMethod("manyParameters", parameterTypes); + Parameter[] parameters = method.getParameters(); + assertEquals(expectedParameterCount, parameters.length); + + NumberFormat format = NumberFormat.getIntegerInstance(); + format.setMinimumIntegerDigits(3); + for (int i = 0; i < parameters.length; i++) { + assertEquals(true, parameters[i].isNamePresent()); + assertEquals(Modifier.FINAL, parameters[i].getModifiers()); + assertEquals("a" + format.format(i), parameters[i].getName()); + } + } + + public void testEmptyMethodParametersAnnotation_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("emptyMethodParametersAnnotation"); + assertEquals(0, method.getParameters().length); + } + + public void testTooManyAccessFlags_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("tooManyAccessFlags", String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testTooFewAccessFlags_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod( + "tooFewAccessFlags", String.class, String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testTooManyNames_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("tooManyNames", String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testTooFewNames_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("tooFewNames", String.class, String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testTooManyBoth_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("tooManyBoth", String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testTooFewBoth_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("tooFewBoth", String.class, String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testNullName_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("nullName", String.class); + Parameter parameter0 = method.getParameters()[0]; + assertEquals("arg0", parameter0.getName()); + assertEquals(Modifier.FINAL, parameter0.getModifiers()); + } + + public void testEmptyName_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("emptyName", String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testNameWithSemicolon_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("nameWithSemicolon", String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testNameWithSlash_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("nameWithSlash", String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testNameWithPeriod_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("nameWithPeriod", String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testNameWithOpenSquareBracket_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("nameWithOpenSquareBracket", String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testBadAccessModifier_withMetadata() throws Exception { + Method method = getMetadataVariationsMethod("badAccessModifier", String.class); + checkGetParametersThrowsMalformedParametersException(method); + } + + public void testBadlyFormedAnnotation() throws Exception { + Method method = getMetadataVariationsMethod("badlyFormedAnnotation", String.class); + // Badly formed annotations are treated as if the annotation is entirely absent. + Parameter parameter0 = method.getParameters()[0]; + assertFalse(parameter0.isNamePresent()); + } + + /** A non-static method that exists to be called by inner classes, lambdas, etc. */ + private String outerClassMethod() { + return "Howdy"; + } + + private static class ExecutableTestHelper { + private final Executable executable; + + ExecutableTestHelper(Executable executable) { + this.executable = executable; + } + + @DependsOnParameterMetadata + ExecutableTestHelper checkParametersToString(String expectedString) { + assertEquals(expectedString, Arrays.toString(executable.getParameters())); + return this; + } + + /** + * Combines checks that should be true of any result from + * {@link Executable#getParameters()} + */ + ExecutableTestHelper checkStandardParametersBehavior() { + return checkGetParametersClonesArray() + .checkParametersGetDeclaringExecutable() + .checkParametersEquals() + .checkParametersHashcode(); + } + + ExecutableTestHelper checkParametersGetDeclaringExecutable() { + for (Parameter p : executable.getParameters()) { + assertSame(executable, p.getDeclaringExecutable()); + } + return this; + } + + ExecutableTestHelper checkGetParametersClonesArray() { + Parameter[] parameters1 = executable.getParameters(); + Parameter[] parameters2 = executable.getParameters(); + assertNotSame(parameters1, parameters2); + + assertEquals(parameters1.length, parameters2.length); + for (int i = 0; i < parameters1.length; i++) { + assertSame(parameters1[i], parameters2[i]); + } + return this; + } + + ExecutableTestHelper checkParametersEquals() { + Parameter[] parameters = executable.getParameters(); + for (int i = 0; i < parameters.length; i++) { + assertEquals(parameters[i], parameters[i]); + if (i > 0) { + assertFalse(parameters[0].equals(parameters[i])); + assertFalse(parameters[i].equals(parameters[0])); + } + } + return this; + } + + ExecutableTestHelper checkParametersHashcode() { + for (Parameter parameter : executable.getParameters()) { + // Not much to assert. Just call the method and check it is consistent. + assertEquals(parameter.hashCode(), parameter.hashCode()); + } + return this; + } + + @DependsOnParameterMetadata + ExecutableTestHelper checkParametersMetadataNotAvailable() { + ParameterTestHelper[] parameterTestHelpers = getParameterTestHelpers(); + for (int i = 0; i < parameterTestHelpers.length; i++) { + ParameterTestHelper parameterTestHelper = parameterTestHelpers[i]; + parameterTestHelper.checkName(false, "arg" + i) + .checkImplicitAndSynthetic(false, false) + .checkModifiers(0); + } + return this; + } + + /** + * Checks that non of the parameters return {@code true} for {@link Parameter#isVarArgs()}. + */ + ExecutableTestHelper checkParametersNoVarArgs() { + for (ParameterTestHelper parameterTestHelper : getParameterTestHelpers()) { + parameterTestHelper.checkIsVarArg(false); + } + return this; + } + + ParameterTestHelper getParameterTestHelper(int index) { + return new ParameterTestHelper(executable.getParameters()[index]); + } + + private ParameterTestHelper[] getParameterTestHelpers() { + final int parameterCount = executable.getParameterCount(); + ParameterTestHelper[] parameterTestHelpers = new ParameterTestHelper[parameterCount]; + for (int i = 0; i < parameterCount; i++) { + parameterTestHelpers[i] = getParameterTestHelper(i); + } + return parameterTestHelpers; + } + + private static class ParameterTestHelper { + private final Parameter parameter; + + ParameterTestHelper(Parameter parameter) { + this.parameter = parameter; + } + + ParameterTestHelper checkGetType(Class expectedType) { + assertEquals(expectedType, parameter.getType()); + return this; + } + + @DependsOnParameterMetadata + ParameterTestHelper checkName(boolean expectedIsNamePresent, String expectedName) { + assertEquals(expectedIsNamePresent, parameter.isNamePresent()); + assertEquals(expectedName, parameter.getName()); + return this; + } + + @DependsOnParameterMetadata + ParameterTestHelper checkModifiers(int expectedModifiers) { + assertEquals(expectedModifiers, parameter.getModifiers()); + return this; + } + + ParameterTestHelper checkGetParameterizedType(String expectedParameterizedTypeString) { + assertEquals( + expectedParameterizedTypeString, + parameter.getParameterizedType().toString()); + return this; + } + + @DependsOnParameterMetadata + ParameterTestHelper checkImplicitAndSynthetic( + boolean expectedIsImplicit, boolean expectedIsSynthetic) { + assertEquals(expectedIsImplicit, parameter.isImplicit()); + assertEquals(expectedIsSynthetic, parameter.isSynthetic()); + return this; + } + + ParameterTestHelper checkIsVarArg(boolean expectedIsVarArg) { + assertEquals(expectedIsVarArg, parameter.isVarArgs()); + return this; + } + } + } + + private static ClassLoader createClassLoaderForDexResource(File dexDir, String resourceName) + throws Exception { + File dexFile = new File(dexDir, resourceName); + copyResource(resourceName, dexFile); + return new PathClassLoader(dexFile.getAbsolutePath(), ClassLoader.getSystemClassLoader()); + } + + /** + * Copy a resource in the libcore/java/lang/reflect/parameter/ resource path to the indicated + * target file. + */ + private static void copyResource(String resourceName, File destination) throws Exception { + assertFalse(destination.exists()); + ClassLoader classLoader = ParameterTest.class.getClassLoader(); + assertNotNull(classLoader); + + final String RESOURCE_PATH = "libcore/java/lang/reflect/parameter/"; + String fullResourcePath = RESOURCE_PATH + resourceName; + try (InputStream in = classLoader.getResourceAsStream(fullResourcePath); + FileOutputStream out = new FileOutputStream(destination)) { + if (in == null) { + throw new IllegalStateException("Resource not found: " + fullResourcePath); + } + Streams.copy(in, out); + } + } + + /** + * Loads an inner class from the ParameterMetadataTestClasses class defined in a separate dex + * file. See src/test/java/libcore/java/lang/reflect/parameter/ for the associated source code. + */ + private Class loadTestInnerClassWithMetadata(String name) throws Exception { + return classesWithMetadataClassLoader.loadClass( + "libcore.java.lang.reflect.parameter.ParameterMetadataTestClasses$" + name); + } + + /** + * Loads the ParameterMetadataTestClasses class defined in a separate dex file. + * See src/test/java/libcore/java/lang/reflect/parameter/ for the associated source code. + */ + private Class loadTestOuterClassWithMetadata() throws Exception { + return classesWithMetadataClassLoader.loadClass( + "libcore.java.lang.reflect.parameter.ParameterMetadataTestClasses"); + } + + /** + * Loads a method from the MetadataVariations class defined in a separate dex file. See + * src/test/java/libcore/java/lang/reflect/parameter/ for the associated source code. + */ + private Method getMetadataVariationsMethod(String methodName, Class... parameterTypes) + throws Exception { + Class metadataVariationsClass = metadataVariationsClassLoader.loadClass( + "libcore.java.lang.reflect.parameter.MetadataVariations"); + return metadataVariationsClass.getDeclaredMethod(methodName, parameterTypes); + } + + private static void checkGetParametersThrowsMalformedParametersException(Method method) { + try { + method.getParameters(); + fail(); + } catch (MalformedParametersException expected) {} + } +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java b/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java index 1950bf34d..f69029563 100644 --- a/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java +++ b/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java @@ -16,22 +16,28 @@ package libcore.java.lang.reflect; +import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.AbstractCollection; import java.util.AbstractList; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.RandomAccess; import java.util.Set; -import junit.framework.Assert; import junit.framework.TestCase; public final class ReflectionTest extends TestCase { @@ -430,4 +436,215 @@ enum TrafficLights { void foobar() {} } } + + public void testGetEnclosingClass() { + assertNull(ReflectionTest.class.getEnclosingClass()); + assertEquals(ReflectionTest.class, Foo.class.getEnclosingClass()); + assertEquals(ReflectionTest.class, HasMemberClassesInterface.class.getEnclosingClass()); + assertEquals(HasMemberClassesInterface.class, + HasMemberClassesInterface.D.class.getEnclosingClass()); + assertEquals(ReflectionTest.class, Foo.class.getEnclosingClass()); + } + + public void testGetDeclaringClass() { + assertNull(ReflectionTest.class.getDeclaringClass()); + assertEquals(ReflectionTest.class, Foo.class.getDeclaringClass()); + assertEquals(ReflectionTest.class, HasMemberClassesInterface.class.getDeclaringClass()); + assertEquals(HasMemberClassesInterface.class, + HasMemberClassesInterface.D.class.getDeclaringClass()); + } + + public void testGetEnclosingClassIsTransitiveForClassesDefinedInAMethod() { + class C {} + assertEquals(ReflectionTest.class, C.class.getEnclosingClass()); + } + + public void testGetDeclaringClassIsNotTransitiveForClassesDefinedInAMethod() { + class C {} + assertEquals(null, C.class.getDeclaringClass()); + } + + public void testGetEnclosingMethodIsNotTransitive() { + class C { + class D {} + } + assertEquals(null, C.D.class.getEnclosingMethod()); + } + + private static final Object staticAnonymous = new Object() {}; + + public void testStaticFieldAnonymousClass() { + // The class declared in the is enclosed by the 's class. + // http://b/11245138 + assertEquals(ReflectionTest.class, staticAnonymous.getClass().getEnclosingClass()); + // However, because it is anonymous, it has no declaring class. + // https://code.google.com/p/android/issues/detail?id=61003 + assertNull(staticAnonymous.getClass().getDeclaringClass()); + // Because the class is declared in which is not exposed through reflection, + // it has no enclosing method or constructor. + assertNull(staticAnonymous.getClass().getEnclosingMethod()); + assertNull(staticAnonymous.getClass().getEnclosingConstructor()); + } + + public void testGetEnclosingMethodOfTopLevelClass() { + assertNull(ReflectionTest.class.getEnclosingMethod()); + } + + public void testGetEnclosingConstructorOfTopLevelClass() { + assertNull(ReflectionTest.class.getEnclosingConstructor()); + } + + public void testClassEnclosedByConstructor() throws Exception { + Foo foo = new Foo("string"); + assertEquals(Foo.class, foo.c.getEnclosingClass()); + assertEquals(Foo.class.getDeclaredConstructor(String.class), + foo.c.getEnclosingConstructor()); + assertNull(foo.c.getEnclosingMethod()); + assertNull(foo.c.getDeclaringClass()); + } + + public void testClassEnclosedByMethod() throws Exception { + Foo foo = new Foo(); + foo.foo("string"); + assertEquals(Foo.class, foo.c.getEnclosingClass()); + assertNull(foo.c.getEnclosingConstructor()); + assertEquals(Foo.class.getDeclaredMethod("foo", String.class), + foo.c.getEnclosingMethod()); + assertNull(foo.c.getDeclaringClass()); + } + + public void testGetClasses() throws Exception { + // getClasses() doesn't include classes inherited from interfaces! + assertSetEquals(HasMemberClasses.class.getClasses(), + HasMemberClassesSuperclass.B.class, HasMemberClasses.H.class); + } + + public void testGetDeclaredClasses() throws Exception { + assertSetEquals(HasMemberClasses.class.getDeclaredClasses(), + HasMemberClasses.G.class, HasMemberClasses.H.class, HasMemberClasses.I.class, + HasMemberClasses.J.class, HasMemberClasses.K.class, HasMemberClasses.L.class); + } + + public void testConstructorGetExceptions() throws Exception { + assertSetEquals(HasThrows.class.getConstructor().getExceptionTypes(), + IOException.class, InvocationTargetException.class, IllegalStateException.class); + assertSetEquals(HasThrows.class.getConstructor(Void.class).getExceptionTypes()); + } + + public void testClassMethodGetExceptions() throws Exception { + assertSetEquals(HasThrows.class.getMethod("foo").getExceptionTypes(), + IOException.class, InvocationTargetException.class, IllegalStateException.class); + assertSetEquals(HasThrows.class.getMethod("foo", Void.class).getExceptionTypes()); + } + + public void testProxyMethodGetExceptions() throws Exception { + InvocationHandler emptyInvocationHandler = new InvocationHandler() { + @Override public Object invoke(Object proxy, Method method, Object[] args) { + return null; + } + }; + + Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), + new Class[] { ThrowsInterface.class }, emptyInvocationHandler); + assertSetEquals(proxy.getClass().getMethod("foo").getExceptionTypes(), + IOException.class, InvocationTargetException.class, IllegalStateException.class); + assertSetEquals(proxy.getClass().getMethod("foo", Void.class).getExceptionTypes()); + } + + public void testClassModifiers() { + int modifiers = ReflectionTest.class.getModifiers(); + assertTrue(Modifier.isPublic(modifiers)); + assertFalse(Modifier.isProtected(modifiers)); + assertFalse(Modifier.isPrivate(modifiers)); + assertFalse(Modifier.isAbstract(modifiers)); + assertFalse(Modifier.isStatic(modifiers)); + assertTrue(Modifier.isFinal(modifiers)); + assertFalse(Modifier.isStrict(modifiers)); + } + + public void testInnerClassModifiers() { + int modifiers = Foo.class.getModifiers(); + assertFalse(Modifier.isPublic(modifiers)); + assertFalse(Modifier.isProtected(modifiers)); + assertTrue(Modifier.isPrivate(modifiers)); + assertFalse(Modifier.isAbstract(modifiers)); + assertTrue(Modifier.isStatic(modifiers)); + assertFalse(Modifier.isFinal(modifiers)); + assertFalse(Modifier.isStrict(modifiers)); + } + + public void testAnonymousClassModifiers() { + int modifiers = staticAnonymous.getClass().getModifiers(); + assertFalse(Modifier.isPublic(modifiers)); + assertFalse(Modifier.isProtected(modifiers)); + assertFalse(Modifier.isPrivate(modifiers)); + assertFalse(Modifier.isAbstract(modifiers)); + assertTrue(Modifier.isStatic(modifiers)); + assertFalse(Modifier.isFinal(modifiers)); + assertFalse(Modifier.isStrict(modifiers)); + } + + public void testInnerClassName() { + assertEquals("ReflectionTest", ReflectionTest.class.getSimpleName()); + assertEquals("Foo", Foo.class.getSimpleName()); + assertEquals("", staticAnonymous.getClass().getSimpleName()); + } + + private static class Foo { + Class c; + private Foo() { + } + private Foo(String s) { + c = new Object() {}.getClass(); + } + private Foo(int i) { + c = new Object() {}.getClass(); + } + private void foo(String s) { + c = new Object() {}.getClass(); + } + private void foo(int i) { + c = new Object() {}.getClass(); + } + } + + static class HasMemberClassesSuperclass { + class A {} + public class B {} + static class C {} + } + + public interface HasMemberClassesInterface { + class D {} + public class E {} + static class F {} + } + + public static class HasMemberClasses extends HasMemberClassesSuperclass + implements HasMemberClassesInterface { + class G {} + public class H {} + static class I {} + enum J {} + interface K {} + @interface L {} + } + + public static class HasThrows { + public HasThrows() throws IOException, InvocationTargetException, IllegalStateException {} + public HasThrows(Void v) {} + public void foo() throws IOException, InvocationTargetException, IllegalStateException {} + public void foo(Void v) {} + } + + public static interface ThrowsInterface { + void foo() throws IOException, InvocationTargetException, IllegalStateException; + void foo(Void v); + } + + private void assertSetEquals(Object[] actual, Object... expected) { + Set actualSet = new HashSet(Arrays.asList(actual)); + Set expectedSet = new HashSet(Arrays.asList(expected)); + assertEquals(expectedSet, actualSet); + } } diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java new file mode 100644 index 000000000..aa14bd345 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations; + +import junit.framework.TestCase; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationD; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; + +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods; + +/** + * Tests for the {@link java.lang.reflect.AnnotatedElement} methods from the {@link Parameter} + * objects obtained from both {@link Constructor} and {@link Method}. + */ +public class AnnotatedElementParameterTest extends TestCase { + + private static class MethodClass { + public void methodWithoutAnnotatedParameters(String parameter1, String parameter2) {} + + public void methodWithAnnotatedParameters(@AnnotationB @AnnotationD String parameter1, + @AnnotationC @AnnotationD String parameter2) {} + } + + public void testMethodParameterAnnotations() throws Exception { + Class c = MethodClass.class; + { + Parameter[] parameters = c.getDeclaredMethod( + "methodWithoutAnnotatedParameters", String.class, String.class).getParameters(); + Parameter parameter0 = parameters[0]; + checkAnnotatedElementPresentMethods(parameter0); + + Parameter parameter1 = parameters[1]; + checkAnnotatedElementPresentMethods(parameter1); + } + { + Parameter[] parameters = c.getDeclaredMethod( + "methodWithAnnotatedParameters", String.class, String.class).getParameters(); + + Parameter parameter0 = parameters[0]; + checkAnnotatedElementPresentMethods(parameter0, AnnotationB.class, AnnotationD.class); + + Parameter parameter1 = parameters[1]; + checkAnnotatedElementPresentMethods(parameter1, AnnotationC.class, AnnotationD.class); + } + } + + private static class ConstructorClass { + // No annotations. + public ConstructorClass(Integer parameter1, Integer parameter2) {} + + // Annotations. + public ConstructorClass(@AnnotationB @AnnotationD String parameter1, + @AnnotationC @AnnotationD String parameter2) {} + } + + public void testConstructorParameterAnnotations() throws Exception { + Class c = ConstructorClass.class; + { + Parameter[] parameters = + c.getDeclaredConstructor(Integer.class, Integer.class).getParameters(); + Parameter parameter0 = parameters[0]; + checkAnnotatedElementPresentMethods(parameter0); + + Parameter parameter1 = parameters[1]; + checkAnnotatedElementPresentMethods(parameter1); + } + { + Parameter[] parameters = + c.getDeclaredConstructor(String.class, String.class).getParameters(); + + Parameter parameter0 = parameters[0]; + checkAnnotatedElementPresentMethods(parameter0, AnnotationB.class, AnnotationD.class); + + Parameter parameter1 = parameters[1]; + checkAnnotatedElementPresentMethods(parameter1, AnnotationC.class, AnnotationD.class); + } + } + + private static class AnnotatedMethodClass { + void noAnnotation(String p0) {} + + void multipleAnnotationOddity( + @Repeated(1) @Container({@Repeated(2), @Repeated(3)}) String p0) {} + + void multipleAnnotationExplicitSingle(@Container({@Repeated(1)}) String p0) {} + + void multipleAnnotation(@Repeated(1) @Repeated(2) String p0) {} + + void singleAnnotation(@Repeated(1) String p0) {} + + static Method getMethodWithoutAnnotations() throws Exception { + return AnnotatedMethodClass.class.getDeclaredMethod("noAnnotation", String.class); + } + + static Method getMethodMultipleAnnotationOddity() throws Exception { + return AnnotatedMethodClass.class.getDeclaredMethod( + "multipleAnnotationOddity", String.class); + } + + static Method getMethodMultipleAnnotationExplicitSingle() throws Exception { + return AnnotatedMethodClass.class.getDeclaredMethod( + "multipleAnnotationExplicitSingle", String.class); + } + + static Method getMethodMultipleAnnotation() throws Exception { + return AnnotatedMethodClass.class.getDeclaredMethod("multipleAnnotation", String.class); + } + + static Method getMethodSingleAnnotation() throws Exception { + return AnnotatedMethodClass.class.getDeclaredMethod("singleAnnotation", String.class); + } + } + + // Tests for isAnnotationPresent and getDeclaredAnnotation. + public void testMethodDeclaredAnnotation() throws Exception { + Class repeated = Repeated.class; + checkParameter0DeclaredAnnotation( + AnnotatedMethodClass.getMethodWithoutAnnotations(), + repeated, null); + checkParameter0DeclaredAnnotation( + AnnotatedMethodClass.getMethodMultipleAnnotationOddity(), + repeated, "@Repeated(1)"); + checkParameter0DeclaredAnnotation( + AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(), + repeated, null); + checkParameter0DeclaredAnnotation( + AnnotatedMethodClass.getMethodMultipleAnnotation(), + repeated, null); + checkParameter0DeclaredAnnotation( + AnnotatedMethodClass.getMethodSingleAnnotation(), + repeated, "@Repeated(1)"); + + Class container = Container.class; + checkParameter0DeclaredAnnotation( + AnnotatedMethodClass.getMethodWithoutAnnotations(), + container, null); + checkParameter0DeclaredAnnotation( + AnnotatedMethodClass.getMethodMultipleAnnotationOddity(), + container, "@Container({@Repeated(2), @Repeated(3)})"); + checkParameter0DeclaredAnnotation( + AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(), + container, "@Container({@Repeated(1)})"); + checkParameter0DeclaredAnnotation( + AnnotatedMethodClass.getMethodMultipleAnnotation(), + container, "@Container({@Repeated(1), @Repeated(2)})"); + checkParameter0DeclaredAnnotation( + AnnotatedMethodClass.getMethodSingleAnnotation(), + container, null); + } + + private static class AnnotatedConstructorClass { + public AnnotatedConstructorClass(Boolean p0) {} + + public AnnotatedConstructorClass( + @Repeated(1) @Container({@Repeated(2), @Repeated(3)}) Long p0) {} + + public AnnotatedConstructorClass(@Container({@Repeated(1)}) Double p0) {} + + public AnnotatedConstructorClass(@Repeated(1) @Repeated(2) Integer p0) {} + + public AnnotatedConstructorClass(@Repeated(1) String p0) {} + + static Constructor getConstructorWithoutAnnotations() throws Exception { + return AnnotatedConstructorClass.class.getDeclaredConstructor(Boolean.class); + } + + static Constructor getConstructorMultipleAnnotationOddity() throws Exception { + return AnnotatedConstructorClass.class.getDeclaredConstructor(Long.class); + } + + static Constructor getConstructorMultipleAnnotationExplicitSingle() + throws Exception { + return AnnotatedConstructorClass.class.getDeclaredConstructor(Double.class); + } + + static Constructor getConstructorSingleAnnotation() throws Exception { + return AnnotatedConstructorClass.class.getDeclaredConstructor(String.class); + } + + static Constructor getConstructorMultipleAnnotation() throws Exception { + return AnnotatedConstructorClass.class.getDeclaredConstructor(Integer.class); + } + } + + // Tests for isAnnotationPresent and getDeclaredAnnotation. + public void testConstructorDeclaredAnnotation() throws Exception { + Class repeated = Repeated.class; + checkParameter0DeclaredAnnotation( + AnnotatedConstructorClass.getConstructorWithoutAnnotations(), + repeated, null); + checkParameter0DeclaredAnnotation( + AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(), + repeated, "@Repeated(1)"); + checkParameter0DeclaredAnnotation( + AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(), + repeated, null); + checkParameter0DeclaredAnnotation( + AnnotatedConstructorClass.getConstructorMultipleAnnotation(), + repeated, null); + checkParameter0DeclaredAnnotation( + AnnotatedConstructorClass.getConstructorSingleAnnotation(), + repeated, "@Repeated(1)"); + + Class container = Container.class; + checkParameter0DeclaredAnnotation( + AnnotatedConstructorClass.getConstructorWithoutAnnotations(), + container, null); + checkParameter0DeclaredAnnotation( + AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(), + container, "@Container({@Repeated(2), @Repeated(3)})"); + checkParameter0DeclaredAnnotation( + AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(), + container, "@Container({@Repeated(1)})"); + checkParameter0DeclaredAnnotation( + AnnotatedConstructorClass.getConstructorMultipleAnnotation(), + container, "@Container({@Repeated(1), @Repeated(2)})"); + checkParameter0DeclaredAnnotation( + AnnotatedConstructorClass.getConstructorSingleAnnotation(), + container, null); + } + + private static void checkParameter0DeclaredAnnotation( + Executable executable, Class annotationType, + String expectedAnnotationString) throws Exception { + Parameter parameter = executable.getParameters()[0]; + + // isAnnotationPresent + assertIsAnnotationPresent(parameter, annotationType, expectedAnnotationString != null); + + // getDeclaredAnnotation + assertGetDeclaredAnnotation(parameter, annotationType, expectedAnnotationString); + } + + public void testMethodGetDeclaredAnnotationsByType() throws Exception { + Class repeated = Repeated.class; + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedMethodClass.getMethodWithoutAnnotations(), + repeated, EXPECT_EMPTY); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotationOddity(), + repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(), + repeated, "@Repeated(1)"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotation(), + repeated, "@Repeated(1)", "@Repeated(2)"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedMethodClass.getMethodSingleAnnotation(), + repeated, "@Repeated(1)"); + + Class container = Container.class; + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedMethodClass.getMethodWithoutAnnotations(), + container, EXPECT_EMPTY); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotationOddity(), + container, "@Container({@Repeated(2), @Repeated(3)})"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(), + container, "@Container({@Repeated(1)})"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotation(), + container, "@Container({@Repeated(1), @Repeated(2)})"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedMethodClass.getMethodSingleAnnotation(), + container, EXPECT_EMPTY); + } + + public void testConstructorGetDeclaredAnnotationsByType() throws Exception { + Class repeated = Repeated.class; + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedConstructorClass.getConstructorWithoutAnnotations(), + repeated, EXPECT_EMPTY); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(), + repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(), + repeated, "@Repeated(1)"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotation(), + repeated, "@Repeated(1)", "@Repeated(2)"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedConstructorClass.getConstructorSingleAnnotation(), + repeated, "@Repeated(1)"); + + Class container = Container.class; + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedConstructorClass.getConstructorWithoutAnnotations(), + container, EXPECT_EMPTY); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(), + container, "@Container({@Repeated(2), @Repeated(3)})"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(), + container, "@Container({@Repeated(1)})"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotation(), + container, "@Container({@Repeated(1), @Repeated(2)})"); + checkParameter0GetDeclaredAnnotationsByType( + AnnotatedConstructorClass.getConstructorSingleAnnotation(), + container, EXPECT_EMPTY); + } + + private static void checkParameter0GetDeclaredAnnotationsByType( + Executable executable, Class annotationType, + String... expectedAnnotationStrings) throws Exception { + Parameter parameter = executable.getParameters()[0]; + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + parameter, annotationType, expectedAnnotationStrings); + } + + public void testMethodGetAnnotationsByType() throws Exception { + Class repeated = Repeated.class; + checkParameter0GetAnnotationsByType( + AnnotatedMethodClass.getMethodWithoutAnnotations(), + repeated, EXPECT_EMPTY); + checkParameter0GetAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotationOddity(), + repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + checkParameter0GetAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(), + repeated, "@Repeated(1)"); + checkParameter0GetAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotation(), + repeated, "@Repeated(1)", "@Repeated(2)"); + checkParameter0GetAnnotationsByType( + AnnotatedMethodClass.getMethodSingleAnnotation(), + repeated, "@Repeated(1)"); + + Class container = Container.class; + checkParameter0GetAnnotationsByType( + AnnotatedMethodClass.getMethodWithoutAnnotations(), + container, EXPECT_EMPTY); + checkParameter0GetAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotationOddity(), + container, "@Container({@Repeated(2), @Repeated(3)})"); + checkParameter0GetAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(), + container, "@Container({@Repeated(1)})"); + checkParameter0GetAnnotationsByType( + AnnotatedMethodClass.getMethodMultipleAnnotation(), + container, "@Container({@Repeated(1), @Repeated(2)})"); + checkParameter0GetAnnotationsByType( + AnnotatedMethodClass.getMethodSingleAnnotation(), + container, EXPECT_EMPTY); + } + + public void testConstructorGetAnnotationsByType() throws Exception { + Class repeated = Repeated.class; + checkParameter0GetAnnotationsByType( + AnnotatedConstructorClass.getConstructorWithoutAnnotations(), + repeated, EXPECT_EMPTY); + checkParameter0GetAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(), + repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + checkParameter0GetAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(), + repeated, "@Repeated(1)"); + checkParameter0GetAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotation(), + repeated, "@Repeated(1)", "@Repeated(2)"); + checkParameter0GetAnnotationsByType( + AnnotatedConstructorClass.getConstructorSingleAnnotation(), + repeated, "@Repeated(1)"); + + Class container = Container.class; + checkParameter0GetAnnotationsByType( + AnnotatedConstructorClass.getConstructorWithoutAnnotations(), + container, EXPECT_EMPTY); + checkParameter0GetAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(), + container, "@Container({@Repeated(2), @Repeated(3)})"); + checkParameter0GetAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(), + container, "@Container({@Repeated(1)})"); + checkParameter0GetAnnotationsByType( + AnnotatedConstructorClass.getConstructorMultipleAnnotation(), + container, "@Container({@Repeated(1), @Repeated(2)})"); + checkParameter0GetAnnotationsByType( + AnnotatedConstructorClass.getConstructorSingleAnnotation(), + container, EXPECT_EMPTY); + } + + private static void checkParameter0GetAnnotationsByType( + Executable executable, Class annotationType, + String... expectedAnnotationStrings) throws Exception { + Parameter parameter = executable.getParameters()[0]; + AnnotatedElementTestSupport.assertGetAnnotationsByType( + parameter, annotationType, expectedAnnotationStrings); + } + + /** + * As an inner class the constructor will actually have two parameters: the first, referencing + * the enclosing object, is inserted by the compiler. + */ + class InnerClass { + InnerClass(@Repeated(1) String p1) {} + } + + /** Special case testing for a compiler-generated constructor parameter. */ + public void testImplicitConstructorParameters_singleAnnotation() throws Exception { + Constructor constructor = + InnerClass.class.getDeclaredConstructor( + AnnotatedElementParameterTest.class, String.class); + Parameter[] parameters = constructor.getParameters(); + + // The compiler-generated constructor should have no annotations. + Parameter parameter0 = parameters[0]; + AnnotatedElementTestSupport.assertGetAnnotationsByType( + parameter0, Repeated.class, new String[0]); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + parameter0, Repeated.class, new String[0]); + AnnotatedElementTestSupport.assertGetDeclaredAnnotation( + parameter0, Repeated.class, null); + AnnotatedElementTestSupport.assertIsAnnotationPresent(parameter0, Repeated.class, false); + + // The annotation should remain on the correct parameter. + Parameter parameter1 = parameters[1]; + AnnotatedElementTestSupport.assertGetAnnotationsByType( + parameter1, Repeated.class, new String[] {"@Repeated(1)"}); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + parameter1, Repeated.class, new String[] {"@Repeated(1)"}); + AnnotatedElementTestSupport.assertGetDeclaredAnnotation( + parameter1, Repeated.class, "@Repeated(1)"); + AnnotatedElementTestSupport.assertIsAnnotationPresent( + parameter1, Repeated.class, true); + } +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java new file mode 100644 index 000000000..b17fbff75 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.AnnotatedElement; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.StringJoiner; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; + +/** + * Utility methods and annotation definitions for use when testing implementations of + * AnnotatedElement. + * + *

For compactness, the repeated annotation methods that take strings use a format based on Java + * syntax rather than the toString() of annotations. For example, "@Repeated(1)" rather than + * "@libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated(value=1)". Use + * {@link #EXPECT_EMPTY} to indicate "no annotationed expected". + */ +public class AnnotatedElementTestSupport { + + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationA {} + + @Inherited + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationB {} + + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationC {} + + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationD {} + + @Retention(RetentionPolicy.RUNTIME) + @Inherited + @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, + ElementType.PARAMETER, ElementType.PACKAGE }) + public @interface Container { + Repeated[] value(); + } + + @Repeatable(Container.class) + @Retention(RetentionPolicy.RUNTIME) + @Inherited + @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, + ElementType.PARAMETER, ElementType.PACKAGE }) + public @interface Repeated { + int value(); + } + + /** + * A named constant that can be used with assert methods below that take + * "String[] expectedAnnotationStrings" as their final argument to indicate "none". + */ + public static final String[] EXPECT_EMPTY = new String[0]; + + private AnnotatedElementTestSupport() { + } + + /** + * Test the {@link AnnotatedElement} methods associated with "presence". i.e. methods that + * deal with annotations being "present" (i.e. "direct" or "inherited" annotations, not + * "indirect"). + * + *

Asserts that calling {@link AnnotatedElement#getAnnotations()} on the supplied element + * returns annotations of the supplied expected classes. + * + *

Where the expected classes contains some subset from + * {@link AnnotationA}, {@link AnnotationB}, {@link AnnotationC}, {@link AnnotationD} this + * method also asserts that {@link AnnotatedElement#isAnnotationPresent(Class)} and + * {@link AnnotatedElement#getAnnotation(Class)} works as expected. + * + *

This method also confirms that {@link AnnotatedElement#isAnnotationPresent(Class)} and + * {@link AnnotatedElement#getAnnotation(Class)} work correctly with a {@code null} argument. + */ + static void checkAnnotatedElementPresentMethods( + AnnotatedElement element, Class... expectedAnnotations) { + Set> actualTypes = annotationsToTypes(element.getAnnotations()); + Set> expectedTypes = set(expectedAnnotations); + assertEquals(expectedTypes, actualTypes); + + // getAnnotations() should be consistent with isAnnotationPresent() and getAnnotation() + assertPresent(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class); + assertPresent(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class); + assertPresent(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class); + assertPresent(expectedTypes.contains(AnnotationD.class), element, AnnotationD.class); + + try { + element.isAnnotationPresent(null); + fail(); + } catch (NullPointerException expected) { + } + + try { + element.getAnnotation(null); + fail(); + } catch (NullPointerException expected) { + } + } + + /** + * Test the {@link AnnotatedElement} methods associated with "direct" annotations. + * + *

Asserts that calling {@link AnnotatedElement#getDeclaredAnnotations()} on the supplied + * element returns annotations of the supplied expected classes. + * + *

Where the expected classes contains some subset from + * {@link AnnotationA}, {@link AnnotationB} and {@link AnnotationC}, this method also asserts + * that {@link AnnotatedElement#getDeclaredAnnotation(Class)} works as expected. + * + *

This method also confirms that {@link AnnotatedElement#isAnnotationPresent(Class)} and + * {@link AnnotatedElement#getAnnotation(Class)} work correctly with a {@code null} argument. + */ + static void checkAnnotatedElementDirectMethods( + AnnotatedElement element, + Class... expectedDeclaredAnnotations) { + Set> actualTypes = annotationsToTypes(element.getDeclaredAnnotations()); + Set> expectedTypes = set(expectedDeclaredAnnotations); + assertEquals(expectedTypes, actualTypes); + + assertDeclared(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class); + assertDeclared(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class); + assertDeclared(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class); + + try { + element.getDeclaredAnnotation(null); + fail(); + } catch (NullPointerException expected) { + } + } + + /** + * Extracts the annotation types ({@link Annotation#annotationType()} from the supplied + * annotations. + */ + static Set> annotationsToTypes(Annotation[] annotations) { + Set> result = new HashSet>(); + for (Annotation annotation : annotations) { + result.add(annotation.annotationType()); + } + return result; + } + + private static void assertPresent(boolean present, AnnotatedElement element, + Class annotation) { + if (present) { + assertNotNull(element.getAnnotation(annotation)); + assertTrue(element.isAnnotationPresent(annotation)); + } else { + assertNull(element.getAnnotation(annotation)); + assertFalse(element.isAnnotationPresent(annotation)); + } + } + + private static void assertDeclared(boolean present, AnnotatedElement element, + Class annotation) { + if (present) { + assertNotNull(element.getDeclaredAnnotation(annotation)); + } else { + assertNull(element.getDeclaredAnnotation(annotation)); + } + } + + @SafeVarargs + static Set set(T... instances) { + return new HashSet<>(Arrays.asList(instances)); + } + + /** + * Asserts that {@link AnnotatedElement#isAnnotationPresent(Class)} returns the expected result. + */ + static void assertIsAnnotationPresent( + AnnotatedElement element, Class annotationType, + boolean expected) { + assertEquals("element.isAnnotationPresent() for " + element + " and " + annotationType, + expected, element.isAnnotationPresent(annotationType)); + } + + /** + * Asserts that {@link AnnotatedElement#getDeclaredAnnotation(Class)} returns the expected + * result. The result is specified using a String. See {@link AnnotatedElementTestSupport} for + * the string syntax. + */ + static void assertGetDeclaredAnnotation(AnnotatedElement annotatedElement, + Class annotationType, String expectedAnnotationString) { + Annotation annotation = annotatedElement.getDeclaredAnnotation(annotationType); + assertAnnotationMatches(annotation, expectedAnnotationString); + } + + /** + * Asserts that {@link AnnotatedElement#getDeclaredAnnotationsByType(Class)} returns the + * expected result. The result is specified using a String. See + * {@link AnnotatedElementTestSupport} for the string syntax. + */ + static void assertGetDeclaredAnnotationsByType( + AnnotatedElement annotatedElement, Class annotationType, + String[] expectedAnnotationStrings) { + Annotation[] annotations = annotatedElement.getDeclaredAnnotationsByType(annotationType); + assertAnnotationsMatch(annotations, expectedAnnotationStrings); + } + + /** + * Asserts that {@link AnnotatedElement#getAnnotationsByType(Class)} returns the + * expected result. The result is specified using a String. See + * {@link AnnotatedElementTestSupport} for the string syntax. + */ + static void assertGetAnnotationsByType(AnnotatedElement annotatedElement, + Class annotationType, String[] expectedAnnotationStrings) + throws Exception { + Annotation[] annotations = annotatedElement.getAnnotationsByType(annotationType); + assertAnnotationsMatch(annotations, expectedAnnotationStrings); + } + + private static void assertAnnotationMatches( + Annotation annotation, String expectedAnnotationString) { + if (expectedAnnotationString == null) { + assertNull(annotation); + } else { + assertNotNull(annotation); + assertEquals(expectedAnnotationString, createAnnotationTestString(annotation)); + } + } + + /** + * Asserts that the supplied annotations match the expectation Strings. See + * {@link AnnotatedElementTestSupport} for the string syntax. + */ + static void assertAnnotationsMatch(Annotation[] annotations, + String[] expectedAnnotationStrings) { + + // Due to Android's dex format insisting that Annotations are sorted by name the ordering of + // annotations is determined by the (simple?) name of the Annotation, not just the order + // that they are defined in the source. Tests have to be sensitive to that when handling + // mixed usage of "Container" and "Repeated" - the "Container" annotations will be + // discovered before "Repeated" due to their sort ordering. + // + // This code assumes that repeated annotations with the same name will be specified in the + // source their natural sort order when attributes are considered, just to make the testing + // simpler. + // e.g. @Repeated(1) @Repeated(2), never @Repeated(2) @Repeated(1) + + // Sorting the expected and actual strings _should_ work providing the assumptions above + // hold. It may mask random ordering issues but it's harder to deal with that while the + // source ordering is no observed. Providing no developers are ascribing meaning to the + // relative order of annotations things should be ok. + Arrays.sort(expectedAnnotationStrings); + + String[] actualAnnotationStrings = createAnnotationTestStrings(annotations); + Arrays.sort(actualAnnotationStrings); + + assertEquals( + Arrays.asList(expectedAnnotationStrings), + Arrays.asList(actualAnnotationStrings)); + } + + private static String[] createAnnotationTestStrings(Annotation[] annotations) { + String[] annotationStrings = new String[annotations.length]; + for (int i = 0; i < annotations.length; i++) { + annotationStrings[i] = createAnnotationTestString(annotations[i]); + } + return annotationStrings; + } + + private static String createAnnotationTestString(Annotation annotation) { + return "@" + annotation.annotationType().getSimpleName() + createArgumentsTestString( + annotation); + } + + private static String createArgumentsTestString(Annotation annotation) { + if (annotation instanceof Repeated) { + Repeated repeated = (Repeated) annotation; + return "(" + repeated.value() + ")"; + } else if (annotation instanceof Container) { + Container container = (Container) annotation; + String[] repeatedValues = createAnnotationTestStrings(container.value()); + StringJoiner joiner = new StringJoiner(", ", "{", "}"); + for (String repeatedValue : repeatedValues) { + joiner.add(repeatedValue); + } + String repeatedValuesString = joiner.toString(); + return "(" + repeatedValuesString + ")"; + } + throw new AssertionError("Unknown annotation: " + annotation); + } +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/Annotations57649Test.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/Annotations57649Test.java similarity index 99% rename from luni/src/test/java/libcore/java/lang/reflect/Annotations57649Test.java rename to luni/src/test/java/libcore/java/lang/reflect/annotations/Annotations57649Test.java index 60e294bd6..70b8a886a 100644 --- a/luni/src/test/java/libcore/java/lang/reflect/Annotations57649Test.java +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/Annotations57649Test.java @@ -1,4 +1,4 @@ -package libcore.java.lang.reflect; +package libcore.java.lang.reflect.annotations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -7,6 +7,12 @@ public final class Annotations57649Test extends TestCase { // https://code.google.com/p/android/issues/detail?id=57649 public void test57649() throws Exception { + // This test consumes a lot of RAM and doesn't release it. Disable on low ram devices. + // See b/32004484 + if (isLowRamDevice()) { + return; + } + Thread a = runTest(A.class); Thread b = runTest(B.class); a.join(); @@ -23,6 +29,10 @@ private static Thread runTest(final Class c) { return t; } + private static boolean isLowRamDevice() { + return Boolean.parseBoolean(System.getProperty("android.cts.device.lowram", "false")); + } + @Retention(RetentionPolicy.RUNTIME) @interface A0 {} @Retention(RetentionPolicy.RUNTIME) @interface A1 {} @Retention(RetentionPolicy.RUNTIME) @interface A2 {} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java new file mode 100644 index 000000000..a694981c7 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 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. + */ +package libcore.java.lang.reflect.annotations; + +import junit.framework.TestCase; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB; + +import dalvik.system.VMRuntime; + +/** + * Tests for the behavior of Annotation instances at runtime. + */ +public class AnnotationsTest extends TestCase { + + enum Breakfast { WAFFLES, PANCAKES } + + @Retention(RetentionPolicy.RUNTIME) + public @interface HasDefaultsAnnotation { + byte a() default 5; + short b() default 6; + int c() default 7; + long d() default 8; + float e() default 9.0f; + double f() default 10.0; + char g() default 'k'; + boolean h() default true; + Breakfast i() default Breakfast.WAFFLES; + AnnotationA j() default @AnnotationA(); + String k() default "maple"; + Class l() default AnnotationB.class; + int[] m() default { 1, 2, 3 }; + Breakfast[] n() default { Breakfast.WAFFLES, Breakfast.PANCAKES }; + Breakfast o(); + int p(); + } + + public void testAnnotationDefaults() throws Exception { + assertEquals((byte) 5, defaultValue("a")); + assertEquals((short) 6, defaultValue("b")); + assertEquals(7, defaultValue("c")); + assertEquals(8L, defaultValue("d")); + assertEquals(9.0f, defaultValue("e")); + assertEquals(10.0, defaultValue("f")); + assertEquals('k', defaultValue("g")); + assertEquals(true, defaultValue("h")); + assertEquals(Breakfast.WAFFLES, defaultValue("i")); + assertEquals("@" + AnnotationA.class.getName() + "()", defaultValue("j").toString()); + assertEquals("maple", defaultValue("k")); + assertEquals(AnnotationB.class, defaultValue("l")); + assertEquals("[1, 2, 3]", Arrays.toString((int[]) defaultValue("m"))); + assertEquals("[WAFFLES, PANCAKES]", Arrays.toString((Breakfast[]) defaultValue("n"))); + assertEquals(null, defaultValue("o")); + assertEquals(null, defaultValue("p")); + } + + private static Object defaultValue(String name) throws NoSuchMethodException { + return HasDefaultsAnnotation.class.getMethod(name).getDefaultValue(); + } + + @Retention(RetentionPolicy.CLASS) + public @interface ClassRetentionAnnotation {} + + @Retention(RetentionPolicy.RUNTIME) + public @interface RuntimeRetentionAnnotation {} + + @Retention(RetentionPolicy.SOURCE) + public @interface SourceRetentionAnnotation {} + + @ClassRetentionAnnotation @RuntimeRetentionAnnotation @SourceRetentionAnnotation + public static class RetentionAnnotations {} + + public void testRetentionPolicy() { + // b/29500035 + int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion(); + try { + // Test N and later behavior + VMRuntime.getRuntime().setTargetSdkVersion(24); + Annotation classRetentionAnnotation = + RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class); + assertNull(classRetentionAnnotation); + + // Test pre-N behavior + VMRuntime.getRuntime().setTargetSdkVersion(23); + classRetentionAnnotation = + RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class); + assertNotNull(classRetentionAnnotation); + } finally { + VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion); + } + assertNotNull(RetentionAnnotations.class.getAnnotation(RuntimeRetentionAnnotation.class)); + assertNull(RetentionAnnotations.class.getAnnotation(SourceRetentionAnnotation.class)); + } +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ClassTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ClassTest.java new file mode 100644 index 000000000..5488ed19d --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ClassTest.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations; + +import junit.framework.TestCase; + +import java.lang.annotation.Inherited; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; + +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementDirectMethods; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent; + +public class ClassTest extends TestCase { + + public void setUp() throws Exception { + super.setUp(); + // Required by all the tests. + Repeated.class.isAnnotationPresent(Inherited.class); + } + + @AnnotationA + @AnnotationB + private static class Type { + } + + public static class ExtendsType extends Type {} + + public void testClassDirectAnnotations() { + checkAnnotatedElementPresentMethods(Type.class, AnnotationA.class, AnnotationB.class); + checkAnnotatedElementDirectMethods(Type.class, AnnotationA.class, AnnotationB.class); + } + + public void testClassInheritedAnnotations() { + checkAnnotatedElementPresentMethods(ExtendsType.class, AnnotationB.class); + checkAnnotatedElementDirectMethods(ExtendsType.class); + } + + @Repeated(1) + private static class SingleAnnotation {} + + @Repeated(1) + @Repeated(2) + private static class MultipleAnnotation {} + + @Container({@Repeated(1)}) + private static class MultipleAnnotationExplicitSingle {} + + @Repeated(1) + @Container({@Repeated(2), @Repeated(3)}) + private static class MultipleAnnotationOddity {} + + private static class NoAnnotation {} + + private static class InheritedNoNewAnnotation extends SingleAnnotation {} + + @Repeated(2) + private static class InheritedSingleWithNewSingleAnnotation extends SingleAnnotation {} + + @Repeated(2) + @Repeated(3) + private static class InheritedSingleWithNewMultipleAnnotations extends SingleAnnotation {} + + @Repeated(2) + private static class InheritedMultipleWithNewSingleAnnotation extends MultipleAnnotation {} + + @Repeated(2) + @Repeated(3) + private static class InheritedMultipleWithNewMultipleAnnotations extends MultipleAnnotation {} + + public void testIsAnnotationPresent() throws Exception { + Class repeated = Repeated.class; + assertIsAnnotationPresent(NoAnnotation.class, repeated, false); + assertIsAnnotationPresent(SingleAnnotation.class, repeated, true); + assertIsAnnotationPresent(MultipleAnnotation.class, repeated, false); + assertIsAnnotationPresent(MultipleAnnotationExplicitSingle.class, repeated, false); + assertIsAnnotationPresent(MultipleAnnotationOddity.class, repeated, true); + assertIsAnnotationPresent(InheritedNoNewAnnotation.class, repeated, true); + assertIsAnnotationPresent(InheritedSingleWithNewSingleAnnotation.class, repeated, true); + assertIsAnnotationPresent(InheritedSingleWithNewMultipleAnnotations.class, repeated, true); + assertIsAnnotationPresent(InheritedMultipleWithNewSingleAnnotation.class, repeated, true); + assertIsAnnotationPresent(InheritedMultipleWithNewMultipleAnnotations.class, repeated, + false); + + Class container = Container.class; + assertIsAnnotationPresent(NoAnnotation.class, repeated, false); + assertIsAnnotationPresent(SingleAnnotation.class, container, false); + assertIsAnnotationPresent(MultipleAnnotation.class, container, true); + assertIsAnnotationPresent(MultipleAnnotationExplicitSingle.class, container, true); + assertIsAnnotationPresent(MultipleAnnotationOddity.class, container, true); + assertIsAnnotationPresent(InheritedNoNewAnnotation.class, container, false); + assertIsAnnotationPresent(InheritedSingleWithNewSingleAnnotation.class, container, false); + assertIsAnnotationPresent(InheritedSingleWithNewMultipleAnnotations.class, container, true); + assertIsAnnotationPresent(InheritedMultipleWithNewSingleAnnotation.class, container, true); + assertIsAnnotationPresent(InheritedMultipleWithNewMultipleAnnotations.class, container, + true); + } + + public void testGetDeclaredAnnotation() throws Exception { + Class repeated = Repeated.class; + assertGetDeclaredAnnotation(NoAnnotation.class, repeated, null); + assertGetDeclaredAnnotation(SingleAnnotation.class, repeated, "@Repeated(1)"); + assertGetDeclaredAnnotation(MultipleAnnotation.class, repeated, null); + assertGetDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, repeated, null); + assertGetDeclaredAnnotation(MultipleAnnotationOddity.class, repeated, "@Repeated(1)"); + assertGetDeclaredAnnotation(InheritedNoNewAnnotation.class, repeated, null); + assertGetDeclaredAnnotation(InheritedSingleWithNewSingleAnnotation.class, repeated, + "@Repeated(2)"); + assertGetDeclaredAnnotation(InheritedSingleWithNewMultipleAnnotations.class, repeated, + null); + assertGetDeclaredAnnotation(InheritedMultipleWithNewSingleAnnotation.class, repeated, + "@Repeated(2)"); + assertGetDeclaredAnnotation(InheritedMultipleWithNewMultipleAnnotations.class, repeated, + null); + + Class container = Container.class; + assertGetDeclaredAnnotation(NoAnnotation.class, container, null); + assertGetDeclaredAnnotation(SingleAnnotation.class, container, null); + assertGetDeclaredAnnotation(MultipleAnnotation.class, container, + "@Container({@Repeated(1), @Repeated(2)})"); + assertGetDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, container, + "@Container({@Repeated(1)})"); + assertGetDeclaredAnnotation(MultipleAnnotationOddity.class, container, + "@Container({@Repeated(2), @Repeated(3)})"); + assertGetDeclaredAnnotation(InheritedNoNewAnnotation.class, container, null); + assertGetDeclaredAnnotation(InheritedSingleWithNewSingleAnnotation.class, container, null); + assertGetDeclaredAnnotation(InheritedSingleWithNewMultipleAnnotations.class, container, + "@Container({@Repeated(2), @Repeated(3)})"); + assertGetDeclaredAnnotation(InheritedMultipleWithNewSingleAnnotation.class, container, + null); + assertGetDeclaredAnnotation(InheritedMultipleWithNewMultipleAnnotations.class, container, + "@Container({@Repeated(2), @Repeated(3)})"); + } + + public void testGetDeclaredAnnotationsByType() throws Exception { + Class repeated = Repeated.class; + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + NoAnnotation.class, repeated, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + SingleAnnotation.class, repeated, new String[] { "@Repeated(1)" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + MultipleAnnotation.class, repeated, new String[] { "@Repeated(1)", "@Repeated(2)" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + MultipleAnnotationExplicitSingle.class, repeated, new String[] { "@Repeated(1)" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + MultipleAnnotationOddity.class, repeated, + new String[] { "@Repeated(1)", "@Repeated(2)", "@Repeated(3)" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + InheritedNoNewAnnotation.class, repeated, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + InheritedSingleWithNewSingleAnnotation.class, repeated, + new String[] { "@Repeated(2)" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + InheritedSingleWithNewMultipleAnnotations.class, repeated, + new String[] { "@Repeated(2)", "@Repeated(3)" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + InheritedMultipleWithNewSingleAnnotation.class, repeated, + new String[] { "@Repeated(2)" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + InheritedMultipleWithNewMultipleAnnotations.class, repeated, + new String[] { "@Repeated(2)", "@Repeated(3)" }); + + Class container = Container.class; + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + NoAnnotation.class, container, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + SingleAnnotation.class, container, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + MultipleAnnotation.class, container, + new String[] { "@Container({@Repeated(1), @Repeated(2)})" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + MultipleAnnotationExplicitSingle.class, container, + new String[] { "@Container({@Repeated(1)})" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + MultipleAnnotationOddity.class, container, + new String[] { "@Container({@Repeated(2), @Repeated(3)})" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + InheritedNoNewAnnotation.class, container, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + InheritedSingleWithNewSingleAnnotation.class, container, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + InheritedSingleWithNewMultipleAnnotations.class, container, + new String[] { "@Container({@Repeated(2), @Repeated(3)})" }); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + InheritedMultipleWithNewSingleAnnotation.class, container, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + InheritedMultipleWithNewMultipleAnnotations.class, container, + new String[] { "@Container({@Repeated(2), @Repeated(3)})" }); + } + + public void testGetAnnotationsByType() throws Exception { + Class repeated = Repeated.class; + AnnotatedElementTestSupport.assertGetAnnotationsByType( + NoAnnotation.class, repeated, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + SingleAnnotation.class, repeated, new String[] { "@Repeated(1)" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + MultipleAnnotation.class, repeated, new String[] { "@Repeated(1)", "@Repeated(2)" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + MultipleAnnotationExplicitSingle.class, repeated, new String[] { "@Repeated(1)" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + MultipleAnnotationOddity.class, repeated, + new String[] { "@Repeated(1)", "@Repeated(2)", "@Repeated(3)" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + InheritedNoNewAnnotation.class, repeated, new String[] { "@Repeated(1)" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + InheritedSingleWithNewSingleAnnotation.class, repeated, + new String[] { "@Repeated(2)" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + InheritedSingleWithNewMultipleAnnotations.class, repeated, + new String[] { "@Repeated(2)", "@Repeated(3)" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + InheritedMultipleWithNewSingleAnnotation.class, repeated, + new String[] { "@Repeated(2)" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + InheritedMultipleWithNewMultipleAnnotations.class, repeated, + new String[] { "@Repeated(2)", "@Repeated(3)" }); + + Class container = Container.class; + AnnotatedElementTestSupport.assertGetAnnotationsByType( + NoAnnotation.class, container, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + SingleAnnotation.class, container, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + MultipleAnnotation.class, container, + new String[] { "@Container({@Repeated(1), @Repeated(2)})" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + MultipleAnnotationExplicitSingle.class, container, + new String[] { "@Container({@Repeated(1)})" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + MultipleAnnotationOddity.class, container, + new String[] { "@Container({@Repeated(2), @Repeated(3)})" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + InheritedNoNewAnnotation.class, container, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + InheritedSingleWithNewSingleAnnotation.class, container, EXPECT_EMPTY); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + InheritedSingleWithNewMultipleAnnotations.class, container, + new String[] { "@Container({@Repeated(2), @Repeated(3)})" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + InheritedMultipleWithNewSingleAnnotation.class, container, + new String[] { "@Container({@Repeated(1), @Repeated(2)})" }); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + InheritedMultipleWithNewMultipleAnnotations.class, container, + new String[] { "@Container({@Repeated(2), @Repeated(3)})" }); + } +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ConstructorTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ConstructorTest.java new file mode 100644 index 000000000..828b6016f --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ConstructorTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations; + +import junit.framework.TestCase; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; + +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent; + +public class ConstructorTest extends TestCase { + + private static class Type { + @AnnotationA + @AnnotationC + public Type() {} + } + + public void testConstructorAnnotations() throws Exception { + Constructor constructor = Type.class.getConstructor(); + checkAnnotatedElementPresentMethods(constructor, AnnotationA.class, AnnotationC.class); + } + + // A class with multiple constructors that differ by their argument count. + private static class AnnotatedClass { + @Repeated(1) + public AnnotatedClass() {} + + @Repeated(1) + @Repeated(2) + public AnnotatedClass(int a) {} + + @Container({@Repeated(1)}) + public AnnotatedClass(int a, int b) {} + + @Repeated(1) + @Container({@Repeated(2), @Repeated(3)}) + public AnnotatedClass(int a, int b, int c) {} + + public AnnotatedClass(int a, int b, int c, int d) {} + } + + // Tests for isAnnotationPresent and getDeclaredAnnotation. + public void testDeclaredAnnotation() throws Exception { + Class c = AnnotatedClass.class; + + Class repeated = Repeated.class; + checkDeclaredAnnotation(c, 4, repeated, null); + checkDeclaredAnnotation(c, 3, repeated, "@Repeated(1)"); + checkDeclaredAnnotation(c, 2, repeated, null); + checkDeclaredAnnotation(c, 1, repeated, null); + checkDeclaredAnnotation(c, 0, repeated, "@Repeated(1)"); + + Class container = Container.class; + checkDeclaredAnnotation(c, 4, container, null); + checkDeclaredAnnotation(c, 3, container, "@Container({@Repeated(2), @Repeated(3)})"); + checkDeclaredAnnotation(c, 2, container, "@Container({@Repeated(1)})"); + checkDeclaredAnnotation(c, 1, container, "@Container({@Repeated(1), @Repeated(2)})"); + checkDeclaredAnnotation(c, 0, container, null); + } + + private static void checkDeclaredAnnotation(Class c, int constructorArgCount, + Class annotationType, + String expectedAnnotationString) throws Exception { + Constructor constructor = getConstructor(c, constructorArgCount); + + // isAnnotationPresent + assertIsAnnotationPresent(constructor, annotationType, + expectedAnnotationString != null); + + // getDeclaredAnnotation + assertGetDeclaredAnnotation(constructor, annotationType, expectedAnnotationString); + } + + public void testGetDeclaredAnnotationsByType() throws Exception { + Class c = AnnotatedClass.class; + + Class repeated = Repeated.class; + assertGetDeclaredAnnotationsByType(c, 4, repeated, EXPECT_EMPTY); + assertGetDeclaredAnnotationsByType(c, 3, repeated, + "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + assertGetDeclaredAnnotationsByType(c, 2, repeated, "@Repeated(1)"); + assertGetDeclaredAnnotationsByType(c, 1, repeated, "@Repeated(1)", "@Repeated(2)"); + assertGetDeclaredAnnotationsByType(c, 0, repeated, "@Repeated(1)"); + + Class container = Container.class; + assertGetDeclaredAnnotationsByType(c, 4, container, EXPECT_EMPTY); + assertGetDeclaredAnnotationsByType(c, 3, container, + "@Container({@Repeated(2), @Repeated(3)})"); + assertGetDeclaredAnnotationsByType(c, 2, container, "@Container({@Repeated(1)})"); + assertGetDeclaredAnnotationsByType(c, 1, container, + "@Container({@Repeated(1), @Repeated(2)})"); + assertGetDeclaredAnnotationsByType(c, 0, container, EXPECT_EMPTY); + } + + private static void assertGetDeclaredAnnotationsByType(Class c, int constructorArgCount, + Class annotationType, + String... expectedAnnotationStrings) throws Exception { + Constructor constructor = getConstructor(c, constructorArgCount); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + constructor, annotationType, expectedAnnotationStrings); + } + + public void testGetAnnotationsByType() throws Exception { + Class c = AnnotatedClass.class; + + Class repeated = Repeated.class; + assertGetAnnotationsByType(c, 4, repeated, EXPECT_EMPTY); + assertGetAnnotationsByType(c, 3, repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + assertGetAnnotationsByType(c, 2, repeated, "@Repeated(1)"); + assertGetAnnotationsByType(c, 1, repeated, "@Repeated(1)", "@Repeated(2)"); + assertGetAnnotationsByType(c, 0, repeated, "@Repeated(1)"); + + Class container = Container.class; + assertGetAnnotationsByType(c, 4, container, EXPECT_EMPTY); + assertGetAnnotationsByType(c, 3, container, "@Container({@Repeated(2), @Repeated(3)})"); + assertGetAnnotationsByType(c, 2, container, "@Container({@Repeated(1)})"); + assertGetAnnotationsByType(c, 1, container, "@Container({@Repeated(1), @Repeated(2)})"); + assertGetAnnotationsByType(c, 0, container, EXPECT_EMPTY); + } + + private static void assertGetAnnotationsByType(Class c, int constructorArgCount, + Class annotationType, + String... expectedAnnotationStrings) throws Exception { + Constructor constructor = getConstructor(c, constructorArgCount); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + constructor, annotationType, expectedAnnotationStrings); + } + + private static Constructor getConstructor(Class c, int constructorArgCount) + throws NoSuchMethodException { + + Class[] args = new Class[constructorArgCount]; + for (int i = 0; i < constructorArgCount; i++) { + args[i] = Integer.TYPE; + } + return c.getDeclaredConstructor(args); + } +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java new file mode 100644 index 000000000..a07f2b358 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations; + +import junit.framework.TestCase; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; + +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationD; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; + +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.annotationsToTypes; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertAnnotationsMatch; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.set; + +/** + * Tests for {@link Executable#getParameterAnnotations()} via the {@link Constructor} and + * {@link Method} classes. See {@link AnnotatedElementParameterTest} for testing of the + * {@link java.lang.reflect.AnnotatedElement} methods. + */ +public class ExecutableParameterTest extends TestCase { + private static class MethodClass { + public void methodWithoutAnnotatedParameters(String parameter1, String parameter2) {} + + public void methodWithAnnotatedParameters(@AnnotationB @AnnotationD String parameter1, + @AnnotationC @AnnotationD String parameter2) {} + } + + public void testMethodGetParameterAnnotations() throws Exception { + Method methodWithoutAnnotatedParameters = MethodClass.class.getMethod( + "methodWithoutAnnotatedParameters", String.class, String.class); + Annotation[][] noParameterAnnotations = + methodWithoutAnnotatedParameters.getParameterAnnotations(); + assertEquals(2, noParameterAnnotations.length); + assertEquals(set(), annotationsToTypes(noParameterAnnotations[0])); + assertEquals(set(), annotationsToTypes(noParameterAnnotations[1])); + + Method methodWithAnnotatedParameters = MethodClass.class.getMethod( + "methodWithAnnotatedParameters", String.class, String.class); + Annotation[][] parameterAnnotations = + methodWithAnnotatedParameters.getParameterAnnotations(); + assertEquals(2, parameterAnnotations.length); + assertEquals(set(AnnotationB.class, AnnotationD.class), + annotationsToTypes(parameterAnnotations[0])); + assertEquals(set(AnnotationC.class, AnnotationD.class), + annotationsToTypes(parameterAnnotations[1])); + } + + private static class ConstructorClass { + // No annotations. + public ConstructorClass(Integer parameter1, Integer parameter2) {} + + // Annotations. + public ConstructorClass(@AnnotationB @AnnotationD String parameter1, + @AnnotationC @AnnotationD String parameter2) {} + } + + public void testConstructorGetParameterAnnotations() throws Exception { + Constructor constructorWithoutAnnotatedParameters = + ConstructorClass.class.getDeclaredConstructor(Integer.class, Integer.class); + Annotation[][] noParameterAnnotations = + constructorWithoutAnnotatedParameters.getParameterAnnotations(); + assertEquals(2, noParameterAnnotations.length); + assertEquals(set(), annotationsToTypes(noParameterAnnotations[0])); + assertEquals(set(), annotationsToTypes(noParameterAnnotations[1])); + + Constructor constructorWithAnnotatedParameters = + ConstructorClass.class.getDeclaredConstructor(String.class, String.class); + Annotation[][] parameterAnnotations = + constructorWithAnnotatedParameters.getParameterAnnotations(); + assertEquals(2, parameterAnnotations.length); + assertEquals(set(AnnotationB.class, AnnotationD.class), + annotationsToTypes(parameterAnnotations[0])); + assertEquals(set(AnnotationC.class, AnnotationD.class), + annotationsToTypes(parameterAnnotations[1])); + } + + private static class AnnotatedMethodClass { + void noAnnotation(String p0) {} + + void multipleAnnotationOddity( + @Repeated(1) @Container({@Repeated(2), @Repeated(3)}) String p0) {} + + void multipleAnnotationExplicitSingle(@Container({@Repeated(1)}) String p0) {} + + void multipleAnnotation(@Repeated(1) @Repeated(2) String p0) {} + + void singleAnnotation(@Repeated(1) String p0) {} + + static Method getMethodWithoutAnnotations() throws Exception { + return AnnotatedMethodClass.class.getDeclaredMethod("noAnnotation", String.class); + } + + static Method getMethodMultipleAnnotationOddity() throws Exception { + return AnnotatedMethodClass.class.getDeclaredMethod( + "multipleAnnotationOddity", String.class); + } + + static Method getMethodMultipleAnnotationExplicitSingle() throws Exception { + return AnnotatedMethodClass.class.getDeclaredMethod( + "multipleAnnotationExplicitSingle", String.class); + } + + static Method getMethodMultipleAnnotation() throws Exception { + return AnnotatedMethodClass.class.getDeclaredMethod("multipleAnnotation", String.class); + } + + static Method getMethodSingleAnnotation() throws Exception { + return AnnotatedMethodClass.class.getDeclaredMethod("singleAnnotation", String.class); + } + } + + public void testMethodGetParameterAnnotations_repeated() throws Exception { + assertParameter0Annotations( + AnnotatedMethodClass.getMethodWithoutAnnotations(), EXPECT_EMPTY); + assertParameter0Annotations( + AnnotatedMethodClass.getMethodMultipleAnnotationOddity(), + "@Repeated(1)", "@Container({@Repeated(2), @Repeated(3)})"); + assertParameter0Annotations( + AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(), + "@Container({@Repeated(1)})"); + assertParameter0Annotations( + AnnotatedMethodClass.getMethodMultipleAnnotation(), + "@Container({@Repeated(1), @Repeated(2)})"); + assertParameter0Annotations( + AnnotatedMethodClass.getMethodSingleAnnotation(), + "@Repeated(1)"); + } + + private static class AnnotatedConstructorClass { + public AnnotatedConstructorClass(Boolean p0) {} + + public AnnotatedConstructorClass( + @Repeated(1) @Container({@Repeated(2), @Repeated(3)}) Long p0) {} + + public AnnotatedConstructorClass(@Container({@Repeated(1)}) Double p0) {} + + public AnnotatedConstructorClass(@Repeated(1) @Repeated(2) Integer p0) {} + + public AnnotatedConstructorClass(@Repeated(1) String p0) {} + + static Constructor getConstructorWithoutAnnotations() throws Exception { + return AnnotatedConstructorClass.class.getDeclaredConstructor(Boolean.class); + } + + static Constructor getConstructorMultipleAnnotationOddity() throws Exception { + return AnnotatedConstructorClass.class.getDeclaredConstructor(Long.class); + } + + static Constructor getConstructorMultipleAnnotationExplicitSingle() + throws Exception { + return AnnotatedConstructorClass.class.getDeclaredConstructor(Double.class); + } + + static Constructor getConstructorMultipleAnnotation() throws Exception { + return AnnotatedConstructorClass.class.getDeclaredConstructor(Integer.class); + } + + static Constructor getConstructorSingleAnnotation() throws Exception { + return AnnotatedConstructorClass.class.getDeclaredConstructor(String.class); + } + } + + public void testConstructorGetParameterAnnotations_repeated() throws Exception { + assertParameter0Annotations( + AnnotatedConstructorClass.getConstructorWithoutAnnotations(), + EXPECT_EMPTY); + assertParameter0Annotations( + AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(), + "@Repeated(1)", "@Container({@Repeated(2), @Repeated(3)})"); + assertParameter0Annotations( + AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(), + "@Container({@Repeated(1)})"); + assertParameter0Annotations( + AnnotatedConstructorClass.getConstructorMultipleAnnotation(), + "@Container({@Repeated(1), @Repeated(2)})"); + assertParameter0Annotations( + AnnotatedConstructorClass.getConstructorSingleAnnotation(), + "@Repeated(1)"); + } + + private static void assertParameter0Annotations( + Executable executable, String... expectedAnnotationStrings) throws Exception { + Annotation[][] allAnnotations = executable.getParameterAnnotations(); + final int expectedParameterCount = 1; + assertEquals(expectedParameterCount, allAnnotations.length); + + Annotation[] p0Annotations = allAnnotations[0]; + assertAnnotationsMatch(p0Annotations, expectedAnnotationStrings); + } +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/FieldTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/FieldTest.java new file mode 100644 index 000000000..8ab7d8ea0 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/FieldTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations; + +import junit.framework.TestCase; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationD; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; + +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent; + +public class FieldTest extends TestCase { + + private static class Type { + @AnnotationA + @AnnotationD + public String field; + } + + public void testFieldAnnotations() throws Exception { + Field field = Type.class.getField("field"); + checkAnnotatedElementPresentMethods(field, AnnotationA.class, AnnotationD.class); + } + + private static class AnnotatedClass { + @Repeated(1) + private Object singleAnnotation; + + @Repeated(1) + @Repeated(2) + private Object multipleAnnotation; + + @Container({@Repeated(1)}) + private Object multipleAnnotationExplicitSingle; + + @Repeated(1) + @Container({@Repeated(2), @Repeated(3)}) + private Object multipleAnnotationOddity; + + private Object noAnnotation; + } + + // Tests for isAnnotationPresent and getDeclaredAnnotation. + public void testDeclaredAnnotation() throws Exception { + Class c = AnnotatedClass.class; + + Class repeated = Repeated.class; + checkDeclaredAnnotation(c, "noAnnotation", repeated, null); + checkDeclaredAnnotation(c, "multipleAnnotationOddity", repeated, "@Repeated(1)"); + checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", repeated, null); + checkDeclaredAnnotation(c, "multipleAnnotation", repeated, null); + checkDeclaredAnnotation(c, "singleAnnotation", repeated, "@Repeated(1)"); + + Class container = Container.class; + checkDeclaredAnnotation(c, "noAnnotation", container, null); + checkDeclaredAnnotation(c, "multipleAnnotationOddity", container, + "@Container({@Repeated(2), @Repeated(3)})"); + checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", container, + "@Container({@Repeated(1)})"); + checkDeclaredAnnotation(c, "multipleAnnotation", container, + "@Container({@Repeated(1), @Repeated(2)})"); + checkDeclaredAnnotation(c, "singleAnnotation", container, null); + } + + private static void checkDeclaredAnnotation( + Class c, String fieldName, Class annotationType, + String expectedAnnotationString) throws Exception { + Field field = c.getDeclaredField(fieldName); + + // isAnnotationPresent + assertIsAnnotationPresent(field, annotationType, expectedAnnotationString != null); + + // getDeclaredAnnotation + assertGetDeclaredAnnotation(field, annotationType, expectedAnnotationString); + } + + public void testGetDeclaredAnnotationsByType() throws Exception { + Class c = AnnotatedClass.class; + + Class repeated = Repeated.class; + assertGetDeclaredAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY); + assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationOddity", + "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle", + "@Repeated(1)"); + assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotation", + "@Repeated(1)", "@Repeated(2)"); + assertGetDeclaredAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)"); + + Class container = Container.class; + assertGetDeclaredAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY); + assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationOddity", + "@Container({@Repeated(2), @Repeated(3)})"); + assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationExplicitSingle", + "@Container({@Repeated(1)})"); + assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotation", + "@Container({@Repeated(1), @Repeated(2)})"); + assertGetDeclaredAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY); + } + + private static void assertGetDeclaredAnnotationsByType( + Class c, Class annotationType, String fieldName, + String... expectedAnnotationStrings) throws Exception { + Field field = c.getDeclaredField(fieldName); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + field, annotationType, expectedAnnotationStrings); + } + + public void testGetAnnotationsByType() throws Exception { + Class c = AnnotatedClass.class; + + Class repeated = Repeated.class; + assertGetAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY); + assertGetAnnotationsByType(c, repeated, "multipleAnnotationOddity", + "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + assertGetAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle", + "@Repeated(1)"); + assertGetAnnotationsByType(c, repeated, "multipleAnnotation", + "@Repeated(1)", "@Repeated(2)"); + assertGetAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)"); + + Class container = Container.class; + assertGetAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY); + assertGetAnnotationsByType(c, container, "multipleAnnotationOddity", + "@Container({@Repeated(2), @Repeated(3)})"); + assertGetAnnotationsByType(c, container, "multipleAnnotationExplicitSingle", + "@Container({@Repeated(1)})"); + assertGetAnnotationsByType(c, container, "multipleAnnotation", + "@Container({@Repeated(1), @Repeated(2)})"); + assertGetAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY); + } + + private static void assertGetAnnotationsByType( + Class c, Class annotationType, + String fieldName, String... expectedAnnotationStrings) throws Exception { + Field field = c.getDeclaredField(fieldName); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + field, annotationType, expectedAnnotationStrings); + } +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/MethodTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/MethodTest.java new file mode 100644 index 000000000..c732a9d53 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/MethodTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations; + +import junit.framework.TestCase; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; + +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent; + +public class MethodTest extends TestCase { + + private static class Type { + @AnnotationB + @AnnotationC + public void method(String parameter1, String parameter2) {} + } + + public void testMethodAnnotations() throws Exception { + Method method = Type.class.getMethod("method", String.class, String.class); + checkAnnotatedElementPresentMethods(method, AnnotationB.class, AnnotationC.class); + } + + private static class AnnotatedClass { + @Repeated(1) + public void singleAnnotation() {} + + @Repeated(1) + @Repeated(2) + public void multipleAnnotation() {} + + @Container({@Repeated(1)}) + public void multipleAnnotationExplicitSingle() {} + + @Repeated(1) + @Container({@Repeated(2), @Repeated(3)}) + public void multipleAnnotationOddity() {} + + public void noAnnotation() {} + } + + // Tests for isAnnotationPresent and getDeclaredAnnotation. + public void testDeclaredAnnotation() throws Exception { + Class c = AnnotatedClass.class; + + Class repeated = Repeated.class; + checkDeclaredAnnotation(c, "noAnnotation", repeated, null); + checkDeclaredAnnotation(c, "multipleAnnotationOddity", repeated, "@Repeated(1)"); + checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", repeated, null); + checkDeclaredAnnotation(c, "multipleAnnotation", repeated, null); + checkDeclaredAnnotation(c, "singleAnnotation", repeated, "@Repeated(1)"); + + Class container = Container.class; + checkDeclaredAnnotation(c, "noAnnotation", container, null); + checkDeclaredAnnotation(c, "multipleAnnotationOddity", container, + "@Container({@Repeated(2), @Repeated(3)})"); + checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", container, + "@Container({@Repeated(1)})"); + checkDeclaredAnnotation(c, "multipleAnnotation", container, + "@Container({@Repeated(1), @Repeated(2)})"); + checkDeclaredAnnotation(c, "singleAnnotation", container, null); + } + + private static void checkDeclaredAnnotation( + Class c, String methodName, Class annotationType, + String expectedAnnotationString) throws Exception { + Method method = c.getDeclaredMethod(methodName); + + // isAnnotationPresent + assertIsAnnotationPresent(method, annotationType, expectedAnnotationString != null); + + // getDeclaredAnnotation + assertGetDeclaredAnnotation(method, annotationType, expectedAnnotationString); + } + + public void testGetDeclaredAnnotationsByType() throws Exception { + Class c = AnnotatedClass.class; + + Class repeated = Repeated.class; + assertGetDeclaredAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY); + assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationOddity", + "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle", + "@Repeated(1)"); + assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotation", + "@Repeated(1)", "@Repeated(2)"); + assertGetDeclaredAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)"); + + Class container = Container.class; + assertGetDeclaredAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY); + assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationOddity", + "@Container({@Repeated(2), @Repeated(3)})"); + assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationExplicitSingle", + "@Container({@Repeated(1)})"); + assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotation", + "@Container({@Repeated(1), @Repeated(2)})"); + assertGetDeclaredAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY); + } + + private static void assertGetDeclaredAnnotationsByType( + Class c, Class annotationType, String methodName, + String... expectedAnnotationStrings) throws Exception { + Method method = c.getDeclaredMethod(methodName); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + method, annotationType, expectedAnnotationStrings); + } + + public void testGetAnnotationsByType() throws Exception { + Class c = AnnotatedClass.class; + + Class repeated = Repeated.class; + assertGetAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY); + assertGetAnnotationsByType(c, repeated, "multipleAnnotationOddity", + "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + assertGetAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle", + "@Repeated(1)"); + assertGetAnnotationsByType(c, repeated, "multipleAnnotation", + "@Repeated(1)", "@Repeated(2)"); + assertGetAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)"); + + Class container = Container.class; + assertGetAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY); + assertGetAnnotationsByType(c, container, "multipleAnnotationOddity", + "@Container({@Repeated(2), @Repeated(3)})"); + assertGetAnnotationsByType(c, container, "multipleAnnotationExplicitSingle", + "@Container({@Repeated(1)})"); + assertGetAnnotationsByType(c, container, "multipleAnnotation", + "@Container({@Repeated(1), @Repeated(2)})"); + assertGetAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY); + } + + private static void assertGetAnnotationsByType( + Class c, Class annotationType, + String methodName, String... expectedAnnotationStrings) throws Exception { + Method method = c.getDeclaredMethod(methodName); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + method, annotationType, expectedAnnotationStrings); + } +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/PackageTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/PackageTest.java new file mode 100644 index 000000000..f54a64e43 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/PackageTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations; + +import junit.framework.TestCase; + +import java.lang.annotation.Annotation; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; +import libcore.java.lang.reflect.annotations.multipleannotation.MultipleAnnotation; +import libcore.java.lang.reflect.annotations.multipleannotationexplicitsingle.MultipleAnnotationExplicitSingle; +import libcore.java.lang.reflect.annotations.multipleannotationoddity.MultipleAnnotationOddity; +import libcore.java.lang.reflect.annotations.noannotation.NoAnnotation; +import libcore.java.lang.reflect.annotations.singleannotation.SingleAnnotation; + +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation; +import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent; + +public class PackageTest extends TestCase { + + // Tests for isAnnotationPresent and getDeclaredAnnotation. + public void testDeclaredAnnotation() throws Exception { + Class repeated = Repeated.class; + checkDeclaredAnnotation(NoAnnotation.class, repeated, null); + checkDeclaredAnnotation(SingleAnnotation.class, repeated, "@Repeated(1)"); + checkDeclaredAnnotation(MultipleAnnotation.class, repeated, null); + checkDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, repeated, null); + checkDeclaredAnnotation(MultipleAnnotationOddity.class, repeated, "@Repeated(1)"); + + Class container = Container.class; + checkDeclaredAnnotation(NoAnnotation.class, container, null); + checkDeclaredAnnotation(SingleAnnotation.class, container, null); + checkDeclaredAnnotation(MultipleAnnotation.class, container, + "@Container({@Repeated(1), @Repeated(2)})"); + checkDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, container, + "@Container({@Repeated(1)})"); + checkDeclaredAnnotation(MultipleAnnotationOddity.class, container, + "@Container({@Repeated(2), @Repeated(3)})"); + } + + private static void checkDeclaredAnnotation( + Class classInPackage, Class annotationType, + String expectedAnnotationString) throws Exception { + + Package aPackage = classInPackage.getPackage(); + // isAnnotationPresent + assertIsAnnotationPresent(aPackage, annotationType, expectedAnnotationString != null); + + // getDeclaredAnnotation + assertGetDeclaredAnnotation(aPackage, annotationType, expectedAnnotationString); + } + + public void testGetDeclaredAnnotationsByType() throws Exception { + Class repeated = Repeated.class; + + assertGetDeclaredAnnotationsByType(NoAnnotation.class, repeated, EXPECT_EMPTY); + assertGetDeclaredAnnotationsByType(SingleAnnotation.class, repeated, + "@Repeated(1)"); + assertGetDeclaredAnnotationsByType(MultipleAnnotation.class, repeated, + "@Repeated(1)", "@Repeated(2)"); + assertGetDeclaredAnnotationsByType(MultipleAnnotationExplicitSingle.class, repeated, + "@Repeated(1)"); + assertGetDeclaredAnnotationsByType(MultipleAnnotationOddity.class, repeated, + "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + + Class container = Container.class; + assertGetDeclaredAnnotationsByType(NoAnnotation.class, container, EXPECT_EMPTY); + assertGetDeclaredAnnotationsByType(SingleAnnotation.class, container, EXPECT_EMPTY); + assertGetDeclaredAnnotationsByType(MultipleAnnotation.class, container, + "@Container({@Repeated(1), @Repeated(2)})"); + assertGetDeclaredAnnotationsByType(MultipleAnnotationExplicitSingle.class, container, + "@Container({@Repeated(1)})"); + assertGetDeclaredAnnotationsByType(MultipleAnnotationOddity.class, container, + "@Container({@Repeated(2), @Repeated(3)})"); + } + + private static void assertGetDeclaredAnnotationsByType( + Class classInPackage, Class annotationType, + String... expectedAnnotationStrings) throws Exception { + Package aPackage = classInPackage.getPackage(); + AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType( + aPackage, annotationType, expectedAnnotationStrings); + } + + public void testGetAnnotationsByType() throws Exception { + Class repeated = Repeated.class; + assertGetAnnotationsByType(NoAnnotation.class, repeated, EXPECT_EMPTY); + assertGetAnnotationsByType(SingleAnnotation.class, repeated, "@Repeated(1)"); + assertGetAnnotationsByType(MultipleAnnotation.class, repeated, + "@Repeated(1)", "@Repeated(2)"); + assertGetAnnotationsByType(MultipleAnnotationExplicitSingle.class, repeated, + "@Repeated(1)"); + assertGetAnnotationsByType(MultipleAnnotationOddity.class, repeated, + "@Repeated(1)", "@Repeated(2)", "@Repeated(3)"); + + Class container = Container.class; + assertGetAnnotationsByType(NoAnnotation.class, container, EXPECT_EMPTY); + assertGetAnnotationsByType(SingleAnnotation.class, container, EXPECT_EMPTY); + assertGetAnnotationsByType(MultipleAnnotation.class, container, + "@Container({@Repeated(1), @Repeated(2)})"); + assertGetAnnotationsByType(MultipleAnnotationExplicitSingle.class, container, + "@Container({@Repeated(1)})"); + assertGetAnnotationsByType(MultipleAnnotationOddity.class, container, + "@Container({@Repeated(2), @Repeated(3)})"); + } + + private static void assertGetAnnotationsByType(Class classInPackage, + Class annotationType, + String... expectedAnnotationStrings) throws Exception { + Package aPackage = classInPackage.getPackage(); + AnnotatedElementTestSupport.assertGetAnnotationsByType( + aPackage, annotationType, expectedAnnotationStrings); + } +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/MultipleAnnotation.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/MultipleAnnotation.java new file mode 100644 index 000000000..d3e77ae47 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/MultipleAnnotation.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 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. + */ +package libcore.java.lang.reflect.annotations.multipleannotation; + +import libcore.java.lang.reflect.annotations.PackageTest; + +/** + * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated + * package and return a {@link Package} object. See {@link PackageTest}. + */ +public class MultipleAnnotation { +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/package-info.java new file mode 100644 index 000000000..1ab457ecf --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/package-info.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 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. + */ + +/** + * See {@link libcore.java.lang.reflect.annotations.PackageTest}. + */ +@Repeated(1) +@Repeated(2) +package libcore.java.lang.reflect.annotations.multipleannotation; + +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/MultipleAnnotationExplicitSingle.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/MultipleAnnotationExplicitSingle.java new file mode 100644 index 000000000..d14ff198b --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/MultipleAnnotationExplicitSingle.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations.multipleannotationexplicitsingle; + +import libcore.java.lang.reflect.annotations.PackageTest; + +/** + * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated + * package and return a {@link Package} object. See {@link PackageTest}. + */ +public class MultipleAnnotationExplicitSingle { +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/package-info.java new file mode 100644 index 000000000..cf41d87bb --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/package-info.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 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. + */ + +/** + * See {@link libcore.java.lang.reflect.annotations.PackageTest}. + */ +@Container({@Repeated(1)}) +package libcore.java.lang.reflect.annotations.multipleannotationexplicitsingle; + +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/MultipleAnnotationOddity.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/MultipleAnnotationOddity.java new file mode 100644 index 000000000..b85f50ff8 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/MultipleAnnotationOddity.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations.multipleannotationoddity; + +import libcore.java.lang.reflect.annotations.PackageTest; + +/** + * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated + * package and return a {@link Package} object. See {@link PackageTest}. + */ +public class MultipleAnnotationOddity { +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/package-info.java new file mode 100644 index 000000000..afbd99eda --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 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. + */ + +/** + * See {@link libcore.java.lang.reflect.annotations.PackageTest}. + */ +@Repeated(1) +@Container({@Repeated(2), @Repeated(3)}) +package libcore.java.lang.reflect.annotations.multipleannotationoddity; + +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container; +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/NoAnnotation.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/NoAnnotation.java new file mode 100644 index 000000000..e7fd95756 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/NoAnnotation.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations.noannotation; + +import libcore.java.lang.reflect.annotations.PackageTest; + +/** + * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated + * package and return a {@link Package} object. See {@link PackageTest}. + */ +public class NoAnnotation { +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/package-info.java new file mode 100644 index 000000000..2942f1a74 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 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. + */ + +/** + * See {@link libcore.java.lang.reflect.annotations.PackageTest}. + */ +package libcore.java.lang.reflect.annotations.noannotation; \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/SingleAnnotation.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/SingleAnnotation.java new file mode 100644 index 000000000..453b2996e --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/SingleAnnotation.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.lang.reflect.annotations.singleannotation; + +import libcore.java.lang.reflect.annotations.PackageTest; + +/** + * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated + * package and return a {@link Package} object. See {@link PackageTest}. + */ +public class SingleAnnotation { +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/package-info.java new file mode 100644 index 000000000..3686f8079 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/package-info.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 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. + */ + +/** + * See {@link libcore.java.lang.reflect.annotations.PackageTest}. + */ +@Repeated(1) +package libcore.java.lang.reflect.annotations.singleannotation; + +import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated; + diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/package-info.java deleted file mode 100644 index 1a84c2ef9..000000000 --- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -//Used by AnnotationsTest. -@AnnotationsTest.RepeatableAnnotation -@AnnotationsTest.RepeatableAnnotation -package libcore.java.lang.reflect; \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/MetadataVariations.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/MetadataVariations.smali new file mode 100644 index 000000000..9d76dc9ff --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/MetadataVariations.smali @@ -0,0 +1,779 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class public interface abstract Llibcore/java/lang/reflect/parameter/MetadataVariations; +.super Ljava/lang/Object; +.source "MetadataVariations.java" + + +# virtual methods +.method public abstract badAccessModifier(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0xFF + } + names = { + "p0" + } + .end annotation +.end method + +.method public abstract badlyFormedAnnotation(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0xFF + } + .end annotation +.end method + +.method public abstract emptyMethodParametersAnnotation()V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = {} + names = {} + .end annotation +.end method + +.method public abstract emptyName(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10 + } + names = { + "" + } + .end annotation +.end method + +.method public abstract manyParameters(IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10, + 0x10 + } + names = { + "a000", + "a001", + "a002", + "a003", + "a004", + "a005", + "a006", + "a007", + "a008", + "a009", + "a010", + "a011", + "a012", + "a013", + "a014", + "a015", + "a016", + "a017", + "a018", + "a019", + "a020", + "a021", + "a022", + "a023", + "a024", + "a025", + "a026", + "a027", + "a028", + "a029", + "a030", + "a031", + "a032", + "a033", + "a034", + "a035", + "a036", + "a037", + "a038", + "a039", + "a040", + "a041", + "a042", + "a043", + "a044", + "a045", + "a046", + "a047", + "a048", + "a049", + "a050", + "a051", + "a052", + "a053", + "a054", + "a055", + "a056", + "a057", + "a058", + "a059", + "a060", + "a061", + "a062", + "a063", + "a064", + "a065", + "a066", + "a067", + "a068", + "a069", + "a070", + "a071", + "a072", + "a073", + "a074", + "a075", + "a076", + "a077", + "a078", + "a079", + "a080", + "a081", + "a082", + "a083", + "a084", + "a085", + "a086", + "a087", + "a088", + "a089", + "a090", + "a091", + "a092", + "a093", + "a094", + "a095", + "a096", + "a097", + "a098", + "a099", + "a100", + "a101", + "a102", + "a103", + "a104", + "a105", + "a106", + "a107", + "a108", + "a109", + "a110", + "a111", + "a112", + "a113", + "a114", + "a115", + "a116", + "a117", + "a118", + "a119", + "a120", + "a121", + "a122", + "a123", + "a124", + "a125", + "a126", + "a127", + "a128", + "a129", + "a130", + "a131", + "a132", + "a133", + "a134", + "a135", + "a136", + "a137", + "a138", + "a139", + "a140", + "a141", + "a142", + "a143", + "a144", + "a145", + "a146", + "a147", + "a148", + "a149", + "a150", + "a151", + "a152", + "a153", + "a154", + "a155", + "a156", + "a157", + "a158", + "a159", + "a160", + "a161", + "a162", + "a163", + "a164", + "a165", + "a166", + "a167", + "a168", + "a169", + "a170", + "a171", + "a172", + "a173", + "a174", + "a175", + "a176", + "a177", + "a178", + "a179", + "a180", + "a181", + "a182", + "a183", + "a184", + "a185", + "a186", + "a187", + "a188", + "a189", + "a190", + "a191", + "a192", + "a193", + "a194", + "a195", + "a196", + "a197", + "a198", + "a199", + "a200", + "a201", + "a202", + "a203", + "a204", + "a205", + "a206", + "a207", + "a208", + "a209", + "a210", + "a211", + "a212", + "a213", + "a214", + "a215", + "a216", + "a217", + "a218", + "a219", + "a220", + "a221", + "a222", + "a223", + "a224", + "a225", + "a226", + "a227", + "a228", + "a229", + "a230", + "a231", + "a232", + "a233", + "a234", + "a235", + "a236", + "a237", + "a238", + "a239", + "a240", + "a241", + "a242", + "a243", + "a244", + "a245", + "a246", + "a247", + "a248", + "a249", + "a250", + "a251", + "a252", + "a253", + "a254", + "a255", + "a256", + "a257", + "a258", + "a259", + "a260", + "a261", + "a262", + "a263", + "a264", + "a265", + "a266", + "a267", + "a268", + "a269", + "a270", + "a271", + "a272", + "a273", + "a274", + "a275", + "a276", + "a277", + "a278", + "a279", + "a280", + "a281", + "a282", + "a283", + "a284", + "a285", + "a286", + "a287", + "a288", + "a289", + "a290", + "a291", + "a292", + "a293", + "a294", + "a295", + "a296", + "a297", + "a298", + "a299" + } + .end annotation +.end method + +.method public abstract nameWithOpenSquareBracket(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { 0x1 } + names = { "a[a" } + .end annotation +.end method + +.method public abstract nameWithPeriod(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { 0x1 } + names = { "a.a" } + .end annotation +.end method + +.method public abstract nameWithSemicolon(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { 0x1 } + names = { "a;a" } + .end annotation +.end method + +.method public abstract nameWithSlash(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { 0x1 } + names = { "a/a" } + .end annotation +.end method + +.method public abstract nullName(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10 + } + names = { + null + } + .end annotation +.end method + +.method public abstract tooFewAccessFlags(Ljava/lang/String;Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10 + } + names = { + "p0", + "p1" + } + .end annotation +.end method + +.method public abstract tooFewBoth(Ljava/lang/String;Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10 + } + names = { + "p0" + } + .end annotation +.end method + +.method public abstract tooFewNames(Ljava/lang/String;Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10, + 0x10 + } + names = { + "p0" + } + .end annotation +.end method + +.method public abstract tooManyAccessFlags(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10, + 0x10 + } + names = { + "p0" + } + .end annotation +.end method + +.method public abstract tooManyBoth(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10, + 0x10 + } + names = { + "p0", + "p1" + } + .end annotation +.end method + +.method public abstract tooManyNames(Ljava/lang/String;)V + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10 + } + names = { + "p0", + "p1" + } + .end annotation +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$-java_lang_Class_getLambdaClassWith1ParameterConstructor__LambdaImpl0.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$-java_lang_Class_getLambdaClassWith1ParameterConstructor__LambdaImpl0.smali new file mode 100644 index 000000000..f872cefd8 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$-java_lang_Class_getLambdaClassWith1ParameterConstructor__LambdaImpl0.smali @@ -0,0 +1,64 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class final synthetic Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$-java_lang_Class_getLambdaClassWith1ParameterConstructor__LambdaImpl0; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + +# interfaces +.implements Ljava/util/concurrent/Callable; + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x1010 + name = "-java_lang_Class_getLambdaClassWith1ParameterConstructor__LambdaImpl0" +.end annotation + + +# instance fields +.field private synthetic val$this:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + +# direct methods +.method public synthetic constructor (Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;)V + .registers 2 + + invoke-direct {p0}, Ljava/lang/Object;->()V + + iput-object p1, p0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$-java_lang_Class_getLambdaClassWith1ParameterConstructor__LambdaImpl0;->val$this:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + return-void +.end method + + +# virtual methods +.method public call()Ljava/lang/Object; + .registers 2 + + iget-object v0, p0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$-java_lang_Class_getLambdaClassWith1ParameterConstructor__LambdaImpl0;->val$this:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + invoke-virtual {v0}, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;->-libcore_java_lang_reflect_parameter_ParameterMetadataTestClasses-mthref-0()Ljava/lang/String; + + move-result-object v0 + + return-object v0 +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1.smali new file mode 100644 index 000000000..b46b4871b --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1.smali @@ -0,0 +1,108 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + +# interfaces +.implements Ljava/util/concurrent/Callable; + + +# annotations +.annotation system Ldalvik/annotation/EnclosingMethod; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;->getAnonymousClassWith1ParameterConstructor()Ljava/lang/Class; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x0 + name = null +.end annotation + +.annotation system Ldalvik/annotation/Signature; + value = { + "Ljava/lang/Object;", + "Ljava/util/concurrent/Callable", + "<", + "Ljava/lang/String;", + ">;" + } +.end annotation + + +# instance fields +.field final synthetic this$0:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + +# direct methods +.method constructor (Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x8010 + } + names = { + "this$0" + } + .end annotation + + .prologue + .line 70 + iput-object p1, p0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1;->this$0:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + + +# virtual methods +.method public bridge synthetic call()Ljava/lang/Object; + .registers 2 + .annotation system Ldalvik/annotation/Throws; + value = { + Ljava/lang/Exception; + } + .end annotation + + .prologue + .line 72 + invoke-virtual {p0}, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1;->call()Ljava/lang/String; + + move-result-object v0 + + return-object v0 +.end method + +.method public call()Ljava/lang/String; + .registers 2 + .annotation system Ldalvik/annotation/Throws; + value = { + Ljava/lang/Exception; + } + .end annotation + + .prologue + .line 73 + iget-object v0, p0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1;->this$0:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + invoke-static {v0}, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;->-wrap0(Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;)Ljava/lang/String; + + move-result-object v0 + + return-object v0 +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1MethodClass.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1MethodClass.smali new file mode 100644 index 000000000..c02622572 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1MethodClass.smali @@ -0,0 +1,61 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1MethodClass; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingMethod; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;->getMethodClassWith1ImplicitParameterConstructor()Ljava/lang/Class; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x0 + name = "MethodClass" +.end annotation + + +# instance fields +.field final synthetic this$0:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + +# direct methods +.method constructor (Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x8010 + } + names = { + "this$0" + } + .end annotation + + .prologue + .line 81 + iput-object p1, p0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1MethodClass;->this$0:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + invoke-direct {p0}, Ljava/lang/Object;->()V + + .line 82 + invoke-static {p1}, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;->-wrap0(Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;)Ljava/lang/String; + + .line 81 + return-void +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$FinalParameter.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$FinalParameter.smali new file mode 100644 index 000000000..6d0ff2db9 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$FinalParameter.smali @@ -0,0 +1,69 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$FinalParameter; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x8 + name = "FinalParameter" +.end annotation + + +# direct methods +.method constructor (Ljava/lang/String;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10 + } + names = { + "p0" + } + .end annotation + + .prologue + .line 26 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + + +# virtual methods +.method finalParameter(Ljava/lang/String;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x10 + } + names = { + "p0" + } + .end annotation + + .prologue + .line 28 + return-void +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$GenericParameter.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$GenericParameter.smali new file mode 100644 index 000000000..2e7a6fbbf --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$GenericParameter.smali @@ -0,0 +1,91 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$GenericParameter; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x8 + name = "GenericParameter" +.end annotation + + +# direct methods +.method constructor (Ljava/util/function/Function;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0 + } + names = { + "p0" + } + .end annotation + + .annotation system Ldalvik/annotation/Signature; + value = { + "(", + "Ljava/util/function/Function", + "<", + "Ljava/lang/String;", + "Ljava/lang/Integer;", + ">;)V" + } + .end annotation + + .prologue + .line 14 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + + +# virtual methods +.method genericParameter(Ljava/util/function/Function;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0 + } + names = { + "p0" + } + .end annotation + + .annotation system Ldalvik/annotation/Signature; + value = { + "(", + "Ljava/util/function/Function", + "<", + "Ljava/lang/String;", + "Ljava/lang/Integer;", + ">;)V" + } + .end annotation + + .prologue + .line 16 + return-void +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$InnerClass.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$InnerClass.smali new file mode 100644 index 000000000..6ed451464 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$InnerClass.smali @@ -0,0 +1,109 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$InnerClass; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x0 + name = "InnerClass" +.end annotation + + +# instance fields +.field final synthetic this$0:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + +# direct methods +.method public constructor (Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x8010 + } + names = { + "this$0" + } + .end annotation + + + .prologue + .line 32 + iput-object p1, p0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$InnerClass;->this$0:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +.method public constructor (Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;Ljava/lang/String;)V + .registers 3 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x8010, 0x0 + } + names = { + "this$0", "p1" + } + .end annotation + + .prologue + .line 34 + iput-object p1, p0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$InnerClass;->this$0:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +.method public constructor (Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;Ljava/util/function/Function;)V + .registers 3 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x8010, 0x0 + } + names = { + "this$0", "p1" + } + .end annotation + + .annotation system Ldalvik/annotation/Signature; + value = { + "(", + "Ljava/util/function/Function", + "<", + "Ljava/lang/String;", + "Ljava/lang/Integer;", + ">;)V" + } + .end annotation + + .prologue + .line 36 + iput-object p1, p0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$InnerClass;->this$0:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; + + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$MixedVarArgs.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$MixedVarArgs.smali new file mode 100644 index 000000000..ad404b0f3 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$MixedVarArgs.smali @@ -0,0 +1,73 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$MixedVarArgs; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x8 + name = "MixedVarArgs" +.end annotation + + +# direct methods +.method varargs constructor ([Ljava/lang/Integer;[Ljava/lang/String;)V + .registers 3 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0, + 0x0 + } + names = { + "p0", + "p1" + } + .end annotation + + .prologue + .line 48 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + + +# virtual methods +.method varargs both([Ljava/lang/Integer;[Ljava/lang/String;)V + .registers 3 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0, + 0x0 + } + names = { + "p0", + "p1" + } + .end annotation + + .prologue + .line 50 + return-void +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$NonIdenticalParameters.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$NonIdenticalParameters.smali new file mode 100644 index 000000000..91be02a0f --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$NonIdenticalParameters.smali @@ -0,0 +1,77 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$NonIdenticalParameters; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x8 + name = "NonIdenticalParameters" +.end annotation + + +# direct methods +.method constructor ()V + .registers 1 + + .prologue + .line 59 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + + +# virtual methods +.method method0(Ljava/lang/String;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0 + } + names = { + "p1" + } + .end annotation + + .prologue + .line 60 + return-void +.end method + +.method method1(Ljava/lang/String;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0 + } + names = { + "p1" + } + .end annotation + + .prologue + .line 61 + return-void +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$NonVarArgs.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$NonVarArgs.smali new file mode 100644 index 000000000..37e4aca46 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$NonVarArgs.smali @@ -0,0 +1,71 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$NonVarArgs; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x8 + name = "NonVarArgs" +.end annotation + + +# direct methods +.method constructor ([Ljava/lang/Integer;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0, + 0x0 + } + names = { + "p0" + } + .end annotation + + .prologue + .line 54 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + + +# virtual methods +.method notVarArgs([Ljava/lang/Integer;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0, + 0x0 + } + names = { + "p0" + } + .end annotation + + .prologue + .line 56 + return-void +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$SingleParameter.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$SingleParameter.smali new file mode 100644 index 000000000..cfaff4a84 --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$SingleParameter.smali @@ -0,0 +1,69 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$SingleParameter; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x8 + name = "SingleParameter" +.end annotation + + +# direct methods +.method constructor (Ljava/lang/String;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0 + } + names = { + "p0" + } + .end annotation + + .prologue + .line 8 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + + +# virtual methods +.method oneParameter(Ljava/lang/String;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0 + } + names = { + "p0" + } + .end annotation + + .prologue + .line 10 + return-void +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$SingleVarArgs.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$SingleVarArgs.smali new file mode 100644 index 000000000..7f020e7fe --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$SingleVarArgs.smali @@ -0,0 +1,69 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$SingleVarArgs; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x8 + name = "SingleVarArgs" +.end annotation + + +# direct methods +.method varargs constructor ([Ljava/lang/String;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0 + } + names = { + "p0" + } + .end annotation + + .prologue + .line 42 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + + +# virtual methods +.method varargs varArgs([Ljava/lang/String;)V + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0 + } + names = { + "p0" + } + .end annotation + + .prologue + .line 44 + return-void +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum.smali new file mode 100644 index 000000000..c564635dc --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum.smali @@ -0,0 +1,144 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class final enum Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; +.super Ljava/lang/Enum; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x4018 + name = "TestEnum" +.end annotation + +.annotation system Ldalvik/annotation/Signature; + value = { + "Ljava/lang/Enum", + "<", + "Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum;", + ">;" + } +.end annotation + + +# static fields +.field private static final synthetic $VALUES:[Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + +.field public static final enum ONE:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + +.field public static final enum TWO:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + +# direct methods +.method static constructor ()V + .registers 4 + + .prologue + const/4 v3, 0x1 + + const/4 v2, 0x0 + + .line 39 + new-instance v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + const-string/jumbo v1, "ONE" + + invoke-direct {v0, v1, v2}, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum;->(Ljava/lang/String;I)V + + sput-object v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum;->ONE:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + new-instance v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + const-string/jumbo v1, "TWO" + + invoke-direct {v0, v1, v3}, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum;->(Ljava/lang/String;I)V + + sput-object v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum;->TWO:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + const/4 v0, 0x2 + + new-array v0, v0, [Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + sget-object v1, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum;->ONE:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + aput-object v1, v0, v2 + + sget-object v1, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum;->TWO:Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + aput-object v1, v0, v3 + + sput-object v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum;->$VALUES:[Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + return-void +.end method + +.method private constructor (Ljava/lang/String;I)V + .registers 3 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x1000, 0x1000 + } + names = { + "$enum$name", "$enum$ordinal" + } + .end annotation + + .prologue + .line 39 + invoke-direct {p0, p1, p2}, Ljava/lang/Enum;->(Ljava/lang/String;I)V + + return-void +.end method + +.method public static valueOf(Ljava/lang/String;)Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + .registers 2 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x8000 + } + names = { + "name" + } + .end annotation + + .prologue + .line 39 + const-class v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + + move-result-object v0 + + check-cast v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + return-object v0 +.end method + +.method public static values()[Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + .registers 1 + + .prologue + .line 39 + sget-object v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum;->$VALUES:[Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum; + + return-object v0 +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TwoParameters.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TwoParameters.smali new file mode 100644 index 000000000..ce4f299ff --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TwoParameters.smali @@ -0,0 +1,73 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TwoParameters; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x8 + name = "TwoParameters" +.end annotation + + +# direct methods +.method constructor (Ljava/lang/String;Ljava/lang/Integer;)V + .registers 3 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0, + 0x0 + } + names = { + "p0", + "p1" + } + .end annotation + + .prologue + .line 20 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + + +# virtual methods +.method twoParameters(Ljava/lang/String;Ljava/lang/Integer;)V + .registers 3 + .annotation system Ldalvik/annotation/MethodParameters; + accessFlags = { + 0x0, + 0x0 + } + names = { + "p0", + "p1" + } + .end annotation + + .prologue + .line 22 + return-void +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses.smali b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses.smali new file mode 100644 index 000000000..f7de5b87a --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses.smali @@ -0,0 +1,148 @@ +# +# Copyright (C) 2016 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. + +# Originally generated using baksmali and edited. See README.txt in this directory. + +.class public Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses; +.super Ljava/lang/Object; +.source "ParameterMetadataTestClasses.java" + + +# annotations +.annotation system Ldalvik/annotation/MemberClasses; + value = { + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$-java_lang_Class_getLambdaClassWith1ParameterConstructor__LambdaImpl0;, + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$FinalParameter;, + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$GenericParameter;, + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$InnerClass;, + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$MixedVarArgs;, + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$NonIdenticalParameters;, + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$NonVarArgs;, + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$SingleParameter;, + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$SingleVarArgs;, + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TestEnum;, + Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$TwoParameters; + } +.end annotation + + +# direct methods +.method static synthetic -wrap0(Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;)Ljava/lang/String; + .registers 2 + + invoke-direct {p0}, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;->outerClassMethod()Ljava/lang/String; + + move-result-object v0 + + return-object v0 +.end method + +.method public constructor ()V + .registers 1 + + .prologue + .line 6 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +.method private outerClassMethod()Ljava/lang/String; + .registers 2 + + .prologue + .line 191 + const-string/jumbo v0, "Howdy" + + return-object v0 +.end method + + +# virtual methods +.method synthetic -libcore_java_lang_reflect_parameter_ParameterMetadataTestClasses-mthref-0()Ljava/lang/String; + .registers 2 + + .prologue + .line 89 + invoke-direct {p0}, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;->outerClassMethod()Ljava/lang/String; + + move-result-object v0 + + return-object v0 +.end method + +.method public getAnonymousClassWith1ParameterConstructor()Ljava/lang/Class; + .registers 2 + .annotation system Ldalvik/annotation/Signature; + value = { + "()", + "Ljava/lang/Class", + "<*>;" + } + .end annotation + + .prologue + .line 70 + new-instance v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1; + + invoke-direct {v0, p0}, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1;->(Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;)V + + .line 76 + invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + + move-result-object v0 + + return-object v0 +.end method + +.method public getLambdaClassWith1ParameterConstructor()Ljava/lang/Class; + .registers 2 + .annotation system Ldalvik/annotation/Signature; + value = { + "()", + "Ljava/lang/Class", + "<*>;" + } + .end annotation + + .prologue + .line 89 + new-instance v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$-java_lang_Class_getLambdaClassWith1ParameterConstructor__LambdaImpl0; + + invoke-direct {v0, p0}, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$-java_lang_Class_getLambdaClassWith1ParameterConstructor__LambdaImpl0;->(Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses;)V + + invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + + move-result-object v0 + + return-object v0 +.end method + +.method public getMethodClassWith1ImplicitParameterConstructor()Ljava/lang/Class; + .registers 2 + .annotation system Ldalvik/annotation/Signature; + value = { + "()", + "Ljava/lang/Class", + "<*>;" + } + .end annotation + + .prologue + .line 85 + const-class v0, Llibcore/java/lang/reflect/parameter/ParameterMetadataTestClasses$1MethodClass; + + return-object v0 +.end method diff --git a/luni/src/test/java/libcore/java/lang/reflect/parameter/README.txt b/luni/src/test/java/libcore/java/lang/reflect/parameter/README.txt new file mode 100644 index 000000000..6d5a6c2ca --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/parameter/README.txt @@ -0,0 +1,212 @@ +This directory contains the .smali files used to generate .dex files used +by libcore.java.lang.reflect.ParameterTest. + +The use of .smali files allows construction of valid and invalid +system annotations for parameter metadata that are then tested in +ParameterTest. + +Regenerate the .dex files with: + +make smali +smali libcore/luni/src/test/java/libcore/java/lang/reflect/parameter/ParameterMetdataTestClasses*.smali \ + -o libcore/luni/src/test/resources/libcore/java/lang/reflect/parameter/parameter_metadata_test_classes.dex + +For reference, the valid smali code should be (roughly) the equivalent of the +following Java code when compiled using a compiler with .dex parameter metadata support +enabled. + +The smali was generated using Jack to create a .dex file. + +For example: + +jack -D jack.java.source.version=1.8 \ + --output-dex . \ + -cp ${ANDROID_BUILD_TOP}/out/target/common/obj/JAVA_LIBRARIES/core-all_intermediates/classes.jack \ + src/test/java/libcore/java/lang/reflect/parameter/ParameterMetadataTestClasses.java + +It was then decompiled using baksmali, hand modified, and a .dex generated from it using smali. + +ParameterMetadataTestClasses* contain valid metadata. +MetadataVariations* contain variations on valid and invalid metdata that would be difficult to +generate from a .java file (i.e. invalid cases, null/empty parameter names). + +--------------------- + +package libcore.java.lang.reflect.parameter; + +import java.util.concurrent.Callable; +import java.util.function.Function; + +public class ParameterMetadataTestClasses { + static class SingleParameter { + SingleParameter(String p0) {} + + void oneParameter(String p0) {} + } + + static class GenericParameter { + GenericParameter(Function p0) {} + + void genericParameter(Function p0) {} + } + + static class TwoParameters { + TwoParameters(String p0, Integer p1) {} + + void twoParameters(String p0, Integer p1) {} + } + + static class FinalParameter { + FinalParameter(final String p0) {} + + void finalParameter(final String p0) {} + } + + class InnerClass { + public InnerClass() {} + + public InnerClass(String p1) {} + + public InnerClass(Function p1) {} + } + + enum TestEnum { ONE, TWO } + + static class SingleVarArgs { + SingleVarArgs(String... p0) {} + + void varArgs(String... p0) {} + } + + static class MixedVarArgs { + MixedVarArgs(Integer[] p0, String... p1) {} + + void both(Integer[] p0, String... p1) {} + } + + static class NonVarArgs { + NonVarArgs(Integer[] p0) {} + + void notVarArgs(Integer[] p0) {} + } + + static class NonIdenticalParameters { + void method0(String p1) {} + + void method1(String p1) {} + } + + private String outerClassMethod() { + return "Howdy"; + } + + public Class getAnonymousClassWith1ParameterConstructor() { + // Deliberately not implemented with a lambda. Do not refactor. + Callable anonymousClassObject = new Callable() { + @Override + public String call() throws Exception { + return ParameterMetadataTestClasses.this.outerClassMethod(); + } + }; + return anonymousClassObject.getClass(); + } + + public Class getMethodClassWith1ImplicitParameterConstructor() { + class MethodClass { + MethodClass() { + ParameterMetadataTestClasses.this.outerClassMethod(); + } + } + return MethodClass.class; + } + + public Class getLambdaClassWith1ParameterConstructor() { + return ((Callable) ParameterMetadataTestClasses.this::outerClassMethod).getClass(); + } +} + +---------------- + +package libcore.java.lang.reflect.parameter; + +public interface MetadataVariations { + + void emptyMethodParametersAnnotation(); + void tooManyAccessFlags(final String p0); + void tooFewAccessFlags(final String p0, final String p1); + void tooManyNames(final String p0); + void tooFewNames(final String p0, final String p1); + void tooManyBoth(final String p0); + void tooFewBoth(final String p0, final String p1); + void nullName(final String p0); + void emptyName(final String p0); + void nameWithSemicolon(final String p0); + void nameWithSlash(final String p0); + void nameWithPeriod(final String p0); + void nameWithOpenSquareBracket(final String p0); + void badAccessModifier(final String p0); + void badlyFormedAnnotation(final String p0); + + void manyParameters( + final int a000, final int a001, final int a002, final int a003, final int a004, + final int a005, final int a006, final int a007, final int a008, final int a009, + final int a010, final int a011, final int a012, final int a013, final int a014, + final int a015, final int a016, final int a017, final int a018, final int a019, + final int a020, final int a021, final int a022, final int a023, final int a024, + final int a025, final int a026, final int a027, final int a028, final int a029, + final int a030, final int a031, final int a032, final int a033, final int a034, + final int a035, final int a036, final int a037, final int a038, final int a039, + final int a040, final int a041, final int a042, final int a043, final int a044, + final int a045, final int a046, final int a047, final int a048, final int a049, + final int a050, final int a051, final int a052, final int a053, final int a054, + final int a055, final int a056, final int a057, final int a058, final int a059, + final int a060, final int a061, final int a062, final int a063, final int a064, + final int a065, final int a066, final int a067, final int a068, final int a069, + final int a070, final int a071, final int a072, final int a073, final int a074, + final int a075, final int a076, final int a077, final int a078, final int a079, + final int a080, final int a081, final int a082, final int a083, final int a084, + final int a085, final int a086, final int a087, final int a088, final int a089, + final int a090, final int a091, final int a092, final int a093, final int a094, + final int a095, final int a096, final int a097, final int a098, final int a099, + final int a100, final int a101, final int a102, final int a103, final int a104, + final int a105, final int a106, final int a107, final int a108, final int a109, + final int a110, final int a111, final int a112, final int a113, final int a114, + final int a115, final int a116, final int a117, final int a118, final int a119, + final int a120, final int a121, final int a122, final int a123, final int a124, + final int a125, final int a126, final int a127, final int a128, final int a129, + final int a130, final int a131, final int a132, final int a133, final int a134, + final int a135, final int a136, final int a137, final int a138, final int a139, + final int a140, final int a141, final int a142, final int a143, final int a144, + final int a145, final int a146, final int a147, final int a148, final int a149, + final int a150, final int a151, final int a152, final int a153, final int a154, + final int a155, final int a156, final int a157, final int a158, final int a159, + final int a160, final int a161, final int a162, final int a163, final int a164, + final int a165, final int a166, final int a167, final int a168, final int a169, + final int a170, final int a171, final int a172, final int a173, final int a174, + final int a175, final int a176, final int a177, final int a178, final int a179, + final int a180, final int a181, final int a182, final int a183, final int a184, + final int a185, final int a186, final int a187, final int a188, final int a189, + final int a190, final int a191, final int a192, final int a193, final int a194, + final int a195, final int a196, final int a197, final int a198, final int a199, + final int a200, final int a201, final int a202, final int a203, final int a204, + final int a205, final int a206, final int a207, final int a208, final int a209, + final int a210, final int a211, final int a212, final int a213, final int a214, + final int a215, final int a216, final int a217, final int a218, final int a219, + final int a220, final int a221, final int a222, final int a223, final int a224, + final int a225, final int a226, final int a227, final int a228, final int a229, + final int a230, final int a231, final int a232, final int a233, final int a234, + final int a235, final int a236, final int a237, final int a238, final int a239, + final int a240, final int a241, final int a242, final int a243, final int a244, + final int a245, final int a246, final int a247, final int a248, final int a249, + final int a250, final int a251, final int a252, final int a253, final int a254, + final int a255, final int a256, final int a257, final int a258, final int a259, + final int a260, final int a261, final int a262, final int a263, final int a264, + final int a265, final int a266, final int a267, final int a268, final int a269, + final int a270, final int a271, final int a272, final int a273, final int a274, + final int a275, final int a276, final int a277, final int a278, final int a279, + final int a280, final int a281, final int a282, final int a283, final int a284, + final int a285, final int a286, final int a287, final int a288, final int a289, + final int a290, final int a291, final int a292, final int a293, final int a294, + final int a295, final int a296, final int a297, final int a298, final int a299 + ); +} diff --git a/luni/src/test/java/libcore/java/math/BigDecimalTest.java b/luni/src/test/java/libcore/java/math/BigDecimalTest.java index cdfab6c13..9f55272e2 100644 --- a/luni/src/test/java/libcore/java/math/BigDecimalTest.java +++ b/luni/src/test/java/libcore/java/math/BigDecimalTest.java @@ -17,10 +17,15 @@ package libcore.java.math; import java.math.BigDecimal; +import java.math.BigInteger; import java.math.MathContext; import java.math.RoundingMode; +import java.util.Locale; + import junit.framework.TestCase; +import static java.math.BigDecimal.valueOf; + public final class BigDecimalTest extends TestCase { public void testGetPrecision() { @@ -67,26 +72,36 @@ public void testRound() { // https://code.google.com/p/android/issues/detail?id=43480 public void testPrecisionFromString() { - BigDecimal a = new BigDecimal("-0.011111111111111111111"); - BigDecimal b = a.multiply(BigDecimal.ONE); + BigDecimal a = new BigDecimal("-0.011111111111111111111"); + BigDecimal b = a.multiply(BigDecimal.ONE); + + assertEquals("-0.011111111111111111111", a.toString()); + assertEquals("-0.011111111111111111111", b.toString()); + + assertEquals(20, a.precision()); + assertEquals(20, b.precision()); - assertEquals("-0.011111111111111111111", a.toString()); - assertEquals("-0.011111111111111111111", b.toString()); + assertEquals(21, a.scale()); + assertEquals(21, b.scale()); - assertEquals(20, a.precision()); - assertEquals(20, b.precision()); + assertEquals("-11111111111111111111", a.unscaledValue().toString()); + assertEquals("-11111111111111111111", b.unscaledValue().toString()); - assertEquals(21, a.scale()); - assertEquals(21, b.scale()); + assertEquals(a, b); + assertEquals(b, a); - assertEquals("-11111111111111111111", a.unscaledValue().toString()); - assertEquals("-11111111111111111111", b.unscaledValue().toString()); + assertEquals(0, a.subtract(b).signum()); + assertEquals(0, a.compareTo(b)); + } - assertEquals(a, b); - assertEquals(b, a); + public void testPrecisionFromString_simplePowersOfTen() { + assertEquals(new BigDecimal(BigInteger.valueOf(-10), 1), new BigDecimal("-1.0")); + assertEquals(new BigDecimal(BigInteger.valueOf(-1), 1), new BigDecimal("-0.1")); + assertEquals(new BigDecimal(BigInteger.valueOf(-1), -1), new BigDecimal("-1E+1")); - assertEquals(0, a.subtract(b).signum()); - assertEquals(0, a.compareTo(b)); + assertEquals(new BigDecimal(BigInteger.valueOf(10), 1), new BigDecimal("1.0")); + assertEquals(new BigDecimal(BigInteger.valueOf(1), 0), new BigDecimal("1")); + assertFalse(new BigDecimal("1.0").equals(new BigDecimal("1"))); } // https://code.google.com/p/android/issues/detail?id=54580 @@ -102,10 +117,308 @@ public void test191227() { BigDecimal zero = BigDecimal.ZERO; zero = zero.setScale(2, RoundingMode.HALF_EVEN); - BigDecimal other = BigDecimal.valueOf(999999998000000001.00); + BigDecimal other = valueOf(999999998000000001.00); other = other.setScale(2, RoundingMode.HALF_EVEN); assertFalse(zero.equals(other)); assertFalse(other.equals(zero)); } + + private static void checkDivide(String expected, long n, long d, int scale, RoundingMode rm) { + assertEquals(String.format(Locale.US, "%d/%d [%d, %s]", n, d, scale, rm.name()), + new BigDecimal(expected), + new BigDecimal(n).divide(new BigDecimal(d), scale, rm)); + } + + public void testDivideRounding() { + // checkDivide(expected, dividend, divisor, scale, roundingMode) + checkDivide("0", 1, Long.MIN_VALUE, 0, RoundingMode.DOWN); + checkDivide("-1", 1, Long.MIN_VALUE, 0, RoundingMode.UP); + checkDivide("-1", 1, Long.MIN_VALUE, 0, RoundingMode.FLOOR); + checkDivide("0", 1, Long.MIN_VALUE, 0, RoundingMode.CEILING); + checkDivide("0", 1, Long.MIN_VALUE, 0, RoundingMode.HALF_EVEN); + checkDivide("0", 1, Long.MIN_VALUE, 0, RoundingMode.HALF_UP); + checkDivide("0", 1, Long.MIN_VALUE, 0, RoundingMode.HALF_DOWN); + + checkDivide("1", Long.MAX_VALUE, Long.MAX_VALUE / 2 + 1, 0, RoundingMode.DOWN); + checkDivide("2", Long.MAX_VALUE, Long.MAX_VALUE / 2, 0, RoundingMode.DOWN); + checkDivide("0.50", Long.MAX_VALUE / 2, Long.MAX_VALUE, 2, RoundingMode.HALF_UP); + checkDivide("0.50", Long.MIN_VALUE / 2, Long.MIN_VALUE, 2, RoundingMode.HALF_UP); + checkDivide("0.5000", Long.MIN_VALUE / 2, Long.MIN_VALUE, 4, RoundingMode.HALF_UP); + // (-2^62 + 1) / (-2^63) = (2^62 - 1) / 2^63 = 0.5 - 2^-63 + checkDivide("0", Long.MIN_VALUE / 2 + 1, Long.MIN_VALUE, 0, RoundingMode.HALF_UP); + checkDivide("1", Long.MIN_VALUE / 2, Long.MIN_VALUE, 0, RoundingMode.HALF_UP); + checkDivide("0", Long.MIN_VALUE / 2, Long.MIN_VALUE, 0, RoundingMode.HALF_DOWN); + // (-2^62 - 1) / (-2^63) = (2^62 + 1) / 2^63 = 0.5 + 2^-63 + checkDivide("1", Long.MIN_VALUE / 2 - 1, Long.MIN_VALUE, 0, RoundingMode.HALF_DOWN); + } + + /** + * Test a bunch of pairings with even/odd dividend and divisor whose + * result is near +/- 0.5. + */ + public void testDivideRounding_sign() { + // checkDivide(expected, dividend, divisor, scale, roundingMode) + // positive dividend and divisor, even/odd values + checkDivide("0", 49, 100, 0, RoundingMode.HALF_UP); + checkDivide("1", 50, 100, 0, RoundingMode.HALF_UP); + checkDivide("1", 51, 101, 0, RoundingMode.HALF_UP); + checkDivide("0", 50, 101, 0, RoundingMode.HALF_UP); + checkDivide("0", Long.MAX_VALUE / 2, Long.MAX_VALUE, 0, RoundingMode.HALF_UP); + + // Same with negative dividend and divisor + checkDivide("0", -49, -100, 0, RoundingMode.HALF_UP); + checkDivide("1", -50, -100, 0, RoundingMode.HALF_UP); + checkDivide("1", -51, -101, 0, RoundingMode.HALF_UP); + checkDivide("0", -50, -101, 0, RoundingMode.HALF_UP); + checkDivide("0", -(Long.MAX_VALUE / 2), -Long.MAX_VALUE, 0, RoundingMode.HALF_UP); + + // Same with negative dividend + checkDivide("0", -49, 100, 0, RoundingMode.HALF_UP); + checkDivide("-1", -50, 100, 0, RoundingMode.HALF_UP); + checkDivide("-1", -51, 101, 0, RoundingMode.HALF_UP); + checkDivide("0", -50, 101, 0, RoundingMode.HALF_UP); + checkDivide("0", -(Long.MAX_VALUE / 2), Long.MAX_VALUE, 0, RoundingMode.HALF_UP); + + // Same with negative divisor + checkDivide("0", 49, -100, 0, RoundingMode.HALF_UP); + checkDivide("-1", 50, -100, 0, RoundingMode.HALF_UP); + checkDivide("-1", 51, -101, 0, RoundingMode.HALF_UP); + checkDivide("0", 50, -101, 0, RoundingMode.HALF_UP); + checkDivide("0", Long.MAX_VALUE / 2, -Long.MAX_VALUE, 0, RoundingMode.HALF_UP); + } + + public void testDivideByOne() { + long[] dividends = new long[] { + Long.MIN_VALUE, + Long.MIN_VALUE + 1, + Long.MAX_VALUE, + Long.MAX_VALUE - 1, + 0, + -1, + 1, + 10, 43, 314159265358979323L, // arbitrary values + }; + for (long dividend : dividends) { + String expected = Long.toString(dividend); + checkDivide(expected, dividend, 1, 0, RoundingMode.UNNECESSARY); + } + } + + public void testNegate() { + checkNegate(valueOf(0), valueOf(0)); + checkNegate(valueOf(1), valueOf(-1)); + checkNegate(valueOf(43), valueOf(-43)); + checkNegate(valueOf(Long.MAX_VALUE), valueOf(-Long.MAX_VALUE)); + checkNegate(new BigDecimal("9223372036854775808"), valueOf(Long.MIN_VALUE)); + // arbitrary large decimal + checkNegate(new BigDecimal("342343243546465623424321423112321.43243434343412321"), + new BigDecimal("-342343243546465623424321423112321.43243434343412321")); + } + + private static void checkNegate(BigDecimal a, BigDecimal b) { + if (!a.toString().equals("0")) { + assertFalse(a.equals(b)); + } + assertEquals(a.negate(), b); + assertEquals(a, b.negate()); + assertEquals(a, a.negate().negate()); + } + + public void testAddAndSubtract_near64BitOverflow() throws Exception { + // Check that the test is set up correctly - these values should be MIN_VALUE and MAX_VALUE + assertEquals("-9223372036854775808", Long.toString(Long.MIN_VALUE)); + assertEquals("9223372036854775807", Long.toString(Long.MAX_VALUE)); + + // Exactly MIN_VALUE and MAX_VALUE + assertSum("-9223372036854775808", -(1L << 62L), -(1L << 62L)); + assertSum("9223372036854775807", (1L << 62L) - 1L, 1L << 62L); + + // One beyond MIN_VALUE and MAX_VALUE + assertSum("-9223372036854775809", -(1L << 62L), -(1L << 62L) - 1); + assertSum("-9223372036854775809", Long.MIN_VALUE + 1, -2); + assertSum("9223372036854775808", 1L << 62L, 1L << 62L); + assertSum("9223372036854775808", Long.MAX_VALUE, 1); + } + + /** + * Assert that {@code (a + b), (b + a), (a - (-b)) and (b - (-a))} all have the same + * expected result in BigDecimal arithmetic. + */ + private static void assertSum(String expectedSumAsString, long a, long b) { + if (a == Long.MIN_VALUE || b == Long.MIN_VALUE) { + // - (Long.MIN_VALUE) can't be represented as a long, so don't allow it here. + throw new IllegalArgumentException("Long.MIN_VALUE not allowed"); + } + BigDecimal bigA = valueOf(a); + BigDecimal bigB = valueOf(b); + BigDecimal bigMinusB = valueOf(-b); + BigDecimal bigMinusA = valueOf(-a); + + assertEquals("a + b", expectedSumAsString, bigA.add(bigB).toString()); + assertEquals("b + a", expectedSumAsString, bigB.add(bigA).toString()); + assertEquals("a - (-b)", expectedSumAsString, bigA.subtract(bigMinusB).toString()); + assertEquals("b - (-a)", expectedSumAsString, bigB.subtract(bigMinusA).toString()); + } + + /** + * Tests that Long.MIN_VALUE / -1 doesn't overflow back to Long.MIN_VALUE, + * like it would in long arithmetic. + */ + // https://code.google.com/p/android/issues/detail?id=196555 + public void testDivideAvoids64bitOverflow() throws Exception { + BigDecimal minLong = new BigDecimal("-9223372036854775808"); + assertEquals("9223372036854775808/(-1)", + new BigDecimal("9223372036854775808"), + minLong.divide(new BigDecimal("-1"), /* scale = */ 0, RoundingMode.UNNECESSARY)); + + assertEquals("922337203685477580.8/(-0.1)", + new BigDecimal("9223372036854775808"), + new BigDecimal("-922337203685477580.8") + .divide(new BigDecimal("-0.1"), /* scale = */ 0, RoundingMode.UNNECESSARY)); + + assertEquals("92233720368547758080/(-1E+1)", + new BigDecimal("9223372036854775808"), + new BigDecimal("-92233720368547758080") + .divide(new BigDecimal("-1E+1"), /* scale = */ 0, RoundingMode.UNNECESSARY)); + + assertEquals("9223372036854775808/(-10) with one decimal of precision", + new BigDecimal("922337203685477580.8"), + minLong.divide(new BigDecimal("-1E+1"), /* scale = */ 1, RoundingMode.UNNECESSARY)); + + // cases that request adjustment of the result scale, i.e. (diffScale != 0) + // i.e. result scale != (dividend.scale - divisor.scale) + assertEquals("9223372036854775808/(-1) with one decimal of precision",// + new BigDecimal("9223372036854775808.0"), + minLong.divide(new BigDecimal("-1"), /* scale = */ 1, RoundingMode.UNNECESSARY)); + + assertEquals("9223372036854775808/(-1.0)",// + new BigDecimal("9223372036854775808"), + minLong.divide(new BigDecimal("-1.0"), /* scale = */ 0, RoundingMode.UNNECESSARY)); + + assertEquals("9223372036854775808/(-1.0) with one decimal of precision",// + new BigDecimal("9223372036854775808.0"), + minLong.divide(new BigDecimal("-1.0"), /* scale = */ 1, RoundingMode.UNNECESSARY)); + + // another arbitrary calculation that results in Long.MAX_VALUE + 1 + // via a different route + assertEquals("4611686018427387904/(-5E-1)",// + new BigDecimal("9223372036854775808"), + new BigDecimal("-4611686018427387904").divide( + new BigDecimal("-5E-1"), /* scale = */ 0, RoundingMode.UNNECESSARY)); + } + + /** + * Tests addition, subtraction, multiplication and division involving a range of + * even long values and 1/2 of that value. + */ + public void testCommonOperations_halfOfEvenLongValue() { + checkCommonOperations(0); + checkCommonOperations(2); + checkCommonOperations(-2); + checkCommonOperations(Long.MIN_VALUE); + checkCommonOperations(1L << 62L); + checkCommonOperations(-(1L << 62L)); + checkCommonOperations(1L << 62L + 1 << 30 + 1 << 10); + checkCommonOperations(Long.MAX_VALUE - 1); + } + + private static void checkCommonOperations(long value) { + if (value % 2 != 0) { + throw new IllegalArgumentException("Expected even value, got " + value); + } + BigDecimal bigHalfValue = valueOf(value / 2); + BigDecimal bigValue = valueOf(value); + BigDecimal two = valueOf(2); + + assertEquals(bigValue, bigHalfValue.multiply(two)); + assertEquals(bigValue, bigHalfValue.add(bigHalfValue)); + assertEquals(bigHalfValue, bigValue.subtract(bigHalfValue)); + assertEquals(bigHalfValue, bigValue.divide(two, RoundingMode.UNNECESSARY)); + if (value != 0) { + assertEquals(two, bigValue.divide(bigHalfValue, RoundingMode.UNNECESSARY)); + } + } + + /** + * Tests that when long multiplication doesn't overflow, its result is consistent with + * BigDecimal multiplication. + */ + public void testMultiply_consistentWithLong() { + checkMultiply_consistentWithLong(0, 0); + checkMultiply_consistentWithLong(0, 1); + checkMultiply_consistentWithLong(1, 1); + checkMultiply_consistentWithLong(2, 3); + checkMultiply_consistentWithLong(123, 456); + checkMultiply_consistentWithLong(9, 9); + checkMultiply_consistentWithLong(34545, 3423421); + checkMultiply_consistentWithLong(5465653, 342343234568L); + checkMultiply_consistentWithLong(Integer.MAX_VALUE, Integer.MAX_VALUE); + checkMultiply_consistentWithLong((1L << 40) + 454L, 34324); + } + + private void checkMultiply_consistentWithLong(long a, long b) { + // Guard against the test using examples that overflow. This condition here is + // not meant to be exact, it'll reject some values that wouldn't overflow. + if (a != 0 && b != 0 && Math.abs(Long.MAX_VALUE / a) <= Math.abs(b)) { + throw new IllegalArgumentException("Multiplication might overflow: " + a + " * " + b); + } + long expectedResult = a * b; + // check the easy case with no decimals + assertEquals(Long.toString(expectedResult), + valueOf(a).multiply(valueOf(b)).toString()); + // number with 2 decimals * number with 3 decimals => number with 5 decimals + // E.g. 9E-2 * 2E-3 == 18E-5 == 0.00018 + // valueOf(unscaledValue, scale) corresponds to {@code unscaledValue * 10-scale} + assertEquals(valueOf(expectedResult, 5), valueOf(a, 2).multiply(valueOf(b, 3))); + } + + public void testMultiply_near64BitOverflow_scaled() { + // -((2^31) / 100) * (-2/10) == (2^64)/1000 + assertEquals("9223372036854775.808", + valueOf(-(1L << 62L), 2).multiply(valueOf(-2, 1)).toString()); + + // -((2^31) / 100) * (2/10) == -(2^64)/1000 + assertEquals("-9223372036854775.808", + valueOf(-(1L << 62L), 2).multiply(valueOf(2, 1)).toString()); + + // -((2^31) * 100) * (-2/10) == (2^64) * 10 + assertEquals(new BigDecimal("9223372036854775808E1"), + valueOf(-(1L << 62L), -2).multiply(valueOf(-2, 1))); + } + + /** Tests multiplications whose result is near 2^63 (= Long.MAX_VALUE + 1). */ + public void testMultiply_near64BitOverflow_positive() { + // Results of exactly +2^63, which doesn't fit into a long even though -2^63 does + assertEquals("9223372036854775808", bigMultiply(Long.MIN_VALUE, -1).toString()); + assertEquals("9223372036854775808", bigMultiply(Long.MIN_VALUE / 2, -2).toString()); + assertEquals("9223372036854775808", bigMultiply(-(Long.MIN_VALUE / 2), 2).toString()); + assertEquals("9223372036854775808", bigMultiply(1L << 31, 1L << 32).toString()); + assertEquals("9223372036854775808", bigMultiply(-(1L << 31), -(1L << 32)).toString()); + + // Results near but not exactly +2^63 + assertEquals("9223372036854775806", bigMultiply(2147483647, 4294967298L).toString()); + assertEquals("9223372036854775807", bigMultiply(Long.MAX_VALUE, 1).toString()); + assertEquals("9223372036854775807", bigMultiply(42128471623L, 218934409L).toString()); + assertEquals("9223372036854775809", bigMultiply(77158673929L, 119537721L).toString()); + assertEquals("9223372036854775810", bigMultiply((1L << 62L) + 1, 2).toString()); + } + + /** Tests multiplications whose result is near -2^63 (= Long.MIN_VALUE). */ + public void testMultiply_near64BitOverflow_negative() { + assertEquals("-9223372036854775808", bigMultiply(Long.MIN_VALUE, 1).toString()); + assertEquals("-9223372036854775808", bigMultiply(Long.MIN_VALUE / 2, 2).toString()); + assertEquals("-9223372036854775808", bigMultiply(-(1L << 31), 1L << 32).toString()); + assertEquals("-9223372036854775807", bigMultiply(-42128471623L, 218934409L).toString()); + assertEquals("-9223372036854775810", bigMultiply(-(Long.MIN_VALUE / 2) + 1, -2).toString()); + } + + private static BigDecimal bigMultiply(long a, long b) { + BigDecimal bigA = valueOf(a); + BigDecimal bigB = valueOf(b); + BigDecimal result = bigA.multiply(bigB); + assertEquals("Multiplication should be commutative", result, bigB.multiply(bigA)); + return result; + } + } diff --git a/luni/src/test/java/libcore/java/math/BigIntegerTest.java b/luni/src/test/java/libcore/java/math/BigIntegerTest.java index 58c68a182..80041c222 100644 --- a/luni/src/test/java/libcore/java/math/BigIntegerTest.java +++ b/luni/src/test/java/libcore/java/math/BigIntegerTest.java @@ -184,4 +184,15 @@ public void test_positiveValues_superfluousZeros() throws Exception { assertEquals(trimmed, extraZeroes); } + + /** + * Tests that Long.MIN_VALUE / -1 doesn't overflow back to Long.MIN_VALUE, + * like it would in long arithmetic. + */ + public void test_divide_avoids64bitOverflow() throws Exception { + BigInteger negV = BigInteger.valueOf(Long.MIN_VALUE); + BigInteger posV = negV.divide(BigInteger.valueOf(-1)); + assertEquals("-9223372036854775808", negV.toString()); + assertEquals( "9223372036854775808", posV.toString()); + } } diff --git a/luni/src/test/java/libcore/java/math/OldBigDecimalConstructorsTest.java b/luni/src/test/java/libcore/java/math/OldBigDecimalConstructorsTest.java index e0ca50d3b..6fbb5ae44 100644 --- a/luni/src/test/java/libcore/java/math/OldBigDecimalConstructorsTest.java +++ b/luni/src/test/java/libcore/java/math/OldBigDecimalConstructorsTest.java @@ -710,7 +710,7 @@ public void testConstrStringMathContext() { assertEquals("incorrect value", "1000000", bd.toString()); } -// ANDROID ADDED +// Android-added /** * java.math.BigDecimal#BigDecimal(java.math.BigInteger, int) diff --git a/luni/src/test/java/libcore/java/net/AbstractCookiesTest.java b/luni/src/test/java/libcore/java/net/AbstractCookiesTest.java index 6e29a62c7..77eb57174 100644 --- a/luni/src/test/java/libcore/java/net/AbstractCookiesTest.java +++ b/luni/src/test/java/libcore/java/net/AbstractCookiesTest.java @@ -521,17 +521,9 @@ public void testCookieStoreNullUris() { HttpCookie cookieA = createCookie("a", "android", ".android.com", "/source"); HttpCookie cookieB = createCookie("b", "banana", "code.google.com", "/p/android"); - try { - cookieStore.add(null, cookieA); - } catch (NullPointerException expected) { - // the RI crashes even though the cookie does get added to the store; sigh - expected.printStackTrace(); - } + cookieStore.add(null, cookieA); assertEquals(Arrays.asList(cookieA), cookieStore.getCookies()); - try { - cookieStore.add(null, cookieB); - } catch (NullPointerException expected) { - } + cookieStore.add(null, cookieB); assertEquals(Arrays.asList(cookieA, cookieB), cookieStore.getCookies()); try { @@ -1535,4 +1527,72 @@ public boolean removeAll() { return true; } } + + // JDK-7169142 + public void testCookieWithNoPeriod() throws Exception { + CookieManager cm = new CookieManager(createCookieStore(), null); + Map> responseHeaders = Collections.singletonMap("Set-Cookie", + Collections.singletonList("foo=bar")); + + URI uri = new URI("http://localhost"); + cm.put(uri, responseHeaders); + + Map> cookies = cm.get( + new URI("https://localhost/log/me/in"), + responseHeaders); + + List cookieList = cookies.values().iterator().next(); + assertEquals(Collections.singletonList("foo=bar"), cookieList); + } + + // http://b/31039416. Android supports cookie "expires" values without a + // "GMT" prefix on the timezone. + public void testLenientExpiresParsing() throws Exception { + CookieManager cm = new CookieManager(createCookieStore(), null); + + URI uri = URI.create("https://test.com"); + Map> header = new HashMap<>(); + List value = new ArrayList<>(); + + value.add("cookie=1234567890test; domain=.test.com; path=/; " + + "expires=Fri, 31 Dec 9999 04:01:25 -0000"); + header.put("Set-Cookie", value); + cm.put(uri, header); + + List cookies = cm.getCookieStore().getCookies(); + assertEquals(1, cookies.size()); + HttpCookie cookie = cookies.get(0); + + assertEquals("1234567890test", cookie.getValue()); + // This should work till year 6830 ((10000 - 6830) years ~= 10^11s) + assertTrue(cookie.getMaxAge() > 100000000000L); + } + + // http://b/33034917. Android supports clearing cookie by re-adding is with + // a "max-age=0". + public void testClearingWithMaxAge0() throws Exception { + CookieManager cm = new CookieManager(createCookieStore(), null); + + URI uri = URI.create("https://test.com"); + Map> header = new HashMap<>(); + List value = new ArrayList<>(); + + value.add("cookie=1234567890test; domain=.test.com; path=/; " + + "expires=Fri, 31 Dec 9999 04:01:25 GMT-0000"); + header.put("Set-Cookie", value); + cm.put(uri, header); + + List cookies = cm.getCookieStore().getCookies(); + assertEquals(1, cookies.size()); + + value.clear(); + header.clear(); + value.add("cookie=1234567890test; domain=.test.com; path=/; " + + "max-age=0"); + header.put("Set-Cookie", value); + cm.put(uri, header); + + cookies = cm.getCookieStore().getCookies(); + assertEquals(0, cookies.size()); + } } diff --git a/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java b/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java index 5ac436e85..440d2cd2f 100644 --- a/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java +++ b/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java @@ -16,6 +16,7 @@ package libcore.java.net; +import java.io.Closeable; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; @@ -39,7 +40,7 @@ */ public class ConcurrentCloseTest extends junit.framework.TestCase { private static final InetSocketAddress UNREACHABLE_ADDRESS - = new InetSocketAddress("192.0.2.0", 80); // RFC 6666 + = new InetSocketAddress("192.0.2.0", 80); // RFC 5737 public void test_accept() throws Exception { ServerSocket ss = new ServerSocket(0); @@ -239,7 +240,7 @@ public void close() throws IOException { } // This thread calls the "close" method on the supplied T after 2s. - static class Killer extends Thread { + static class Killer extends Thread { private final T s; public Killer(T s) { @@ -251,7 +252,7 @@ public void run() { System.err.println("sleep..."); Thread.sleep(2000); System.err.println("close..."); - s.getClass().getMethod("close").invoke(s); + s.close(); } catch (Exception ex) { ex.printStackTrace(); } diff --git a/luni/src/test/java/libcore/java/net/CookiesMCompatibilityTest.java b/luni/src/test/java/libcore/java/net/CookiesMCompatibilityTest.java index 167391511..983a8a398 100644 --- a/luni/src/test/java/libcore/java/net/CookiesMCompatibilityTest.java +++ b/luni/src/test/java/libcore/java/net/CookiesMCompatibilityTest.java @@ -21,6 +21,7 @@ import java.net.InMemoryCookieStore; import java.net.URI; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,10 +35,8 @@ public CookieStore createCookieStore() { // http://b/26456024 public void testCookiesWithoutLeadingPeriod() throws Exception { CookieManager cm = new CookieManager(createCookieStore(), null); - Map> responseHeaders = new HashMap<>(); - List list = new ArrayList(); - list.add("a=b; domain=chargepoint.com"); - responseHeaders.put("Set-Cookie", list); + Map> responseHeaders = Collections.singletonMap("Set-Cookie", + Collections.singletonList("a=b; domain=chargepoint.com")); URI uri = new URI("http://services.chargepoint.com"); cm.put(uri, responseHeaders); @@ -53,16 +52,14 @@ public void testCookiesWithLeadingPeriod() throws Exception { CookieManager cm = new CookieManager(createCookieStore(), null); URI uri = new URI("http://services.chargepoint.com"); List list = new ArrayList<>(); - Map> responseHeaders = new HashMap<>(); - list.add("b=c; domain=.chargepoint.com;"); - responseHeaders.put("Set-Cookie", list); + Map> responseHeaders = Collections.singletonMap("Set-Cookie", + Collections.singletonList("b=c; domain=.chargepoint.com;")); cm.put(uri, responseHeaders); Map> cookies = cm.get( new URI("https://webservices.chargepoint.com/foo"), responseHeaders); - assertEquals(1, cookies.size()); List cookieList = cookies.values().iterator().next(); - assertEquals("b=c", cookieList.get(0)); + assertEquals(Collections.singletonList("b=c"), cookieList); } } diff --git a/luni/src/test/java/libcore/java/net/CookiesTest.java b/luni/src/test/java/libcore/java/net/CookiesTest.java index ebcf302b5..c6bff9a8f 100644 --- a/luni/src/test/java/libcore/java/net/CookiesTest.java +++ b/luni/src/test/java/libcore/java/net/CookiesTest.java @@ -21,6 +21,7 @@ import java.net.InMemoryCookieStore; import java.net.URI; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,10 +35,8 @@ public CookieStore createCookieStore() { // http://b/26456024 public void testCookiesWithLeadingPeriod() throws Exception { CookieManager cm = new CookieManager(createCookieStore(), null); - Map> responseHeaders = new HashMap<>(); - List list = new ArrayList(); - list.add("coulomb_sess=81c112d7dabac869ffa821aa8f672df2"); - responseHeaders.put("Set-Cookie", list); + Map> responseHeaders = Collections.singletonMap("Set-Cookie", + Collections.singletonList("foo=bar")); URI uri = new URI("http://chargepoint.com"); cm.put(uri, responseHeaders); @@ -46,8 +45,7 @@ public void testCookiesWithLeadingPeriod() throws Exception { new URI("https://webservices.chargepoint.com/backend.php/mobileapi/"), responseHeaders); - assertEquals(1, cookies.size()); List cookieList = cookies.values().iterator().next(); - assertEquals("coulomb_sess=81c112d7dabac869ffa821aa8f672df2", cookieList.get(0)); + assertEquals(Collections.singletonList("foo=bar"), cookieList); } } diff --git a/luni/src/test/java/libcore/java/net/DatagramSocketTest.java b/luni/src/test/java/libcore/java/net/DatagramSocketTest.java index 86e47ec29..fabed977a 100644 --- a/luni/src/test/java/libcore/java/net/DatagramSocketTest.java +++ b/luni/src/test/java/libcore/java/net/DatagramSocketTest.java @@ -16,12 +16,24 @@ package libcore.java.net; -import junit.framework.TestCase; - +import java.lang.reflect.Field; +import java.net.DatagramPacket; import java.net.DatagramSocket; +import java.net.DatagramSocketImpl; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.util.Arrays; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public class DatagramSocketTest extends TestCase { +public class DatagramSocketTest extends TestCaseWithRules { + @Rule + public TestRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule(); public void testInitialState() throws Exception { DatagramSocket ds = new DatagramSocket(); @@ -55,4 +67,156 @@ public void testStateAfterClose() throws Exception { assertEquals(-1, ds.getLocalPort()); assertNull(ds.getLocalSocketAddress()); } + + public void testPendingException() throws Exception { + final int port = 9999; + + try (DatagramSocket s = new DatagramSocket()) { + forceConnectToThrowSocketException(s); + + s.connect(InetAddress.getLocalHost(), port); + + byte[] data = new byte[100]; + DatagramPacket p = new DatagramPacket(data, data.length); + + // Confirm send() throws the pendingConnectException. + try { + s.send(p); + fail(); + } catch (SocketException expected) { + assertTrue(expected.getMessage().contains("Pending connect failure")); + } + + // Confirm receive() throws the pendingConnectException. + try { + s.receive(p); + fail(); + } catch (SocketException expected) { + assertTrue(expected.getMessage().contains("Pending connect failure")); + } + + // Confirm that disconnect() doesn't throw a runtime exception. + s.disconnect(); + } + } + + public void test_setTrafficClass() throws Exception { + try (DatagramSocket s = new DatagramSocket()) { + for (int i = 0; i <= 255; ++i) { + s.setTrafficClass(i); + assertEquals(i, s.getTrafficClass()); + } + } + } + + // DatagramSocket should "become connected" even when impl.connect() fails and throws an + // exception. + public void test_b31218085() throws Exception { + final int port = 9999; + + try (DatagramSocket s = new DatagramSocket()) { + forceConnectToThrowSocketException(s); + + s.connect(InetAddress.getLocalHost(), port); + assertTrue(s.isConnected()); + + // Confirm that disconnect() doesn't throw a runtime exception. + s.disconnect(); + } + } + + public void testForceConnectToThrowSocketException() throws Exception { + // Unlike connect(InetAddress, int), connect(SocketAddress) can (and should) throw an + // exception after a call to forceConnectToThrowSocketException(). The + // forceConnectToThrowSocketException() method is used in various tests for + // connect(InetAddress, int) and this test exists to confirm it stays working. + + SocketAddress validAddress = new InetSocketAddress(InetAddress.getLocalHost(), 9999); + + try (DatagramSocket s1 = new DatagramSocket()) { + s1.connect(validAddress); + s1.disconnect(); + } + + try (DatagramSocket s2 = new DatagramSocket()) { + forceConnectToThrowSocketException(s2); + try { + s2.connect(validAddress); + } catch (SocketException expected) { + } + s2.disconnect(); + } + } + + // DatagramSocket should ignore packets received from other sources prior to connect(). + // CVE-2014-6512 + // b/31586706 + public void testExplicitFilter() throws Exception { + final byte[] data = new byte[]{1, 2, 3, 4}; + + try (DatagramSocket dgramSocket = new DatagramSocket(); + DatagramSocket otherSocket = new DatagramSocket()) { + otherSocket.connect(dgramSocket.getLocalSocketAddress()); + otherSocket.send(new DatagramPacket(data, data.length)); + + dgramSocket.setSoTimeout(100); + dgramSocket.connect(new InetSocketAddress(0)); + + // Packet from otherSocket was sent to ds before ds is connected, and it was stored in + // dgramSocket's local buffer. After connect(), dgramSocket should discard this packet + // from the buffer since it is not sent from the connected socket address. + try { + DatagramPacket recv = new DatagramPacket(new byte[data.length], data.length); + dgramSocket.receive(recv); + fail(); + } catch (SocketTimeoutException expected) { } + } + + try (DatagramSocket dgramSocket = new DatagramSocket(); + DatagramSocket srcSocket = new DatagramSocket()) { + srcSocket.connect(dgramSocket.getLocalSocketAddress()); + srcSocket.send(new DatagramPacket(data, data.length)); + + dgramSocket.setSoTimeout(100); + dgramSocket.connect(srcSocket.getLocalSocketAddress()); + + // If the packet is sent from the connected address, even though that is before connect(), it + // should not be dropped and receive() should succeed. + DatagramPacket recv = new DatagramPacket(new byte[data.length], data.length); + dgramSocket.receive(recv); + assertTrue(Arrays.equals(recv.getData(), data)); + } + } + + private static void forceConnectToThrowSocketException(DatagramSocket s) throws Exception { + // Set fd of DatagramSocketImpl to null, forcing impl.connect() to throw a SocketException + // (Socket closed). + Field f = DatagramSocket.class.getDeclaredField("impl"); + f.setAccessible(true); + DatagramSocketImpl impl = (DatagramSocketImpl) f.get(s); + f = DatagramSocketImpl.class.getDeclaredField("fd"); + f.setAccessible(true); + f.set(impl, null); + } + + public void testAddressSameIfUnchanged() throws Exception { + try (DatagramSocket ds = new DatagramSocket(); + DatagramSocket srcDs = new DatagramSocket()) { + ds.setSoTimeout(1000); + srcDs.connect(ds.getLocalSocketAddress()); + srcDs.send(new DatagramPacket(new byte[16], 16)); + srcDs.send(new DatagramPacket(new byte[16], 16)); + + DatagramPacket p = new DatagramPacket(new byte[16], 16); + ds.receive(p); + InetAddress packetAddr = p.getAddress(); + + // This time the packet should have the same address as source address, and it's address + // should remain the same object. + ds.receive(p); + InetAddress newPacketAddr = p.getAddress(); + assertTrue(packetAddr.isLoopbackAddress()); + assertSame(packetAddr, newPacketAddr); + } + } } diff --git a/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java b/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java new file mode 100644 index 000000000..2f5400c75 --- /dev/null +++ b/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2017 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. + */ + +package libcore.java.net; + +import junit.framework.TestCase; + +import org.mockftpserver.core.util.IoUtil; +import org.mockftpserver.fake.FakeFtpServer; +import org.mockftpserver.fake.UserAccount; +import org.mockftpserver.fake.filesystem.DirectoryEntry; +import org.mockftpserver.fake.filesystem.FileEntry; +import org.mockftpserver.fake.filesystem.UnixFakeFileSystem; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * Tests URLConnections for ftp:// URLs. + */ +public class FtpURLConnectionTest extends TestCase { + + private static final String FILE_PATH = "test/file/for/FtpURLConnectionTest.txt"; + private static final String USER = "user"; + private static final String PASSWORD = "password"; + private static final String SERVER_HOSTNAME = "localhost"; + private static final String USER_HOME_DIR = "/home/user"; + + private FakeFtpServer fakeFtpServer; + private UnixFakeFileSystem fileSystem; + + @Override + public void setUp() throws Exception { + super.setUp(); + fakeFtpServer = new FakeFtpServer(); + fakeFtpServer.setServerControlPort(0 /* allocate port number automatically */); + fakeFtpServer.addUserAccount(new UserAccount(USER, PASSWORD, USER_HOME_DIR)); + fileSystem = new UnixFakeFileSystem(); + fakeFtpServer.setFileSystem(fileSystem); + fileSystem.add(new DirectoryEntry(USER_HOME_DIR)); + fakeFtpServer.start(); + } + + @Override + public void tearDown() throws Exception { + fakeFtpServer.stop(); + super.tearDown(); + } + + public void testInputUrl() throws Exception { + byte[] fileContents = "abcdef 1234567890".getBytes(UTF_8); + URL fileUrl = addFileEntry(FILE_PATH, fileContents); + URLConnection connection = fileUrl.openConnection(); + assertContents(fileContents, connection.getInputStream()); + } + + public void testOutputUrl() throws Exception { + byte[] fileContents = "abcdef 1234567890".getBytes(UTF_8); + addFileEntry("test/output-url/existing file.txt", fileContents); + byte[] newFileContents = "contents of brand new file".getBytes(UTF_8); + String filePath = "test/output-url/file that is newly created.txt"; + URL fileUrl = new URL(getFileUrlString(filePath)); + URLConnection connection = fileUrl.openConnection(); + connection.setDoInput(false); + connection.setDoOutput(true); + OutputStream os = connection.getOutputStream(); + writeBytes(os, newFileContents); + + assertContents(newFileContents, openFileSystemContents(filePath)); + } + + public void testConnectOverProxy_noProxy() throws Exception { + Proxy proxy = Proxy.NO_PROXY; + byte[] fileContents = "abcdef 1234567890".getBytes(UTF_8); + URL fileUrl = addFileEntry(FILE_PATH, fileContents); + URLConnection connection = fileUrl.openConnection(proxy); + assertContents(fileContents, connection.getInputStream()); + // Sanity check that NO_PROXY covers the Type.DIRECT case + assertEquals(Proxy.Type.DIRECT, proxy.type()); + } + + /** + * Tests that the helper class {@link CountingProxy} correctly accepts and + * counts connection attempts to the address represented by {@code asProxy()}. + */ + public void testCountingProxy() throws Exception { + Socket socket = new Socket(); + try { + CountingProxy countingProxy = CountingProxy.start(); + try { + Proxy proxy = countingProxy.asProxy(); + assertEquals(Proxy.Type.HTTP, proxy.type()); + SocketAddress address = proxy.address(); + socket.connect(address, /* timeout (msec) */ 200); // attempt one connection + countingProxy.waitAndAssertConnectionCount(1); + } finally { + countingProxy.shutdown(); + } + } finally { + socket.close(); + } + } + + /** + * Tests that a HTTP proxy explicitly passed to {@link URL#openConnection(Proxy)} + * ignores HTTP proxies (since it doesn't support them) and attempts a direct + * connection instead. + */ + public void testConnectOverProxy_explicit_http_uses_direct_connection() throws Exception { + byte[] fileContents = "abcdef 1234567890".getBytes(UTF_8); + URL fileUrl = addFileEntry(FILE_PATH, fileContents); + CountingProxy countingProxy = CountingProxy.start(); + try { + Proxy proxy = countingProxy.asProxy(); + URLConnection connection = fileUrl.openConnection(proxy); + // direct connection succeeds + assertContents(fileContents, connection.getInputStream()); + countingProxy.waitAndAssertConnectionCount(0); + } finally { + countingProxy.shutdown(); + } + } + + /** + * Tests that if a ProxySelector is set, any HTTP proxies selected for + * ftp:// URLs will be rejected. A direct connection will + * be selected once the ProxySelector's proxies have failed. + */ + public void testConnectOverProxy_implicit_http_fails() throws Exception { + byte[] fileContents = "abcdef 1234567890".getBytes(UTF_8); + URL fileUrl = addFileEntry(FILE_PATH, fileContents); + ProxySelector defaultProxySelector = ProxySelector.getDefault(); + try { + CountingProxy countingProxy = CountingProxy.start(); + try { + Proxy proxy = countingProxy.asProxy(); + SingleProxySelector proxySelector = new SingleProxySelector(proxy); + ProxySelector.setDefault(proxySelector); + URLConnection connection = fileUrl.openConnection(); + InputStream inputStream = connection.getInputStream(); + + IOException e = proxySelector.getLastException(); + assertEquals("FTP connections over HTTP proxy not supported", + e.getMessage()); + + // The direct connection is successful + assertContents(fileContents, inputStream); + countingProxy.waitAndAssertConnectionCount(0); + } finally { + countingProxy.shutdown(); + } + } finally { + ProxySelector.setDefault(defaultProxySelector); + } + } + + public void testInputUrlWithSpaces() throws Exception { + byte[] fileContents = "abcdef 1234567890".getBytes(UTF_8); + URL url = addFileEntry("file with spaces.txt", fileContents); + URLConnection connection = url.openConnection(); + assertContents(fileContents, connection.getInputStream()); + } + + public void testBinaryFileContents() throws Exception { + byte[] data = new byte[4096]; + new Random(31337).nextBytes(data); // arbitrary pseudo-random but repeatable test data + URL url = addFileEntry("binaryfile.dat", data.clone()); + assertContents(data, url.openConnection().getInputStream()); + } + + // https://code.google.com/p/android/issues/detail?id=160725 + public void testInputUrlWithSpacesViaProxySelector() throws Exception { + byte[] fileContents = "abcdef 1234567890".getBytes(UTF_8); + ProxySelector defaultProxySelector = ProxySelector.getDefault(); + try { + SingleProxySelector proxySelector = new SingleProxySelector(Proxy.NO_PROXY); + ProxySelector.setDefault(proxySelector); + URL url = addFileEntry("file with spaces.txt", fileContents); + assertContents(fileContents, url.openConnection().getInputStream()); + assertNull(proxySelector.getLastException()); + } finally { + ProxySelector.setDefault(defaultProxySelector); + } + } + + private InputStream openFileSystemContents(String fileName) throws IOException { + String fullFileName = USER_HOME_DIR + "/" + fileName; + FileEntry entry = (FileEntry) fileSystem.getEntry(fullFileName); + assertNotNull("File must exist with name " + fullFileName, entry); + return entry.createInputStream(); + } + + private static void writeBytes(OutputStream os, byte[] fileContents) throws IOException { + os.write(fileContents); + os.close(); + } + + private static void assertContents(byte[] expectedContents, InputStream inputStream) + throws IOException { + try { + byte[] contentBytes = IoUtil.readBytes(inputStream); + if (!Arrays.equals(expectedContents, contentBytes)) { + // optimize the error message for the case of the content being character data + fail("Expected " + new String(expectedContents, UTF_8) + ", but got " + + new String(contentBytes, UTF_8)); + } + } finally { + inputStream.close(); + } + } + + private String getFileUrlString(String filePath) { + int serverPort = fakeFtpServer.getServerControlPort(); + String urlString = String.format(Locale.US, "ftp://%s:%s@%s:%s/%s", + USER, PASSWORD, SERVER_HOSTNAME, serverPort, filePath); + return urlString; + } + + private URL addFileEntry(String filePath, byte[] fileContents) { + FileEntry fileEntry = new FileEntry(USER_HOME_DIR + "/" + filePath); + fileEntry.setContents(fileContents); + fileSystem.add(fileEntry); + String urlString = getFileUrlString(filePath); + try { + return new URL(urlString); + } catch (MalformedURLException e) { + fail("Malformed URL: " + urlString); + throw new AssertionError("Can never happen"); + } + } + + /** + * A {@link ProxySelector} that selects the same (given) Proxy for all URIs. + */ + static class SingleProxySelector extends ProxySelector { + private final Proxy proxy; + private IOException lastException = null; + + public SingleProxySelector(Proxy proxy) { + this.proxy = proxy; + } + + @Override + public List select(URI uri) { + assertNotNull(uri); + return Collections.singletonList(proxy); + } + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + lastException = ioe; + } + + public IOException getLastException() { + return lastException; + } + } + + /** + * Counts the number of attempts to connect to a ServerSocket exposed + * {@link #asProxy() as a Proxy}. From {@link #start()} until + * {@link #shutdown()}, a background server thread accepts and counts + * connections on the socket but immediately closes them without + * reading any data. + */ + static class CountingProxy { + class ServerThread extends Thread { + public ServerThread(String name) { + super(name); + } + + @Override + public void run() { + while (true) { + try { + Socket socket = serverSocket.accept(); + connectionAttempts.release(1); // count one connection attempt + socket.close(); + } catch (SocketException e) { + shutdownLatch.countDown(); + return; + } catch (IOException e) { + // retry + } + } + } + } + + // Signals that serverThread has gracefully completed shutdown (not crashed) + private final CountDownLatch shutdownLatch = new CountDownLatch(1); + private final ServerSocket serverSocket; + private final Proxy proxy; + private final Thread serverThread; + // holds one permit for each connection attempt encountered; this allows + // us to block until a certain number of attempts have taken place. + private final Semaphore connectionAttempts = new Semaphore(0); + + private CountingProxy() throws IOException { + serverSocket = new ServerSocket(0 /* allocate port number automatically */); + SocketAddress socketAddress = serverSocket.getLocalSocketAddress(); + proxy = new Proxy(Proxy.Type.HTTP, socketAddress); + String threadName = getClass().getSimpleName() + " @ " + socketAddress; + serverThread = new ServerThread(threadName); + } + + public static CountingProxy start() throws IOException { + CountingProxy result = new CountingProxy(); + // only start the thread once the object has been properly constructed + result.serverThread.start(); + try { + // Give ServerThread time to call accept(). + Thread.sleep(300); + } catch (InterruptedException e) { + throw new IOException("Unexpectedly interrupted", e); + } + return result; + } + + /** + * Returns the HTTP {@link Proxy} that can represents the ServerSocket + * connections to which this class manages/counts. + */ + public Proxy asProxy() { + return proxy; + } + + /** + * Causes the ServerSocket represented by {@link #asProxy()} to stop accepting + * connections by shutting down the server thread. + * + * @return the number of connections that were attempted during the proxy's lifetime + */ + public void waitAndAssertConnectionCount(int expectedConnectionAttempts) + throws IOException, InterruptedException { + // Wait for a timeout, or fail early if expected # of connections is exceeded + boolean tooManyConnections = connectionAttempts.tryAcquire( + expectedConnectionAttempts + 1, 300, TimeUnit.MILLISECONDS); + assertFalse("Observed more connections than the expected " + expectedConnectionAttempts, + tooManyConnections); + assertEquals(expectedConnectionAttempts, connectionAttempts.availablePermits()); + } + + public void shutdown() throws IOException, InterruptedException { + serverSocket.close(); + // Check that the server shuts down quickly and gracefully via the expected + // code path (as opposed to an uncaught exception). + shutdownLatch.await(1, TimeUnit.SECONDS); + serverThread.join(1000); + assertFalse("serverThread failed to shut down quickly", serverThread.isAlive()); + } + + @Override + public String toString() { + return serverThread.toString() ; + } + } + +} diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java index d496801b4..73c6ea37e 100644 --- a/luni/src/test/java/libcore/java/net/InetAddressTest.java +++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java @@ -16,24 +16,35 @@ package libcore.java.net; -import java.io.IOException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.NetworkInterface; -import java.net.SocketException; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; import libcore.util.SerializationTester; +import org.junit.Test; +import org.junit.runner.RunWith; -public class InetAddressTest extends junit.framework.TestCase { +@RunWith(JUnitParamsRunner.class) +public class InetAddressTest { private static final byte[] LOOPBACK4_BYTES = new byte[] { 127, 0, 0, 1 }; private static final byte[] LOOPBACK6_BYTES = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; - private static final String[] INVALID_IPv4_NUMERIC_ADDRESSES = new String[] { + private static final String[] INVALID_IPv4_AND_6_NUMERIC_ADDRESSES = new String[] { // IPv4 addresses may not be surrounded by square brackets. "[127.0.0.1]", @@ -55,6 +66,9 @@ public class InetAddressTest extends junit.framework.TestCase { "1234", "0", // Single out the deprecated form of the ANY address. + // Older Harmony tests expected this to be resolved to 255.255.255.255. + "4294967295", // 0xffffffffL, + // Hex. Not supported by Android but supported by the RI. "0x1.0x2.0x3.0x4", "0x7f.0x00.0x00.0x01", @@ -71,6 +85,25 @@ public class InetAddressTest extends junit.framework.TestCase { "1.-1.0.1", "1.0.-1.1", "1.0.0.-1", + + // Invalid IPv6 addresses + "FFFF:FFFF", + }; + + private static final String VALID_IPv6_ADDRESSES[] = { + "::1.2.3.4", + "::", + "::", + "1::0", + "1::", + "::1", + "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", + "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255", + "0:0:0:0:0:0:0:0", + "0:0:0:0:0:0:0.0.0.0", + "::255.255.255.255", + "::FFFF:0.0.0.0", + "F:F:F:F:F:F:F:F", }; private static Inet6Address loopback6() throws Exception { @@ -81,66 +114,96 @@ private static Inet6Address localhost6() throws Exception { return (Inet6Address) InetAddress.getByAddress("ip6-localhost", LOOPBACK6_BYTES); } - public void test_parseNumericAddress() throws Exception { - // Regular IPv4. - assertEquals("/1.2.3.4", InetAddress.parseNumericAddress("1.2.3.4").toString()); - // Regular IPv6. - assertEquals("/2001:4860:800d::68", InetAddress.parseNumericAddress("2001:4860:800d::68").toString()); - // Mapped IPv4 - assertEquals("/127.0.0.1", InetAddress.parseNumericAddress("::ffff:127.0.0.1").toString()); - // Optional square brackets around IPv6 addresses, including mapped IPv4. - assertEquals("/2001:4860:800d::68", InetAddress.parseNumericAddress("[2001:4860:800d::68]").toString()); - assertEquals("/127.0.0.1", InetAddress.parseNumericAddress("[::ffff:127.0.0.1]").toString()); + public static String[][] validNumericAddressesAndStringRepresentation() { + return new String[][]{ + // Regular IPv4. + { "1.2.3.4", "/1.2.3.4" }, + + // Regular IPv6. + { "2001:4860:800d::68", "/2001:4860:800d::68" }, + + // Mapped IPv4 + { "::ffff:127.0.0.1", "/127.0.0.1" }, + + // Optional square brackets around IPv6 addresses, including mapped IPv4. + { "[2001:4860:800d::68]", "/2001:4860:800d::68" }, + { "[::ffff:127.0.0.1]", "/127.0.0.1" }, + // Android does not recognize Octal (leading 0) cases: they are treated as decimal. + { "0177.00.00.01", "/177.0.0.1" }, + }; + } + + @Parameters(method = "validNumericAddressesAndStringRepresentation") + @Test + public void test_parseNumericAddress(String address, String expectedString) throws Exception { + assertEquals(expectedString, InetAddress.parseNumericAddress(address).toString()); + } + + @Test + public void test_parseNumericAddress_notNumeric() throws Exception { try { InetAddress.parseNumericAddress("example.com"); // Not numeric. fail(); } catch (IllegalArgumentException expected) { } - // Android does not recognize Octal (leading 0) cases: they are treated as decimal. - assertEquals("/177.0.0.1", InetAddress.parseNumericAddress("0177.00.00.01").toString()); - - for (String invalid : INVALID_IPv4_NUMERIC_ADDRESSES) { - try { - InetAddress.parseNumericAddress(invalid); - fail(invalid); - } catch (IllegalArgumentException expected) { - } - } - // Strange special cases, for compatibility with InetAddress.getByName. assertTrue(InetAddress.parseNumericAddress(null).isLoopbackAddress()); assertTrue(InetAddress.parseNumericAddress("").isLoopbackAddress()); } - public void test_isNumeric() throws Exception { - // IPv4 - assertTrue(InetAddress.isNumeric("1.2.3.4")); - assertTrue(InetAddress.isNumeric("127.0.0.1")); + @Parameters(method = "invalidNumericAddresses") + @Test + public void test_parseNumericAddress_invalid(String invalid) throws Exception { + try { + InetAddress.parseNumericAddress(invalid); + fail(invalid); + } catch (IllegalArgumentException expected) { + } + } + + public static String[] validNumericAddresses() { + return new String[] { + // IPv4 + "1.2.3.4", + "127.0.0.1", - // IPv6 - assertTrue(InetAddress.isNumeric("::1")); - assertTrue(InetAddress.isNumeric("2001:4860:800d::68")); + // IPv6 + "::1", + "2001:4860:800d::68", - // Mapped IPv4 - assertTrue(InetAddress.isNumeric("::ffff:127.0.0.1")); + // Mapped IPv4 + "::ffff:127.0.0.1", - // Optional square brackets around IPv6 addresses, including mapped IPv4. - assertTrue(InetAddress.isNumeric("[2001:4860:800d::68]")); - assertTrue(InetAddress.isNumeric("[::ffff:127.0.0.1]")); + // Optional square brackets around IPv6 addresses, including mapped IPv4. + "[2001:4860:800d::68]", + "[::ffff:127.0.0.1]", + // Android does not handle Octal (leading 0) cases: they are treated as decimal. + "0177.00.00.01", + }; + } + + @Parameters(method = "validNumericAddresses") + @Test + public void test_isNumeric(String valid) throws Exception { + assertTrue(InetAddress.isNumeric(valid)); + } + + @Test + public void test_isNumeric_notNumeric() throws Exception { // Negative test assertFalse(InetAddress.isNumeric("example.com")); + } - // Android does not handle Octal (leading 0) cases: they are treated as decimal. - assertTrue(InetAddress.isNumeric("0177.00.00.01")); // Interpreted as 177.0.0.1 - - for (String invalid : INVALID_IPv4_NUMERIC_ADDRESSES) { - assertFalse(invalid, InetAddress.isNumeric(invalid)); - } + @Parameters(method = "invalidNumericAddresses") + @Test + public void test_isNumeric_invalid(String invalid) { + assertFalse(invalid, InetAddress.isNumeric(invalid)); } + @Test public void test_isLinkLocalAddress() throws Exception { assertFalse(InetAddress.getByName("127.0.0.1").isLinkLocalAddress()); assertFalse(InetAddress.getByName("::ffff:127.0.0.1").isLinkLocalAddress()); @@ -150,6 +213,7 @@ public void test_isLinkLocalAddress() throws Exception { assertTrue(InetAddress.getByName("fe80::").isLinkLocalAddress()); } + @Test public void test_isMCSiteLocalAddress() throws Exception { assertFalse(InetAddress.getByName("239.254.255.255").isMCSiteLocal()); assertTrue(InetAddress.getByName("239.255.0.0").isMCSiteLocal()); @@ -161,6 +225,7 @@ public void test_isMCSiteLocalAddress() throws Exception { assertTrue(InetAddress.getByName("ff15::").isMCSiteLocal()); } + @Test public void test_isReachable() throws Exception { // http://code.google.com/p/android/issues/detail?id=20203 String s = "aced0005737200146a6176612e6e65742e496e6574416464726573732d9b57af" @@ -182,13 +247,12 @@ public void test_isReachable() throws Exception { }.test(); } + @Test public void test_isReachable_neverThrows() throws Exception { InetAddress inetAddress = InetAddress.getByName("www.google.com"); - final NetworkInterface netIf; - try { - netIf = NetworkInterface.getByName("dummy0"); - } catch (SocketException e) { + final NetworkInterface netIf = NetworkInterface.getByName("dummy0"); + if (netIf == null) { System.logI("Skipping test_isReachable_neverThrows because dummy0 isn't available"); return; } @@ -196,6 +260,23 @@ public void test_isReachable_neverThrows() throws Exception { assertFalse(inetAddress.isReachable(netIf, 256, 500)); } + // IPPROTO_ICMP socket kind requires setting ping_group_range. This is set on boot on Android. + // When running on host, make sure you run the command: + // sudo sysctl -w net.ipv4.ping_group_range="0 65535" + @Test + public void test_isReachable_by_ICMP() throws Exception { + InetAddress[] inetAddresses = InetAddress.getAllByName("www.google.com"); + for (InetAddress ia : inetAddresses) { + // ICMP is not reliable, allow 5 attempts before failing. + assertTrue(ia.isReachableByICMP(5 * 1000 /* ICMP timeout */)); + } + + // IPv6 discard prefix. RFC 6666. + final InetAddress blackholeAddress = InetAddress.getByName("100::1"); + assertFalse(blackholeAddress.isReachable(1000)); + } + + @Test public void test_isSiteLocalAddress() throws Exception { assertFalse(InetAddress.getByName("144.32.32.1").isSiteLocalAddress()); assertTrue(InetAddress.getByName("10.0.0.1").isSiteLocalAddress()); @@ -207,20 +288,53 @@ public void test_isSiteLocalAddress() throws Exception { assertTrue(InetAddress.getByName("fec0::").isSiteLocalAddress()); } - public void test_getByName() throws Exception { - for (String invalid : INVALID_IPv4_NUMERIC_ADDRESSES) { - try { - InetAddress.getByName(invalid); - fail(invalid); - } catch (UnknownHostException expected) { - } + public static String[] invalidNumericAddresses() { + return INVALID_IPv4_AND_6_NUMERIC_ADDRESSES; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Parameters(method = "invalidNumericAddresses") + @Test + public void test_getByName_invalid(String invalid) throws Exception { + try { + InetAddress.getByName(invalid); + fail("Invalid IP address incorrectly recognized as valid: " + + invalid); + } catch (UnknownHostException expected) { } + + // exercise negative cache + try { + InetAddress.getByName(invalid); + fail("Invalid IP address incorrectly recognized as valid: " + + invalid); + } catch (Exception expected) { + } + } + + public static String[] validIPv6Addresses() { + return VALID_IPv6_ADDRESSES; } + @Parameters(method = "validIPv6Addresses") + @Test + public void test_getByName_valid(String valid) throws Exception { + InetAddress.getByName(valid); + + // exercise positive cache + InetAddress.getByName(valid); + + // when wrapped in [..] + String tempIPAddress = "[" + valid + "]"; + InetAddress.getByName(tempIPAddress); + } + + @Test public void test_getLoopbackAddress() throws Exception { assertTrue(InetAddress.getLoopbackAddress().isLoopbackAddress()); } + @Test public void test_equals() throws Exception { InetAddress addr = InetAddress.getByName("239.191.255.255"); assertTrue(addr.equals(addr)); @@ -234,6 +348,7 @@ public void test_equals() throws Exception { assertEquals(Inet6Address.getByAddress("1", bs, 1), Inet6Address.getByAddress("2", bs, 2)); } + @Test public void test_getHostAddress() throws Exception { assertEquals("::1", localhost6().getHostAddress()); assertEquals("::1", InetAddress.getByName("::1").getHostAddress()); @@ -301,6 +416,7 @@ public void test_getHostAddress() throws Exception { assertEquals("10:2030:4050:6070:8090:a0b0:c0d0:e0f0", aAddr.getHostAddress()); } + @Test public void test_hashCode() throws Exception { InetAddress addr1 = InetAddress.getByName("1.0.0.1"); InetAddress addr2 = InetAddress.getByName("1.0.0.1"); @@ -309,84 +425,97 @@ public void test_hashCode() throws Exception { assertTrue(loopback6().hashCode() == localhost6().hashCode()); } - public void test_toString() throws Exception { - String validIPAddresses[] = { - "::1.2.3.4", "::", "::", "1::0", "1::", "::1", - "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", - "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255", - "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0.0.0.0" - }; - - String [] resultStrings = { - "/::1.2.3.4", "/::", "/::", "/1::", "/1::", "/::1", - "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "/::", - "/::" + public static String[][] validAddressesAndStringRepresentation() { + return new String[][] { + { "::1.2.3.4", "/::1.2.3.4" }, + { "::", "/::" }, + { "1::0", "/1::" }, + { "1::", "/1::" }, + { "::1", "/::1" }, + { "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" }, + { "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255", "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" }, + { "0:0:0:0:0:0:0:0", "/::" }, + { "0:0:0:0:0:0:0.0.0.0", "/::" }, }; + } - for(int i = 0; i < validIPAddresses.length; i++) { - InetAddress ia = InetAddress.getByName(validIPAddresses[i]); - String result = ia.toString(); - assertNotNull(result); - assertEquals(resultStrings[i], result); - } + @Parameters(method = "validAddressesAndStringRepresentation") + @Test + public void test_toString(String address, String expectedString) throws Exception { + InetAddress ia = InetAddress.getByName(address); + String result = ia.toString(); + assertNotNull(result); + assertEquals(expectedString, result); } + @Test public void test_getHostNameCaches() throws Exception { InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES); - // TODO(narayan): Investigate why these tests are suppressed. - // assertEquals("::1", inetAddress.getHostString()); + + // There should be no cached name. + assertEquals("::1", getHostStringWithoutReverseDns(inetAddress)); + + // Force the reverse-DNS lookup. assertEquals("ip6-localhost", inetAddress.getHostName()); - // getHostString() should now be different. - // assertEquals("ip6-localhost", inetAddress.getHostString()); + + // The cached name should now be different. + assertEquals("ip6-localhost", getHostStringWithoutReverseDns(inetAddress)); } + @Test public void test_getByAddress_loopbackIpv4() throws Exception { InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK4_BYTES); - assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress); + checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress); assertTrue(inetAddress.isLoopbackAddress()); } + @Test public void test_getByAddress_loopbackIpv6() throws Exception { InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES); - assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress); + checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress); assertTrue(inetAddress.isLoopbackAddress()); } + @Test public void test_getByName_loopbackIpv4() throws Exception { InetAddress inetAddress = InetAddress.getByName("127.0.0.1"); - assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress); + checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress); assertTrue(inetAddress.isLoopbackAddress()); } + @Test public void test_getByName_loopbackIpv6() throws Exception { InetAddress inetAddress = InetAddress.getByName("::1"); - assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress); + checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress); assertTrue(inetAddress.isLoopbackAddress()); } + @Test public void test_getByName_empty() throws Exception { InetAddress inetAddress = InetAddress.getByName(""); - assertEquals(LOOPBACK6_BYTES, "localhost", inetAddress); + checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress); assertTrue(inetAddress.isLoopbackAddress()); } + @Test public void test_getAllByName_localhost() throws Exception { InetAddress[] inetAddresses = InetAddress.getAllByName("localhost"); assertEquals(1, inetAddresses.length); InetAddress inetAddress = inetAddresses[0]; - assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress); + checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress); assertTrue(inetAddress.isLoopbackAddress()); } + @Test public void test_getAllByName_ip6_localhost() throws Exception { InetAddress[] inetAddresses = InetAddress.getAllByName("ip6-localhost"); assertEquals(1, inetAddresses.length); InetAddress inetAddress = inetAddresses[0]; - assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress); + checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress); assertTrue(inetAddress.isLoopbackAddress()); } + @Test public void test_getByName_v6loopback() throws Exception { InetAddress inetAddress = InetAddress.getByName("::1"); @@ -395,6 +524,7 @@ public void test_getByName_v6loopback() throws Exception { assertTrue(expectedLoopbackAddresses.contains(inetAddress)); } + @Test public void test_getByName_cloning() throws Exception { InetAddress[] addresses = InetAddress.getAllByName(null); InetAddress[] addresses2 = InetAddress.getAllByName(null); @@ -410,6 +540,7 @@ public void test_getByName_cloning() throws Exception { assertNotNull(addresses2[1]); } + @Test public void test_getAllByName_null() throws Exception { InetAddress[] inetAddresses = InetAddress.getAllByName(null); assertEquals(2, inetAddresses.length); @@ -418,7 +549,15 @@ public void test_getAllByName_null() throws Exception { assertEquals(expectedLoopbackAddresses, createSet(inetAddresses)); } - private static void assertEquals( + // http://b/29311351 + @Test + public void test_loopbackConstantsPreInitializedNames() { + // Note: Inet6Address / Inet4Address equals() does not check host name. + assertEquals("ip6-localhost", getHostStringWithoutReverseDns(Inet6Address.LOOPBACK)); + assertEquals("localhost", getHostStringWithoutReverseDns(Inet4Address.LOOPBACK)); + } + + private static void checkInetAddress( byte[] expectedAddressBytes, String expectedHostname, InetAddress actual) { assertArrayEquals(expectedAddressBytes, actual.getAddress()); assertEquals(expectedHostname, actual.getHostName()); @@ -433,4 +572,11 @@ private static void assertArrayEquals(byte[] expected, byte[] actual) { private static Set createSet(InetAddress... members) { return new HashSet(Arrays.asList(members)); } + + private static String getHostStringWithoutReverseDns(InetAddress inetAddress) { + // The InetAddress API provides no way of avoiding a DNS lookup, but InetSocketAddress + // does via InetSocketAddress.getHostString(). + InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, 9999); + return inetSocketAddress.getHostString(); + } } diff --git a/luni/src/test/java/libcore/java/net/MulticastSocketTest.java b/luni/src/test/java/libcore/java/net/MulticastSocketTest.java new file mode 100644 index 000000000..f442e1be6 --- /dev/null +++ b/luni/src/test/java/libcore/java/net/MulticastSocketTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.net; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.MulticastSocket; +import java.net.SocketTimeoutException; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public final class MulticastSocketTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); + + private static final int SO_TIMEOUT = 1000; + + public void testGroupReceiveIPv4() throws Exception { + testGroupReceive(InetAddress.getByName("239.1.1.1")); + } + + public void testGroupReceiveIPv6() throws Exception { + testGroupReceive(InetAddress.getByName("ff05::1:1")); + } + + private void testGroupReceive(InetAddress mcGroup) throws IOException { + final String message = "hello"; + + try (MulticastSocket mcSock = new MulticastSocket(); + DatagramSocket ds = new DatagramSocket()) { + mcSock.setSoTimeout(SO_TIMEOUT); + final int mcPort = mcSock.getLocalPort(); + + DatagramPacket p = new DatagramPacket(message.getBytes(), message.length()); + p.setAddress(mcGroup); + p.setPort(mcPort); + + mcSock.joinGroup(mcGroup); + ds.send(p); + assertRecv(mcSock, true, message); + + mcSock.leaveGroup(mcGroup); + ds.send(p); + assertRecv(mcSock, false, message); + } + } + + private void assertRecv(MulticastSocket mcSock, boolean expectedSucceed, String expectedMsg) + throws IOException { + try { + byte[] buf = new byte[expectedMsg.length()]; + DatagramPacket recvPacket = new DatagramPacket(buf, buf.length); + mcSock.receive(recvPacket); + if (expectedSucceed) { + assertTrue(new String(buf).equals(expectedMsg)); + } else { + fail(); + } + } catch (SocketTimeoutException e) { + if (expectedSucceed) { + fail(); + } + } + } +} diff --git a/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java b/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java index 6ddb4831f..d809eab9a 100644 --- a/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java +++ b/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java @@ -17,24 +17,26 @@ package libcore.java.net; import junit.framework.TestCase; + +import java.io.BufferedReader; +import java.io.InputStreamReader; import java.lang.reflect.Field; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InterfaceAddress; +import java.net.MulticastSocket; import java.net.NetworkInterface; import java.net.SocketException; -import java.io.File; -import java.util.ArrayList; import java.util.Collections; +import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; -import libcore.io.IoUtils; - -import java.util.regex.Matcher; import java.util.regex.Pattern; +import static java.net.NetworkInterface.getNetworkInterfaces; + public class NetworkInterfaceTest extends TestCase { // http://code.google.com/p/android/issues/detail?id=13784 private final static int ARPHRD_ETHER = 1; // from if_arp.h @@ -80,7 +82,7 @@ public void test_collectIpv6Addresses_skipsUnmatchedLines() throws Exception { }*/ public void testInterfaceProperties() throws Exception { - for (NetworkInterface nif : Collections.list(NetworkInterface.getNetworkInterfaces())) { + for (NetworkInterface nif : Collections.list(getNetworkInterfaces())) { assertEquals(nif, NetworkInterface.getByName(nif.getName())); // Skip interfaces that are inactive if (nif.isUp() == false) { @@ -109,7 +111,7 @@ public void testLoopback() throws Exception { public void testDumpAll() throws Exception { Set allNames = new HashSet(); Set allIndexes = new HashSet(); - for (NetworkInterface nif : Collections.list(NetworkInterface.getNetworkInterfaces())) { + for (NetworkInterface nif : Collections.list(getNetworkInterfaces())) { System.err.println(nif); System.err.println(nif.getInterfaceAddresses()); String flags = nif.isUp() ? "UP" : "DOWN"; @@ -189,9 +191,52 @@ public void testInterfaceRemoval() throws Exception { } catch(SocketException expected) {} } + // b/29243557 + public void testGetNetworkInterfaces() throws Exception { + // Check that the interfaces we get from #getNetworkInterfaces agrees with IP-LINK(8). + + // Parse output of ip link. + String[] cmd = { "ip", "link" }; + Process proc = Runtime.getRuntime().exec(cmd); + BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream())); + Set expectedNiNames = new HashSet<>(); + for (String s; (s = stdInput.readLine()) != null; ) { + String[] split = s.split(": |@"); + try { + if (split.length > 2) { + expectedNiNames.add(split[1]); + } + } catch (NumberFormatException e) { + // Skip this line. + } + } + + Enumeration nifs = NetworkInterface.getNetworkInterfaces(); + Set actualNiNames = new HashSet<>(); + Collections.list(nifs).forEach(ni -> actualNiNames.add(ni.getName())); + + assertEquals(expectedNiNames, actualNiNames); + } + + // Calling getSubInterfaces on interfaces with no subinterface should not throw NPE. + // http://b/33844501 + public void testGetSubInterfaces() throws Exception { + List nifs = Collections.list(NetworkInterface.getNetworkInterfaces()); + + for (NetworkInterface nif : nifs) { + nif.getSubInterfaces(); + } + } + // Is ifName a name of a Ethernet device? private static Pattern ethernetNamePattern = Pattern.compile("^(eth|wlan)[0-9]+$"); private static boolean isEthernet(String ifName) throws Exception { return ethernetNamePattern.matcher(ifName).matches(); } + + public void testGetInterfaceAddressesDoesNotThrowNPE() throws Exception { + try (MulticastSocket mcastSock = new MulticastSocket()) { + mcastSock.getNetworkInterface().getInterfaceAddresses(); + } + } } diff --git a/luni/src/test/java/libcore/java/net/OldAndroidDatagramTest.java b/luni/src/test/java/libcore/java/net/OldAndroidDatagramTest.java index 108fd3593..65ca12aa7 100644 --- a/luni/src/test/java/libcore/java/net/OldAndroidDatagramTest.java +++ b/luni/src/test/java/libcore/java/net/OldAndroidDatagramTest.java @@ -164,30 +164,6 @@ public void testDatagram() throws Exception { } } - // Regression test for issue 1018003: DatagramSocket ignored a set timeout. - public void testDatagramSocketSetSOTimeout() throws Exception { - DatagramSocket sock = null; - int timeout = 5000; - long start = System.currentTimeMillis(); - try { - sock = new DatagramSocket(); - DatagramPacket pack = new DatagramPacket(new byte[100], 100); - sock.setSoTimeout(timeout); - sock.receive(pack); - } catch (SocketTimeoutException e) { - // expected - long delay = System.currentTimeMillis() - start; - if (Math.abs(delay - timeout) > 1000) { - fail("timeout was not accurate. expected: " + timeout - + " actual: " + delay + " miliseconds."); - } - } finally { - if (sock != null) { - sock.close(); - } - } - } - public void test_54072_DatagramSocket() throws Exception { DatagramSocket s = new DatagramSocket(null); assertTrue(s.getLocalAddress().isAnyLocalAddress()); diff --git a/luni/src/test/java/libcore/java/net/OldServerSocketTest.java b/luni/src/test/java/libcore/java/net/OldServerSocketTest.java index 85881444b..8c9c3ea72 100644 --- a/luni/src/test/java/libcore/java/net/OldServerSocketTest.java +++ b/luni/src/test/java/libcore/java/net/OldServerSocketTest.java @@ -212,15 +212,6 @@ public void test_ConstructorI_SocksSet() throws IOException { } public void test_accept() throws IOException { - ServerSocket newSocket = new ServerSocket(0); - newSocket.setSoTimeout(500); - try { - Socket accepted = newSocket.accept(); - fail("SocketTimeoutException was not thrown: " + accepted); - } catch(SocketTimeoutException expected) { - } - newSocket.close(); - ServerSocketChannel ssc = ServerSocketChannel.open(); ServerSocket ss = ssc.socket(); diff --git a/luni/src/test/java/libcore/java/net/OldSocketTest.java b/luni/src/test/java/libcore/java/net/OldSocketTest.java index 1ab0f5642..8a577fb0c 100644 --- a/luni/src/test/java/libcore/java/net/OldSocketTest.java +++ b/luni/src/test/java/libcore/java/net/OldSocketTest.java @@ -37,12 +37,13 @@ import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.SocketChannel; import java.security.Permission; +import libcore.junit.util.ResourceLeakageDetector.DisableResourceLeakageDetection; import tests.support.Support_Configuration; public class OldSocketTest extends OldSocketTestCase { private static final InetSocketAddress UNREACHABLE_ADDRESS - = new InetSocketAddress("192.0.2.0", 0); // RFC 6666 + = new InetSocketAddress("192.0.2.0", 0); // RFC 5737 ServerSocket ss; @@ -144,8 +145,9 @@ public void test_ConstructorLjava_lang_StringILjava_net_InetAddressI2() throws I public void test_ConstructorLjava_lang_StringIZ() throws IOException { // Test for method java.net.Socket(java.lang.String, int, boolean) int sport = startServer("Cons String,I,Z"); - s = new Socket(InetAddress.getLocalHost().getHostName(), sport, true); - assertTrue("Failed to create socket", s.getPort() == sport); + try (Socket s = new Socket(InetAddress.getLocalHost().getHostName(), sport, true)) { + assertTrue("Failed to create socket", s.getPort() == sport); + } s = new Socket(InetAddress.getLocalHost().getHostName(), sport, false); } @@ -169,8 +171,9 @@ public void test_ConstructorLjava_net_InetAddressILjava_net_InetAddressI() public void test_ConstructorLjava_net_InetAddressIZ() throws IOException { // Test for method java.net.Socket(java.net.InetAddress, int, boolean) int sport = startServer("Cons InetAddress,I,Z"); - s = new Socket(InetAddress.getLocalHost(), sport, true); - assertTrue("Failed to create socket", s.getPort() == sport); + try (Socket s = new Socket(InetAddress.getLocalHost(), sport, true)) { + assertTrue("Failed to create socket", s.getPort() == sport); + } s = new Socket(InetAddress.getLocalHost(), sport, false); } @@ -248,17 +251,18 @@ public void test_getLocalAddress() throws IOException { // Test for method java.net.InetAddress // java.net.Socket.getLocalAddress() int sport = startServer("SServer getLocAddress"); - s = new Socket(InetAddress.getLocalHost(), sport, null, 0); - assertEquals("Returned incorrect InetAddress", - InetAddress.getLocalHost(), s.getLocalAddress()); + try (Socket s = new Socket(InetAddress.getLocalHost(), sport, null, 0)) { + assertEquals("Returned incorrect InetAddress", + InetAddress.getLocalHost(), s.getLocalAddress()); + } // now check behavior when the ANY address is returned - s = new Socket(); - s.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0)); + try (Socket s = new Socket()) { + s.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0)); - assertTrue("ANY address not IPv6: " + s.getLocalSocketAddress(), + assertTrue("ANY address not IPv6: " + s.getLocalSocketAddress(), s.getLocalAddress() instanceof Inet6Address); - s.close(); + } } public void test_getLocalPort() throws IOException { @@ -275,32 +279,31 @@ public void test_getOutputStream() throws IOException { // Test for method java.io.OutputStream // java.net.Socket.getOutputStream() int sport = startServer("SServer getOutputStream"); - s = new Socket(InetAddress.getLocalHost(), sport); - java.io.OutputStream os = s.getOutputStream(); - assertNotNull("Failed to get stream", os); - os.write(1); - s.close(); + try (Socket s = new Socket(InetAddress.getLocalHost(), sport)) { + java.io.OutputStream os = s.getOutputStream(); + assertNotNull("Failed to get stream", os); + os.write(1); + } + // Regression test for harmony-2934 - s = new Socket("127.0.0.1", sport, false); - OutputStream o = s.getOutputStream(); - o.write(1); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { + try (Socket s = new Socket("127.0.0.1", sport, false); + OutputStream o = s.getOutputStream()) { + o.write(1); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } } - o.close(); - s.close(); // Regression test for harmony-2942 - s = new Socket("0.0.0.0", sport, false); - o = s.getOutputStream(); - o.write(1); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { + try (Socket s = new Socket("0.0.0.0", sport, false); + OutputStream o = s.getOutputStream()) { + o.write(1); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } } - o.close(); - s.close(); } public void test_getPort() throws IOException { @@ -313,8 +316,7 @@ public void test_getPort() throws IOException { public void test_getSoLinger() { // Test for method int java.net.Socket.getSoLinger() int sport = startServer("SServer getSoLinger"); - try { - s = new Socket(InetAddress.getLocalHost(), sport, null, 0); + try (Socket s = new Socket(InetAddress.getLocalHost(), sport, null, 0)) { s.setSoLinger(true, 200); assertEquals("Returned incorrect linger", 200, s.getSoLinger()); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_LINGER); @@ -364,8 +366,7 @@ public void test_getReceiveBufferSize() { public void test_getSendBufferSize() { int sport = startServer("SServer setSendBufferSize"); - try { - s = new Socket(InetAddress.getLocalHost().getHostName(), sport, null, 0); + try (Socket s = new Socket(InetAddress.getLocalHost().getHostName(), sport, null, 0)) { s.setSendBufferSize(134); assertTrue("Incorrect buffer size", s.getSendBufferSize() >= 134); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_SNDBUF); @@ -408,8 +409,7 @@ public void test_getSoTimeout_setSoTimeout() throws Exception { public void test_getTcpNoDelay() { // Test for method boolean java.net.Socket.getTcpNoDelay() int sport = startServer("SServer getTcpNoDelay"); - try { - s = new Socket(InetAddress.getLocalHost(), sport, null, 0); + try (Socket s = new Socket(InetAddress.getLocalHost(), sport, null, 0)) { boolean bool = !s.getTcpNoDelay(); s.setTcpNoDelay(bool); assertTrue("Failed to get no delay setting: " + s.getTcpNoDelay(), @@ -436,9 +436,8 @@ public void test_getTcpNoDelay() { public void test_setKeepAliveZ() throws Exception { // There is not really a good test for this as it is there to detect // crashed machines. Just make sure we can set it - try { - int sport = startServer("SServer setKeepAlive"); - Socket theSocket = new Socket(InetAddress.getLocalHost(), sport, null, 0); + int sport = startServer("SServer setKeepAlive"); + try (Socket theSocket = new Socket(InetAddress.getLocalHost(), sport, null, 0)) { theSocket.setKeepAlive(true); theSocket.setKeepAlive(false); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_KEEPALIVE); @@ -446,7 +445,9 @@ public void test_setKeepAliveZ() throws Exception { handleException(e, SO_KEEPALIVE); } // regression test for HARMONY-1136 - new TestSocket((SocketImpl) null).setKeepAlive(true); + try (TestSocket testSocket = new TestSocket((SocketImpl) null)) { + testSocket.setKeepAlive(true); + } try { Socket theSocket = new Socket(); @@ -784,31 +785,27 @@ public void test_isConnected() throws IOException { } public void test_isClosed() throws IOException { - ServerSocket serverSocket = new ServerSocket(0, 5); - Socket theSocket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); - Socket servSock = serverSocket.accept(); + try (ServerSocket serverSocket = new ServerSocket(0, 5)) { + Socket theSocket = new Socket(serverSocket.getInetAddress(), + serverSocket.getLocalPort()); + Socket servSock = serverSocket.accept(); - // validate isClosed returns expected values - assertFalse("Socket should indicate it is not closed(1):", theSocket - .isClosed()); - theSocket.close(); - assertTrue("Socket should indicate it is closed(1):", theSocket - .isClosed()); + // validate isClosed returns expected values + assertFalse("Socket should indicate it is not closed(1):", theSocket.isClosed()); + theSocket.close(); + assertTrue("Socket should indicate it is closed(1):", theSocket.isClosed()); - theSocket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); - assertFalse("Socket should indicate it is not closed(2):", theSocket - .isClosed()); - theSocket.close(); - assertTrue("Socket should indicate it is closed(2):", theSocket - .isClosed()); + theSocket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); + assertFalse("Socket should indicate it is not closed(2):", theSocket.isClosed()); + theSocket.close(); + assertTrue("Socket should indicate it is closed(2):", theSocket.isClosed()); - // validate that isClosed works ok for sockets returned from - // ServerSocket.accept() - assertFalse("Server Socket should indicate it is not closed:", servSock - .isClosed()); - servSock.close(); - assertTrue("Server Socket should indicate it is closed:", servSock - .isClosed()); + // validate that isClosed works ok for sockets returned from + // ServerSocket.accept() + assertFalse("Server Socket should indicate it is not closed:", servSock.isClosed()); + servSock.close(); + assertTrue("Server Socket should indicate it is closed:", servSock.isClosed()); + } } public void test_bindLjava_net_SocketAddress() throws IOException { @@ -910,28 +907,6 @@ public mySocketAddress() { } } - class SocketCloser extends Thread { - - int timeout = 0; - - Socket theSocket = null; - - public void run() { - try { - Thread.sleep(timeout); - theSocket.close(); - } catch (Exception e) { - } - ; - return; - } - - public SocketCloser(int timeout, Socket theSocket) { - this.timeout = timeout; - this.theSocket = theSocket; - } - } - // start by validating the error checks byte[] theBytes = { 0, 0, 0, 0 }; @@ -1052,15 +1027,7 @@ public SocketCloser(int timeout, Socket theSocket) { Thread.sleep(1000); - int totalBytesRead = 0; - byte[] myBytes = new byte[100]; - while (theInput.available() > 0) { - int bytesRead = theInput.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; - } - - String receivedString = new String(myBytes, 0, totalBytesRead); + String receivedString = readShortString(theInput); assertTrue("Could not recv on socket connected with timeout:" + receivedString + ":" + sendString, receivedString .equals(sendString)); @@ -1070,15 +1037,7 @@ public SocketCloser(int timeout, Socket theSocket) { theOutput2.flush(); Thread.sleep(1000); - totalBytesRead = 0; - myBytes = new byte[100]; - while (theInput2.available() > 0) { - int bytesRead = theInput2.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; - } - - receivedString = new String(myBytes, 0, totalBytesRead); + receivedString = readShortString(theInput2); assertTrue("Could not send on socket connected with timeout:" + receivedString + ":" + sendString, receivedString .equals(sendString)); @@ -1106,52 +1065,6 @@ public mySocketAddress() { } } - class SocketCloser extends Thread { - - int timeout = 0; - - Socket theSocket = null; - - public void run() { - try { - Thread.sleep(timeout); - theSocket.close(); - } catch (Exception e) { - } - return; - } - - public SocketCloser(int timeout, Socket theSocket) { - this.timeout = timeout; - this.theSocket = theSocket; - } - } - - class SocketConnector extends Thread { - - int timeout = 0; - - Socket theSocket = null; - - SocketAddress address = null; - - public void run() { - try { - theSocket.connect(address, timeout); - } catch (Exception e) { - } - - return; - } - - public SocketConnector(int timeout, Socket theSocket, - SocketAddress address) { - this.timeout = timeout; - this.theSocket = theSocket; - this.address = address; - } - } - // start by validating the error checks byte[] theBytes = { 0, 0, 0, 0 }; SocketAddress theAddress = new InetSocketAddress(InetAddress.getLocalHost(), 0); @@ -1159,11 +1072,7 @@ public SocketConnector(int timeout, Socket theSocket, SocketAddress nonReachableAddress = UNREACHABLE_ADDRESS; SocketAddress invalidType = new mySocketAddress(); - Socket theSocket = null; - ServerSocket serverSocket = null; - - try { - theSocket = new Socket(); + try (Socket theSocket = new Socket()) { theSocket.connect(theAddress, -100); fail("No exception after negative timeout passed in"); } catch (Exception e) { @@ -1171,8 +1080,7 @@ public SocketConnector(int timeout, Socket theSocket, + e.toString(), (e instanceof IllegalArgumentException)); } - try { - theSocket = new Socket(); + try (Socket theSocket = new Socket()) { theSocket.connect(null, 0); fail("No exception after null address passed in"); } catch (Exception e) { @@ -1180,8 +1088,7 @@ public SocketConnector(int timeout, Socket theSocket, + e.toString(), (e instanceof IllegalArgumentException)); } - try { - theSocket = new Socket(); + try (Socket theSocket = new Socket()) { theSocket.connect(invalidType, 100000); fail("No exception when invalid socket address type passed in: "); } catch (Exception e) { @@ -1191,8 +1098,7 @@ public SocketConnector(int timeout, Socket theSocket, (e instanceof IllegalArgumentException)); } - try { - theSocket = new Socket(); + try (Socket theSocket = new Socket()) { theSocket.connect(nonConnectableAddress, 100000); fail("No exception when non Connectable Address passed in: "); } catch (Exception e) { @@ -1203,10 +1109,8 @@ public SocketConnector(int timeout, Socket theSocket, // now validate that we get a connect exception if we try to connect to // an address on which nobody is listening - try { - theSocket = new Socket(); + try (Socket theSocket = new Socket()) { theSocket.connect(theAddress, 0); - theSocket.close(); fail("No timeout:No exception when connecting to address nobody listening on: "); } catch (Exception e) { assertTrue( @@ -1215,191 +1119,197 @@ public SocketConnector(int timeout, Socket theSocket, } // now validate that we can actually connect when somebody is listening - theSocket = new Socket(); - serverSocket = new ServerSocket(0, 5); - theSocket.connect(serverSocket.getLocalSocketAddress()); - theSocket.close(); - serverSocket.close(); + try (Socket theSocket = new Socket(); + ServerSocket serverSocket = new ServerSocket(0, 5)) { + theSocket.connect(serverSocket.getLocalSocketAddress()); + } // now validate that we get a connect exception if we try to connect to // an address on which nobody is listening - try { - theSocket = new Socket(); - theSocket.connect(new InetSocketAddress(InetAddress.getLocalHost(), 80), 100000); - theSocket.close(); - fail("No exception when connecting to address nobody listening on: "); - } catch (Exception e) { - assertTrue( - "Wrong exception when connecting to address nobody listening on: " - + e.toString(), (e instanceof ConnectException)); + try (Socket theSocket = new Socket()) { + try { + theSocket.connect(new InetSocketAddress(InetAddress.getLocalHost(), 80), 100000); + fail("No exception when connecting to address nobody listening on: "); + } catch (Exception e) { + assertTrue( + "Wrong exception when connecting to address nobody listening on: " + + e.toString(), (e instanceof ConnectException)); + } } // now validate that we get a interrupted exception if we try to connect // to an address on which nobody is accepting connections and the // timeout expired - try { - theSocket = new Socket(); - theSocket.connect(nonReachableAddress, 200); - theSocket.close(); - fail("No interrupted exception when connecting to address nobody listening on with short timeout 200: "); - } catch (ConnectException ce) { - // some networks will quickly reset the TCP connection attempt to this fake IP - assertTrue( - "Wrong exception when connecting to address nobody listening on with short timeout 200: " - + ce.toString(), - (ce.getMessage() != null && ce.getMessage().contains("ECONNREFUSED"))); - } catch (Exception e) { - assertTrue( - "Wrong exception when connecting to address nobody listening on with short timeout 200: " - + e.toString(), - (e instanceof SocketTimeoutException)); + try (Socket theSocket = new Socket()) { + try { + theSocket.connect(nonReachableAddress, 200); + fail("No interrupted exception when connecting to address nobody listening on with short timeout 200: "); + } catch (ConnectException ce) { + // some networks will quickly reset the TCP connection attempt to this fake IP + assertTrue( + "Wrong exception when connecting to address nobody listening on with short timeout 200: " + + ce.toString(), + (ce.getMessage() != null && ce.getMessage().contains("ECONNREFUSED"))); + } catch (Exception e) { + assertTrue( + "Wrong exception when connecting to address nobody listening on with short timeout 200: " + + e.toString(), + (e instanceof SocketTimeoutException)); + } } // now validate that we get a interrupted exception if we try to connect // to an address on which nobody is accepting connections and the // timeout expired - try { - theSocket = new Socket(); - theSocket.connect(nonReachableAddress, 40); - theSocket.close(); - fail("No interrupted exception when connecting to address nobody listening on with short timeout 40: "); - } catch (ConnectException ce) { - // some networks will quickly reset the TCP connection attempt to this fake IP - assertTrue( - "Wrong exception when connecting to address nobody listening on with short timeout 40: " - + ce.toString(), - (ce.getMessage() != null && ce.getMessage().contains("ECONNREFUSED"))); - } catch (Exception e) { - assertTrue( - "Wrong exception when connecting to address nobody listening on with short timeout 40: " - + e.toString(), - (e instanceof SocketTimeoutException)); + try (Socket theSocket = new Socket()) { + try { + theSocket.connect(nonReachableAddress, 40); + fail("No interrupted exception when connecting to address nobody listening on with short timeout 40: "); + } catch (ConnectException ce) { + // some networks will quickly reset the TCP connection attempt to this fake IP + assertTrue( + "Wrong exception when connecting to address nobody listening on with short timeout 40: " + + ce.toString(), + (ce.getMessage() != null && ce.getMessage().contains("ECONNREFUSED"))); + } catch (Exception e) { + assertTrue( + "Wrong exception when connecting to address nobody listening on with short timeout 40: " + + e.toString(), + (e instanceof SocketTimeoutException)); + } } // now validate that we can actually connect when somebody is listening - theSocket = new Socket(); - serverSocket = new ServerSocket(0, 5); - theSocket.connect(serverSocket.getLocalSocketAddress()); + try (Socket theSocket = new Socket(); + ServerSocket serverSocket = new ServerSocket(0, 5)) { + theSocket.connect(serverSocket.getLocalSocketAddress()); - // validate that when a socket is connected that it answers - // correctly to related queries - assertTrue("Socket did not returned connected when it is: ", theSocket - .isConnected()); - assertFalse("Socket returned closed when it should be connected ", - theSocket.isClosed()); - assertTrue("Socket returned not bound when it should be: ", theSocket - .isBound()); - assertFalse( - "Socket returned input Shutdown when it should be connected ", - theSocket.isInputShutdown()); - assertFalse( - "Socket returned output Shutdown when it should be connected ", - theSocket.isOutputShutdown()); - assertTrue("Local port on connected socket was 0", theSocket - .getLocalPort() != 0); - theSocket.close(); - serverSocket.close(); + // validate that when a socket is connected that it answers + // correctly to related queries + assertTrue("Socket did not returned connected when it is: ", theSocket + .isConnected()); + assertFalse("Socket returned closed when it should be connected ", + theSocket.isClosed()); + assertTrue("Socket returned not bound when it should be: ", theSocket + .isBound()); + assertFalse( + "Socket returned input Shutdown when it should be connected ", + theSocket.isInputShutdown()); + assertFalse( + "Socket returned output Shutdown when it should be connected ", + theSocket.isOutputShutdown()); + assertTrue("Local port on connected socket was 0", theSocket + .getLocalPort() != 0); + } // now validate that we get the right exception if we connect when we // are already connected - try { - theSocket = new Socket(); - serverSocket = new ServerSocket(); + try (Socket theSocket = new Socket(); + ServerSocket serverSocket = new ServerSocket()) { serverSocket.bind(theAddress); - theSocket.connect(theAddress, 100000); - theSocket.connect(theAddress, 100000); - theSocket.close(); - serverSocket.close(); - fail("No exception when we try to connect on a connected socket: "); - - } catch (Exception e) { - assertTrue( - "Wrong exception when connecting on socket that is already connected" - + e.toString(), (e instanceof SocketException)); - assertFalse( - "Wrong exception when connecting on socket that is already connected" - + e.toString(), - (e instanceof SocketTimeoutException)); + theSocket.connect(serverSocket.getLocalSocketAddress(), 100000); try { - theSocket.close(); - serverSocket.close(); - } catch (Exception e2) { + theSocket.connect(serverSocket.getLocalSocketAddress(), 100000); + fail("No exception when we try to connect on a connected socket: "); + } catch (Exception e) { + assertTrue( + "Wrong exception when connecting on socket that is already connected" + + e.toString(), (e instanceof SocketException)); + assertFalse( + "Wrong exception when connecting on socket that is already connected" + + e.toString(), + (e instanceof SocketTimeoutException)); } - } // now validate that connected socket can be used to read/write - theSocket = new Socket(); - serverSocket = new ServerSocket(0, 5); - theSocket.connect(serverSocket.getLocalSocketAddress()); - Socket servSock = serverSocket.accept(); - InputStream theInput = theSocket.getInputStream(); - OutputStream theOutput = servSock.getOutputStream(); - InputStream theInput2 = servSock.getInputStream(); - OutputStream theOutput2 = theSocket.getOutputStream(); + SocketAddress localSocketAddress; + try (Socket theSocket = new Socket(); + ServerSocket serverSocket = new ServerSocket(0, 5)) { + localSocketAddress = serverSocket.getLocalSocketAddress(); + theSocket.connect(localSocketAddress); + Socket servSock = serverSocket.accept(); + InputStream theInput = theSocket.getInputStream(); + OutputStream theOutput = servSock.getOutputStream(); + InputStream theInput2 = servSock.getInputStream(); + OutputStream theOutput2 = theSocket.getOutputStream(); + + String sendString = new String("Test"); + theOutput.write(sendString.getBytes()); + theOutput.flush(); - String sendString = new String("Test"); - theOutput.write(sendString.getBytes()); - theOutput.flush(); + Thread.sleep(1000); - Thread.sleep(1000); + String receivedString = readShortString(theInput); + assertTrue("Could not recv on socket connected with timeout:" + + receivedString + ":" + sendString, receivedString + .equals(sendString)); - int totalBytesRead = 0; - byte[] myBytes = new byte[100]; - while (theInput.available() > 0) { - int bytesRead = theInput.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; + sendString = new String("SEND - Test"); + theOutput2.write(sendString.getBytes()); + theOutput2.flush(); + + receivedString = readShortString(theInput2); + assertTrue("Could not send on socket connected with timeout:" + + receivedString + ":" + sendString, receivedString + .equals(sendString)); } - String receivedString = new String(myBytes, 0, totalBytesRead); - assertTrue("Could not recv on socket connected with timeout:" - + receivedString + ":" + sendString, receivedString - .equals(sendString)); + try (SocketChannel channel = SocketChannel.open()) { + channel.configureBlocking(false); + Socket socket = channel.socket(); + try { + socket.connect(localSocketAddress); + fail("IllegalBlockingModeException was not thrown."); + } catch (IllegalBlockingModeException expected) { + } + } + } - sendString = new String("SEND - Test"); - theOutput2.write(sendString.getBytes()); - theOutput2.flush(); + @DisableResourceLeakageDetection( + why = "Strange threading behavior causes resource leak", + bug = "31820278") + public void test_connectLjava_net_SocketAddressI_setSOTimeout() throws Exception { + class SocketConnector extends Thread { - totalBytesRead = 0; - myBytes = new byte[100]; - Thread.sleep(1000); - while (theInput2.available() > 0) { - int bytesRead = theInput2.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; - } + int timeout = 0; - receivedString = new String(myBytes, 0, totalBytesRead); - assertTrue("Could not send on socket connected with timeout:" - + receivedString + ":" + sendString, receivedString - .equals(sendString)); + Socket theSocket = null; - theSocket.close(); - serverSocket.close(); + SocketAddress address = null; - // now try to set options while we are connecting - theSocket = new Socket(); - SocketConnector connector = new SocketConnector(5000, theSocket, nonReachableAddress); - connector.start(); - theSocket.setSoTimeout(1000); - Thread.sleep(10); - assertTrue("Socket option not set during connect: 10 ", Math.abs(1000 - theSocket.getSoTimeout()) <= 10); - Thread.sleep(50); - theSocket.setSoTimeout(2000); - assertTrue("Socket option not set during connect: 50 ", Math.abs(2000 - theSocket.getSoTimeout()) <= 10); - Thread.sleep(5000); - theSocket.close(); + public void run() { + try { + theSocket.connect(address, timeout); + } catch (Exception e) { + } - SocketChannel channel = SocketChannel.open(); - channel.configureBlocking(false); - Socket socket = channel.socket(); - try { - socket.connect(serverSocket.getLocalSocketAddress()); - fail("IllegalBlockingModeException was not thrown."); - } catch (IllegalBlockingModeException expected) { + return; + } + + public SocketConnector(int timeout, Socket theSocket, + SocketAddress address) { + this.timeout = timeout; + this.theSocket = theSocket; + this.address = address; + } + } + + // now try to set options while we are connecting + SocketAddress nonReachableAddress = UNREACHABLE_ADDRESS; + try (Socket theSocket = new Socket()) { + SocketConnector connector = new SocketConnector(5000, theSocket, nonReachableAddress); + connector.start(); + theSocket.setSoTimeout(1000); + Thread.sleep(10); + assertTrue("Socket option not set during connect: 10 ", + Math.abs(1000 - theSocket.getSoTimeout()) <= 10); + Thread.sleep(50); + theSocket.setSoTimeout(2000); + assertTrue("Socket option not set during connect: 50 ", + Math.abs(2000 - theSocket.getSoTimeout()) <= 10); + Thread.sleep(5000); } - channel.close(); } public void test_isInputShutdown() throws IOException { @@ -1554,8 +1464,7 @@ public void test_setReuseAddressZ() throws Exception { } public void test_getReuseAddress() { - try { - Socket theSocket = new Socket(); + try (Socket theSocket = new Socket()) { theSocket.setReuseAddress(true); assertTrue("getReuseAddress false when it should be true", theSocket.getReuseAddress()); @@ -1583,8 +1492,7 @@ public void test_getReuseAddress() { public void test_setOOBInlineZ() { // mostly tested in getOOBInline. Just set to make sure call works ok - try { - Socket theSocket = new Socket(); + try (Socket theSocket = new Socket()) { theSocket.setOOBInline(true); assertTrue("expected OOBIline to be true", theSocket.getOOBInline()); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_OOBINLINE); @@ -1634,12 +1542,10 @@ public void test_getOOBInline() { } public void test_setTrafficClassI() { - try { + try (Socket theSocket = new Socket()) { int IPTOS_LOWCOST = 0x2; int IPTOS_THROUGHPUT = 0x8; - Socket theSocket = new Socket(); - // validate that value set must be between 0 and 255 try { theSocket.setTrafficClass(256); @@ -1674,9 +1580,7 @@ public void test_setTrafficClassI() { } public void test_getTrafficClass() { - try { - Socket theSocket = new Socket(); - + try (Socket theSocket = new Socket()) { /* * we cannot actually check that the values are set as if a platform * does not support the option then it may come back unset even @@ -1699,7 +1603,7 @@ public void test_getChannel() throws Exception { channel.close(); } - public void test_sendUrgentDataI() { + public void test_sendUrgentDataI() throws IOException { // Some platforms may not support urgent data in this case we will not // run these tests. For now run on all platforms until we find those @@ -1710,270 +1614,212 @@ public void test_sendUrgentDataI() { // is silently ignored String urgentData = "U"; try { - Socket theSocket = new Socket(); - ServerSocket serverSocket = new ServerSocket(0, 5); - theSocket.connect(serverSocket.getLocalSocketAddress()); - Socket servSock = serverSocket.accept(); - InputStream theInput = theSocket.getInputStream(); - OutputStream theOutput = servSock.getOutputStream(); - - // send the regular data - String sendString = new String("Test"); - theOutput.write(sendString.getBytes()); - theOutput.flush(); - - // send the urgent data which should not be received - theSocket.setOOBInline(false); - servSock.sendUrgentData(urgentData.getBytes()[0]); - theOutput.write(sendString.getBytes()); - theOutput.flush(); - - // give things some time to settle - Thread.sleep(1000); - - int totalBytesRead = 0; - byte[] myBytes = new byte[100]; - while (theInput.available() > 0) { - int bytesRead = theInput.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; + try (Socket theSocket = new Socket(); + ServerSocket serverSocket = new ServerSocket(0, 5)) { + theSocket.connect(serverSocket.getLocalSocketAddress()); + try (Socket servSock = serverSocket.accept(); + InputStream theInput = theSocket.getInputStream(); + OutputStream theOutput = servSock.getOutputStream()) { + + // send the regular data + String sendString = "Test"; + theOutput.write(sendString.getBytes()); + theOutput.flush(); + + // send the urgent data which should not be received + theSocket.setOOBInline(false); + servSock.sendUrgentData(urgentData.getBytes()[0]); + theOutput.write(sendString.getBytes()); + theOutput.flush(); + + // give things some time to settle + Thread.sleep(1000); + + String receivedString = readShortString(theInput); + //assertTrue("Urgent Data seems to have been received:" + // + receivedString + ":" + sendString, receivedString + // .equals(sendString + sendString)); + } } - String receivedString = new String(myBytes, 0, totalBytesRead); - //assertTrue("Urgent Data seems to have been received:" - // + receivedString + ":" + sendString, receivedString - // .equals(sendString + sendString)); - - theSocket.close(); - serverSocket.close(); - // now validate that urgent data is received as expected. Expect // that it should be between the two writes. - theSocket = new Socket(); - serverSocket = new ServerSocket(0, 5); - theSocket.connect(serverSocket.getLocalSocketAddress()); - servSock = serverSocket.accept(); - theInput = theSocket.getInputStream(); - theOutput = servSock.getOutputStream(); + try (Socket theSocket = new Socket(); + ServerSocket serverSocket = new ServerSocket(0, 5)) { + theSocket.connect(serverSocket.getLocalSocketAddress()); + try (Socket servSock = serverSocket.accept(); + InputStream theInput = theSocket.getInputStream(); + OutputStream theOutput = servSock.getOutputStream()) { - // send the regular data - sendString = new String("Test - Urgent Data"); - theOutput.write(sendString.getBytes()); - theOutput.flush(); + // send the regular data + String sendString = "Test - Urgent Data"; + theOutput.write(sendString.getBytes()); + theOutput.flush(); - // send the urgent data which should be received - theSocket.setOOBInline(true); - servSock.sendUrgentData(urgentData.getBytes()[0]); + // send the urgent data which should be received + theSocket.setOOBInline(true); + servSock.sendUrgentData(urgentData.getBytes()[0]); - theOutput.write(sendString.getBytes()); - theOutput.flush(); + theOutput.write(sendString.getBytes()); + theOutput.flush(); - Thread.sleep(1000); + Thread.sleep(1000); - totalBytesRead = 0; - myBytes = new byte[100]; - while (theInput.available() > 0) { - int bytesRead = theInput.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; + String receivedString = readShortString(theInput); + assertTrue("Urgent Data was not received with one urgent byte:" + + receivedString + ":" + sendString + urgentData + + sendString, receivedString.equals(sendString + + urgentData + sendString)); + } } - receivedString = new String(myBytes, 0, totalBytesRead); - assertTrue("Urgent Data was not received with one urgent byte:" - + receivedString + ":" + sendString + urgentData - + sendString, receivedString.equals(sendString - + urgentData + sendString)); - - theSocket.close(); - serverSocket.close(); - // now test case where we try to send two urgent bytes. - theSocket = new Socket(); - serverSocket = new ServerSocket(0, 5); - theSocket.connect(serverSocket.getLocalSocketAddress()); - servSock = serverSocket.accept(); - theInput = theSocket.getInputStream(); - theOutput = servSock.getOutputStream(); - - // send the regular data - sendString = new String("Test - Urgent Data"); - theOutput.write(sendString.getBytes()); - theOutput.flush(); - - // send the urgent data which should not be received - theSocket.setOOBInline(true); - servSock.sendUrgentData(urgentData.getBytes()[0]); - servSock.sendUrgentData(urgentData.getBytes()[0]); - - theOutput.write(sendString.getBytes()); - theOutput.flush(); - - Thread.sleep(1000); - - totalBytesRead = 0; - myBytes = new byte[100]; - while (theInput.available() > 0) { - int bytesRead = theInput.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; + try (Socket theSocket = new Socket(); + ServerSocket serverSocket = new ServerSocket(0, 5)) { + theSocket.connect(serverSocket.getLocalSocketAddress()); + try (Socket servSock = serverSocket.accept(); + InputStream theInput = theSocket.getInputStream(); + OutputStream theOutput = servSock.getOutputStream()) { + + // send the regular data + String sendString = "Test - Urgent Data"; + theOutput.write(sendString.getBytes()); + theOutput.flush(); + + // send the urgent data which should not be received + theSocket.setOOBInline(true); + servSock.sendUrgentData(urgentData.getBytes()[0]); + servSock.sendUrgentData(urgentData.getBytes()[0]); + + theOutput.write(sendString.getBytes()); + theOutput.flush(); + + Thread.sleep(1000); + + String receivedString = readShortString(theInput); + assertTrue( + "Did not get right byte of urgent data when two sent:" + + receivedString + ":" + sendString + + urgentData + urgentData + sendString, + receivedString.equals(sendString + urgentData + + urgentData + sendString)); + } } - receivedString = new String(myBytes, 0, totalBytesRead); - assertTrue( - "Did not get right byte of urgent data when two sent:" - + receivedString + ":" + sendString - + urgentData + urgentData + sendString, - receivedString.equals(sendString + urgentData - + urgentData + sendString)); - - theSocket.close(); - serverSocket.close(); - /* * TODO : These do not currently pass on XP SP2 and Server 2003 */ if (!platform.startsWith("Windows")) { // now test the case were we send turn the OOBInline on/off - theSocket = new Socket(); - serverSocket = new ServerSocket(0, 5); - theSocket.connect(serverSocket.getLocalSocketAddress()); - servSock = serverSocket.accept(); - theInput = theSocket.getInputStream(); - theOutput = servSock.getOutputStream(); - - // send the regular data - sendString = new String("Test - Urgent Data"); - theOutput.write(sendString.getBytes()); - theOutput.flush(); - - // send the urgent data which should be received - theSocket.setOOBInline(true); - servSock.sendUrgentData(urgentData.getBytes()[0]); - - theOutput.write(sendString.getBytes()); - theOutput.flush(); - - Thread.sleep(1000); - - totalBytesRead = 0; - myBytes = new byte[100]; - while (theInput.available() > 0) { - int bytesRead = theInput.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; + try (Socket theSocket = new Socket(); + ServerSocket serverSocket = new ServerSocket(0, 5)) { + theSocket.connect(serverSocket.getLocalSocketAddress()); + try (Socket servSock = serverSocket.accept(); + InputStream theInput = theSocket.getInputStream(); + OutputStream theOutput = servSock.getOutputStream()) { + + // send the regular data + String sendString = "Test - Urgent Data"; + theOutput.write(sendString.getBytes()); + theOutput.flush(); + + // send the urgent data which should be received + theSocket.setOOBInline(true); + servSock.sendUrgentData(urgentData.getBytes()[0]); + + theOutput.write(sendString.getBytes()); + theOutput.flush(); + + Thread.sleep(1000); + + String receivedString = readShortString(theInput); + assertTrue( + "Did not get urgent data when turning on/off(1):" + + receivedString + ":" + sendString + + urgentData + sendString, receivedString + .equals(sendString + urgentData + + sendString)); + + // send the regular data + sendString = "Test - Urgent Data"; + theOutput.write(sendString.getBytes()); + theOutput.flush(); + + // send the urgent data which should not be received + theSocket.setOOBInline(false); + servSock.sendUrgentData(urgentData.getBytes()[0]); + + // send trailing data + theOutput.write(sendString.getBytes()); + theOutput.flush(); + + Thread.sleep(1000); + + receivedString = readShortString(theInput); + //assertTrue( + // "Got unexpected data data when turning on/off(2):" + // + receivedString + ":" + sendString + // + sendString, receivedString + // .equals(sendString + sendString)); + + // now turn back on and get data. Here we also + // get the previously sent byte of urgent data as it is + // still in the urgent buffer + + // send the regular data + sendString = "Test - Urgent Data"; + theOutput.write(sendString.getBytes()); + theOutput.flush(); + + // send the urgent data which should be received again + theSocket.setOOBInline(true); + servSock.sendUrgentData(urgentData.getBytes()[0]); + + theOutput.write(sendString.getBytes()); + theOutput.flush(); + + Thread.sleep(1000); + + receivedString = readShortString(theInput); + // depending on the platform we may get the previously sent + // urgent data or not (examples windows-yes, Linux-no). + // So accept either so long as we get the urgent data from + // when it was on. + //assertTrue( + // "Did not get urgent data when turning on/off(3) GOT:" + // + receivedString + ":Expected" + urgentData + // + sendString + urgentData + sendString + // + ":OR:" + sendString + urgentData + // + sendString, + // (receivedString.equals(urgentData + sendString + // + urgentData + sendString) || receivedString + // .equals(sendString + urgentData + // + sendString))); + } } - - receivedString = new String(myBytes, 0, totalBytesRead); - assertTrue( - "Did not get urgent data when turning on/off(1):" - + receivedString + ":" + sendString - + urgentData + sendString, receivedString - .equals(sendString + urgentData - + sendString)); - - // send the regular data - sendString = new String("Test - Urgent Data"); - theOutput.write(sendString.getBytes()); - theOutput.flush(); - - // send the urgent data which should not be received - theSocket.setOOBInline(false); - servSock.sendUrgentData(urgentData.getBytes()[0]); - - // send trailing data - theOutput.write(sendString.getBytes()); - theOutput.flush(); - - Thread.sleep(1000); - - totalBytesRead = 0; - myBytes = new byte[100]; - while (theInput.available() > 0) { - int bytesRead = theInput.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; - } - - receivedString = new String(myBytes, 0, totalBytesRead); - //assertTrue( - // "Got unexpected data data when turning on/off(2):" - // + receivedString + ":" + sendString - // + sendString, receivedString - // .equals(sendString + sendString)); - - // now turn back on and get data. Here we also - // get the previously sent byte of urgent data as it is - // still in the urgent buffer - - // send the regular data - sendString = new String("Test - Urgent Data"); - theOutput.write(sendString.getBytes()); - theOutput.flush(); - - // send the urgent data which should be received again - theSocket.setOOBInline(true); - servSock.sendUrgentData(urgentData.getBytes()[0]); - - theOutput.write(sendString.getBytes()); - theOutput.flush(); - - Thread.sleep(1000); - - totalBytesRead = 0; - myBytes = new byte[100]; - while (theInput.available() > 0) { - int bytesRead = theInput.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; - } - - receivedString = new String(myBytes, 0, totalBytesRead); - // depending on the platform we may get the previously sent - // urgent data or not (examples windows-yes, Linux-no). - // So accept either so long as we get the urgent data from - // when it was on. - //assertTrue( - // "Did not get urgent data when turning on/off(3) GOT:" - // + receivedString + ":Expected" + urgentData - // + sendString + urgentData + sendString - // + ":OR:" + sendString + urgentData - // + sendString, - // (receivedString.equals(urgentData + sendString - // + urgentData + sendString) || receivedString - // .equals(sendString + urgentData - // + sendString))); - - theSocket.close(); - serverSocket.close(); } // now test the case where there is only urgent data - theSocket = new Socket(); - serverSocket = new ServerSocket(0, 5); - theSocket.connect(serverSocket.getLocalSocketAddress()); - servSock = serverSocket.accept(); - theInput = theSocket.getInputStream(); - theOutput = servSock.getOutputStream(); + try (Socket theSocket = new Socket(); + ServerSocket serverSocket = new ServerSocket(0, 5)) { + theSocket.connect(serverSocket.getLocalSocketAddress()); + try (Socket servSock = serverSocket.accept(); + InputStream theInput = theSocket.getInputStream(); + OutputStream theOutput = servSock.getOutputStream()) { - // send the urgent data which should not be received. - theSocket.setOOBInline(true); - servSock.sendUrgentData(urgentData.getBytes()[0]); + // send the urgent data which should not be received. + theSocket.setOOBInline(true); + servSock.sendUrgentData(urgentData.getBytes()[0]); - Thread.sleep(1000); + Thread.sleep(1000); - totalBytesRead = 0; - myBytes = new byte[100]; - while (theInput.available() > 0) { - int bytesRead = theInput.read(myBytes, totalBytesRead, - myBytes.length - totalBytesRead); - totalBytesRead = totalBytesRead + bytesRead; + String receivedString = readShortString(theInput); + assertTrue("Did not get urgent data only urgent data sent:" + + receivedString + ":" + urgentData, receivedString + .equals(urgentData)); + } } - receivedString = new String(myBytes, 0, totalBytesRead); - assertTrue("Did not get urgent data only urgent data sent:" - + receivedString + ":" + urgentData, receivedString - .equals(urgentData)); - } catch (Exception e) { // for platforms that do not support urgent data we expect an // exception. For the others report an error. @@ -1985,20 +1831,50 @@ public void test_sendUrgentDataI() { + e.toString()); } } + } + // Calling sendUrgentData on a closed socket should not allocate a new impl and leak resources. + // Bug: 31818400 + public void test_sendUrgentDataI_leaky() throws IOException { + Socket theSocket = new Socket(); + theSocket.close(); try { - Socket theSocket = new Socket(); - theSocket.close(); theSocket.sendUrgentData(0); fail("IOException was not thrown."); - } catch(IOException ioe) { + } catch (IOException ioe) { //expected } } - public void test_setPerformancePreference_Int_Int_Int() throws Exception { + // Calling getTrafficClass on a closed socket should not allocate a new impl and leak resources. + // Bug: 31818400 + public void test_getTrafficClass_leaky() throws IOException { Socket theSocket = new Socket(); - theSocket.setPerformancePreferences(1, 1, 1); + theSocket.close(); + try { + theSocket.getTrafficClass(); + fail(); + } catch (IOException ioe) { + //expected + } + } + + private String readShortString(InputStream theInput) throws IOException { + int totalBytesRead = 0; + byte[] myBytes = new byte[100]; + while (theInput.available() > 0) { + int bytesRead = theInput.read(myBytes, totalBytesRead, + myBytes.length - totalBytesRead); + totalBytesRead = totalBytesRead + bytesRead; + } + + return new String(myBytes, 0, totalBytesRead); + } + + public void test_setPerformancePreference_Int_Int_Int() throws Exception { + try (Socket theSocket = new Socket()) { + theSocket.setPerformancePreferences(1, 1, 1); + } } public void test_ConstructorLjava_net_Proxy_Exception() { @@ -2075,58 +1951,53 @@ public void test_connect_unresolved() throws Exception { public void test_getOutputStream_shutdownOutput() throws Exception { // regression test for Harmony-873 - ServerSocket ss = new ServerSocket(0); - Socket s = new Socket("127.0.0.1", ss.getLocalPort()); - ss.accept(); - s.shutdownOutput(); - try { - s.getOutputStream(); - fail("should throw SocketException"); - } catch (IOException e) { - // expected - } finally { - s.close(); - } - - SocketChannel channel = SocketChannel.open( - new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort())); - channel.configureBlocking(false); - ss.accept(); - Socket socket = channel.socket(); - - OutputStream out = null; + try (ServerSocket ss = new ServerSocket(0)) { + try (Socket s = new Socket("127.0.0.1", ss.getLocalPort())) { + ss.accept(); + s.shutdownOutput(); + try { + s.getOutputStream(); + fail("should throw SocketException"); + } catch (IOException e) { + // expected + } + } - try { - out = socket.getOutputStream(); - out.write(1); - fail("IllegalBlockingModeException was not thrown."); - } catch(IllegalBlockingModeException ibme) { - //expected - } finally { - if(out != null) out.close(); - socket.close(); - channel.close(); + SocketChannel channel = SocketChannel.open( + new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort())); + channel.configureBlocking(false); + ss.accept(); + try (Socket socket = channel.socket(); + OutputStream out = socket.getOutputStream()) { + try { + out.write(1); + fail("IllegalBlockingModeException was not thrown."); + } catch (IllegalBlockingModeException ibme) { + //expected + } + } } } public void test_shutdownInputOutput_twice() throws Exception { // regression test for Harmony-2944 - Socket s = new Socket("0.0.0.0", 0, false); - s.shutdownInput(); - - try { + try (Socket s = new Socket("0.0.0.0", 0, false)) { s.shutdownInput(); - fail("should throw SocketException"); - } catch (SocketException se) { - // expected - } - s.shutdownOutput(); - try { + try { + s.shutdownInput(); + fail("should throw SocketException"); + } catch (SocketException se) { + // expected + } s.shutdownOutput(); - fail("should throw SocketException"); - } catch (SocketException se) { - // expected + + try { + s.shutdownOutput(); + fail("should throw SocketException"); + } catch (SocketException se) { + // expected + } } } diff --git a/luni/src/test/java/libcore/java/net/OldSocketTestCase.java b/luni/src/test/java/libcore/java/net/OldSocketTestCase.java index bcb6af9e4..5e83abce7 100644 --- a/luni/src/test/java/libcore/java/net/OldSocketTestCase.java +++ b/luni/src/test/java/libcore/java/net/OldSocketTestCase.java @@ -17,9 +17,14 @@ package libcore.java.net; -import junit.framework.TestCase; - -public abstract class OldSocketTestCase extends TestCase { +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public abstract class OldSocketTestCase extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); public static final int SO_MULTICAST = 0; diff --git a/luni/src/test/java/libcore/java/net/OldURLClassLoaderTest.java b/luni/src/test/java/libcore/java/net/OldURLClassLoaderTest.java index 2aea4efa9..2f1221def 100644 --- a/luni/src/test/java/libcore/java/net/OldURLClassLoaderTest.java +++ b/luni/src/test/java/libcore/java/net/OldURLClassLoaderTest.java @@ -134,6 +134,17 @@ public void test_addURLLjava_net_URL() throws MalformedURLException { } } + // JDK-8057936 + public void testFindClass() { + TestURLClassLoader tucl = new TestURLClassLoader(new URL[0]); + + // Should throw ClassNotFoundException instead of NPE. + try { + tucl.findClass("foobar"); + fail(); + } catch (ClassNotFoundException expected) { } + } + public void test_definePackage() throws MalformedURLException { Manifest manifest = new Manifest(); URL[] u = new URL[0]; diff --git a/luni/src/test/java/libcore/java/net/OldUnixSocketTest.java b/luni/src/test/java/libcore/java/net/OldUnixSocketTest.java index 6dfed52e7..89b46a0f4 100644 --- a/luni/src/test/java/libcore/java/net/OldUnixSocketTest.java +++ b/luni/src/test/java/libcore/java/net/OldUnixSocketTest.java @@ -26,9 +26,14 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; -import junit.framework.TestCase; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public class OldUnixSocketTest extends TestCase { +public class OldUnixSocketTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); public void test_getInputStream() throws IOException { // Simple read/write test over the IO streams diff --git a/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java b/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java index d78456d7c..9515fcc65 100644 --- a/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java +++ b/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java @@ -1,11 +1,17 @@ -package java.net; +package libcore.java.net; import junit.framework.TestCase; import java.io.IOException; +import java.net.Socket; +import java.net.SocketImpl; +import java.net.SocketException; +import java.net.SocketAddress; +import java.net.ServerSocket; import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; /** * Tests for race conditions between {@link ServerSocket#close()} and @@ -34,23 +40,40 @@ public void implAcceptExposedForTest(Socket socket) throws IOException { } final ExposedServerSocket serverSocket = new ExposedServerSocket(); serverSocket.close(); - try { - // Hack: Need to subclass to access the protected constructor without reflection - Socket socket = new Socket((SocketImpl) null) { }; - serverSocket.implAcceptExposedForTest(socket); - fail("accepting on a closed socket should throw"); - } catch (SocketException expected) { - // expected - } catch (IOException e) { - throw new AssertionError(e); + // implAccept() on background thread to prevent this test hanging + final AtomicReference failure = new AtomicReference<>(); + final CountDownLatch threadFinishedLatch = new CountDownLatch(1); + Thread thread = new Thread("implAccept() closed ServerSocket") { + public void run() { + try { + // Hack: Need to subclass to access the protected constructor without reflection + Socket socket = new Socket((SocketImpl) null) { }; + serverSocket.implAcceptExposedForTest(socket); + } catch (SocketException expected) { + // pass + } catch (IOException|RuntimeException e) { + failure.set(e); + } finally { + threadFinishedLatch.countDown(); + } + } + }; + thread.start(); + + boolean completed = threadFinishedLatch.await(5, TimeUnit.SECONDS); + assertTrue("implAccept didn't throw or return within time limit", completed); + Exception e = failure.get(); + if (e != null) { + throw new AssertionError("Unexpected exception", e); } + thread.join(); } /** * Test for b/27763633. */ public void testConcurrentServerSocketCloseReliablyThrows() { - int numIterations = 200; + int numIterations = 100; for (int i = 0; i < numIterations; i++) { checkConnectIterationAndCloseSocket("Iteration " + (i+1) + " of " + numIterations, /* msecPerIteration */ 50); @@ -73,16 +96,17 @@ private void checkConnectIterationAndCloseSocket(String iterationName, int msecP fail("Abort: " + e); throw new AssertionError("unreachable"); } - final CountDownLatch shutdownLatch = new CountDownLatch(1); - ServerRunnable serverRunnable = new ServerRunnable(serverSocket, shutdownLatch); + ServerRunnable serverRunnable = new ServerRunnable(serverSocket); Thread serverThread = new Thread(serverRunnable, TAG + " (server)"); ClientRunnable clientRunnable = new ClientRunnable( - serverSocket.getLocalSocketAddress(), shutdownLatch); + serverSocket.getLocalSocketAddress(), serverRunnable); Thread clientThread = new Thread(clientRunnable, TAG + " (client)"); serverThread.start(); clientThread.start(); try { - if (shutdownLatch.getCount() == 0) { + assertTrue("Slow server startup", serverRunnable.awaitStart(1, TimeUnit.SECONDS)); + assertTrue("Slow client startup", clientRunnable.awaitStart(1, TimeUnit.SECONDS)); + if (serverRunnable.isShutdown()) { fail("Server prematurely shut down"); } // Let server and client keep connecting for some time, then close the socket. @@ -94,9 +118,8 @@ private void checkConnectIterationAndCloseSocket(String iterationName, int msecP } // Check that the server shut down quickly in response to the socket closing. long hardLimitSeconds = 5; - boolean serverShutdownReached = shutdownLatch.await(hardLimitSeconds, TimeUnit.SECONDS); + boolean serverShutdownReached = serverRunnable.awaitShutdown(hardLimitSeconds, TimeUnit.SECONDS); if (!serverShutdownReached) { // b/27763633 - shutdownLatch.countDown(); String serverStackTrace = stackTraceAsString(serverThread.getStackTrace()); fail("Server took > " + hardLimitSeconds + "sec to react to serverSocket.close(). " + "Server thread's stackTrace: " + serverStackTrace); @@ -111,7 +134,7 @@ private void checkConnectIterationAndCloseSocket(String iterationName, int msecP iterationName, msecPerIteration), serverRunnable.numSuccessfulConnections > 0); - assertEquals(0, shutdownLatch.getCount()); + assertTrue(serverRunnable.isShutdown()); // Sanity check to ensure the threads don't live into the next iteration. This should // be quick because we only get here if shutdownLatch reached 0 within the time limit. serverThread.join(); @@ -128,17 +151,20 @@ private void checkConnectIterationAndCloseSocket(String iterationName, int msecP */ static class ClientRunnable implements Runnable { private final SocketAddress socketAddress; - private final CountDownLatch shutdownLatch; + + private final ServerRunnable serverRunnable; + private final CountDownLatch startLatch = new CountDownLatch(1); public ClientRunnable( - SocketAddress socketAddress, CountDownLatch shutdownLatch) { + SocketAddress socketAddress, ServerRunnable serverRunnable) { this.socketAddress = socketAddress; - this.shutdownLatch = shutdownLatch; + this.serverRunnable = serverRunnable; } @Override public void run() { - while (shutdownLatch.getCount() != 0) { // check if server is shutting down + startLatch.countDown(); + while (!serverRunnable.isShutdown()) { try { Socket socket = new Socket(); socket.connect(socketAddress, /* timeout (msec) */ 10); @@ -148,6 +174,11 @@ public void run() { } } } + + public boolean awaitStart(long timeout, TimeUnit timeUnit) throws InterruptedException { + return startLatch.await(timeout, timeUnit); + } + } /** @@ -157,15 +188,16 @@ public void run() { static class ServerRunnable implements Runnable { private final ServerSocket serverSocket; volatile int numSuccessfulConnections; - private final CountDownLatch shutdownLatch; + private final CountDownLatch startLatch = new CountDownLatch(1); + private final CountDownLatch shutdownLatch = new CountDownLatch(1); - ServerRunnable(ServerSocket serverSocket, CountDownLatch shutdownLatch) { + ServerRunnable(ServerSocket serverSocket) { this.serverSocket = serverSocket; - this.shutdownLatch = shutdownLatch; } @Override public void run() { + startLatch.countDown(); int numSuccessfulConnections = 0; while (true) { try { @@ -181,6 +213,18 @@ public void run() { } } } + + public boolean awaitStart(long timeout, TimeUnit timeUnit) throws InterruptedException { + return startLatch.await(timeout, timeUnit); + } + + public boolean awaitShutdown(long timeout, TimeUnit timeUnit) throws InterruptedException { + return shutdownLatch.await(timeout, timeUnit); + } + + public boolean isShutdown() { + return shutdownLatch.getCount() == 0; + } } private static String stackTraceAsString(StackTraceElement[] stackTraceElements) { diff --git a/luni/src/test/java/libcore/java/net/ServerSocketTest.java b/luni/src/test/java/libcore/java/net/ServerSocketTest.java index d82e934f9..c368a2220 100644 --- a/luni/src/test/java/libcore/java/net/ServerSocketTest.java +++ b/luni/src/test/java/libcore/java/net/ServerSocketTest.java @@ -21,29 +21,37 @@ import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public class ServerSocketTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); -public class ServerSocketTest extends junit.framework.TestCase { public void testTimeoutAfterAccept() throws Exception { - final ServerSocket ss = new ServerSocket(0); - ss.setReuseAddress(true); - // On Unix, the receive timeout is inherited by the result of accept(2). - // Java specifies that it should always be 0 instead. - ss.setSoTimeout(1234); - final Socket[] result = new Socket[1]; - Thread t = new Thread(new Runnable() { - public void run() { - try { - result[0] = ss.accept(); - } catch (IOException ex) { - ex.printStackTrace(); - fail(); + try (ServerSocket ss = new ServerSocket(0)) { + ss.setReuseAddress(true); + // On Unix, the receive timeout is inherited by the result of accept(2). + // Java specifies that it should always be 0 instead. + ss.setSoTimeout(1234); + final Socket[] result = new Socket[1]; + Thread t = new Thread(new Runnable() { + public void run() { + try { + result[0] = ss.accept(); + } catch (IOException ex) { + ex.printStackTrace(); + fail(); + } } - } - }); - t.start(); - new Socket(ss.getInetAddress(), ss.getLocalPort()); - t.join(); - assertEquals(0, result[0].getSoTimeout()); + }); + t.start(); + new Socket(ss.getInetAddress(), ss.getLocalPort()).close(); + t.join(); + assertEquals(0, result[0].getSoTimeout()); + } } public void testInitialState() throws Exception { diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java index 52638d458..50d8ce6f8 100644 --- a/luni/src/test/java/libcore/java/net/SocketTest.java +++ b/luni/src/test/java/libcore/java/net/SocketTest.java @@ -16,11 +16,13 @@ package libcore.java.net; +import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ConnectException; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Proxy; @@ -34,6 +36,7 @@ import java.net.UnknownHostException; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; @@ -41,10 +44,22 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.io.FileDescriptor; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + + +public class SocketTest extends TestCaseWithRules { + @Rule + public TestRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule(); + + // This hostname is required to resolve to 127.0.0.1 and ::1 for all tests to pass. + private static final String ALL_LOOPBACK_HOSTNAME = "loopback46.unittest.grpc.io"; + // From net/inet_ecn.h + private static final int INET_ECN_MASK = 0x3; -public class SocketTest extends junit.framework.TestCase { // See http://b/2980559. public void test_close() throws Exception { Socket s = new Socket(); @@ -242,9 +257,15 @@ public MySocket(SocketImpl impl) throws SocketException { } public void test_setTrafficClass() throws Exception { - Socket s = new Socket(); - s.setTrafficClass(123); - assertEquals(123, s.getTrafficClass()); + try (Socket s = new Socket()) { + for (int i = 0; i <= 255; ++i) { + s.setTrafficClass(i); + + // b/30909505 + // Linux does not set ECN bits for STREAM sockets, so these bits should be zero. + assertEquals(i & ~INET_ECN_MASK, s.getTrafficClass()); + } + } } public void testReadAfterClose() throws Exception { @@ -364,33 +385,43 @@ public void testStateAfterClose() throws Exception { public void testCloseDuringConnect() throws Exception { final CountDownLatch signal = new CountDownLatch(1); - final Socket s = new Socket(); - new Thread() { - @Override - public void run() { - try { - // This address is reserved for documentation: should never be reachable. - InetSocketAddress unreachableIp = new InetSocketAddress("192.0.2.0", 80); - // This should never return. - s.connect(unreachableIp, 0 /* infinite */); - fail("Connect returned unexpectedly for: " + unreachableIp); - } catch (SocketException expected) { - assertTrue(expected.getMessage().contains("Socket closed")); - signal.countDown(); - } catch (IOException e) { - fail("Unexpected exception: " + e); - } + + // Executes a connect() that should block. + Callable connectWorker = () -> { + try { + // This address is reserved for documentation: should never be reachable. + InetSocketAddress unreachableIp = new InetSocketAddress("192.0.2.0", 80); + // This should never return. + s.connect(unreachableIp, 0 /* infinite */); + return "Connect returned unexpectedly for: " + unreachableIp; + } catch (SocketException expected) { + signal.countDown(); + return expected.getMessage().contains("Socket closed") + ? null + : "Unexpected SocketException message: " + expected.getMessage(); + } catch (IOException e) { + return "Unexpected exception: " + e; } - }.start(); + }; + Future connectResult = + Executors.newSingleThreadScheduledExecutor().submit(connectWorker); - // Wait for the connect() thread to run and start connect() + // Wait sufficient time for the connectWorker thread to run and start connect(). Thread.sleep(2000); + // Close the socket that connectWorker should currently be blocked in connect(). s.close(); + // connectWorker should have been unblocked so await() should return true. boolean connectUnblocked = signal.await(2000, TimeUnit.MILLISECONDS); - assertTrue(connectUnblocked); + + // connectWorker should have returned null if everything went as expected. + String workerFailure = connectResult.get(2000, TimeUnit.MILLISECONDS); + + assertTrue("connectUnblocked=[" + connectUnblocked + + "], workerFailure=[" + workerFailure + "]", + connectUnblocked && workerFailure == null); } // http://b/29092095 @@ -411,11 +442,9 @@ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { }); ServerSocket server = new ServerSocket(0); - - // We shouldn't ask the proxy selector to select() a proxy for us during - // connect(). Socket client = new Socket(InetAddress.getLocalHost(), server.getLocalPort()); client.close(); + server.close(); } finally { ProxySelector.setDefault(ps); } @@ -525,21 +554,74 @@ public void close() { // Test all Socket ctors try { new SocketThatFailOnClose("localhost", 1); + fail(); } catch(IOException expected) {} try { new SocketThatFailOnClose(InetAddress.getLocalHost(), 1); + fail(); } catch(IOException expected) {} try { new SocketThatFailOnClose("localhost", 1, null, 0); + fail(); } catch(IOException expected) {} try { new SocketThatFailOnClose(InetAddress.getLocalHost(), 1, null, 0); + fail(); } catch(IOException expected) {} try { new SocketThatFailOnClose("localhost", 1, true); + fail(); } catch(IOException expected) {} try { new SocketThatFailOnClose(InetAddress.getLocalHost(), 1, true); + fail(); } catch(IOException expected) {} } + + // b/30007735 + public void testSocketTestAllAddresses() throws Exception { + // Socket Ctor should try all sockets. + // + // This test creates a server socket bound to 127.0.0.1 or ::1 only, and connects using a + // hostname that resolves to both addresses. We should be able to connect to the server + // socket in either setup. + final String loopbackHost = ALL_LOOPBACK_HOSTNAME; + + assertTrue("Loopback DNS record is unreachable or is invalid.", checkLoopbackHost( + loopbackHost)); + + final int port = 9999; + for (InetAddress addr : new InetAddress[]{ Inet4Address.LOOPBACK, Inet6Address.LOOPBACK }) { + try (ServerSocket ss = new ServerSocket(port, 0, addr)) { + new Thread(() -> { + try { + ss.accept(); + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + + assertTrue(canConnect(loopbackHost, port)); + } + } + } + + /** Confirm the supplied hostname maps to only loopback addresses. */ + private static boolean checkLoopbackHost(String host) { + try { + List addrs = Arrays.asList(InetAddress.getAllByName(host)); + return addrs.stream().allMatch(InetAddress::isLoopbackAddress) && + addrs.contains(Inet4Address.LOOPBACK) && addrs.contains(Inet6Address.LOOPBACK); + } catch (UnknownHostException e) { + return false; + } + } + + private static boolean canConnect(String host, int port) { + try(Socket sock = new Socket(host, port)) { + return sock.isConnected(); + } catch (IOException e) { + return false; + } + } } diff --git a/luni/src/test/java/libcore/java/net/SocketTimeoutTest.java b/luni/src/test/java/libcore/java/net/SocketTimeoutTest.java new file mode 100644 index 000000000..78ee3ab8d --- /dev/null +++ b/luni/src/test/java/libcore/java/net/SocketTimeoutTest.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017 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. + */ + +package libcore.java.net; + +import org.junit.Test; + +import java.io.Closeable; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.nio.channels.ServerSocketChannel; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import libcore.util.EmptyArray; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Tests socket timeout behavior for various different socket types. + */ +public class SocketTimeoutTest { + + private static final int TIMEOUT_MILLIS = 500; + + private static final InetSocketAddress UNREACHABLE_ADDRESS + = new InetSocketAddress("192.0.2.0", 0); // RFC 5737 + + @FunctionalInterface + private interface SocketOperation { + void operate(T s) throws IOException; + } + + @FunctionalInterface + private interface SocketConstructor { + T get() throws IOException; + } + + private static void checkOperationTimesOut(SocketConstructor construct, + SocketOperation op) throws Exception { + try (T socket = construct.get()) { + long startingTime = System.currentTimeMillis(); + try { + op.operate(socket); + fail(); + } catch (SocketTimeoutException timeoutException) { + long timeElapsed = System.currentTimeMillis() - startingTime; + assertTrue( + Math.abs(((float) timeElapsed / TIMEOUT_MILLIS) - 1) + < 0.2f); // Allow some error. + } + } + } + + @Test + public void testSocketConnectTimeout() throws Exception { + // #connect(SocketAddress endpoint, int timeout) + checkOperationTimesOut(() -> new Socket(), s -> s.connect(UNREACHABLE_ADDRESS, + TIMEOUT_MILLIS)); + + // Setting SO_TIMEOUT should not affect connect timeout. + checkOperationTimesOut(() -> new Socket(), + s -> { + s.setSoTimeout(TIMEOUT_MILLIS / 2); + s.connect(UNREACHABLE_ADDRESS, TIMEOUT_MILLIS); + }); + } + + @Test + public void testSocketReadTimeout() throws Exception { + // #read() + try (ServerSocket ss = new ServerSocket(0)) { + // The server socket will accept the connection without explicitly calling accept() due + // to TCP backlog. + + checkOperationTimesOut(() -> new Socket(), s -> { + s.connect(ss.getLocalSocketAddress()); + s.setSoTimeout(TIMEOUT_MILLIS); + s.getInputStream().read(); + }); + } + } + + @Test + public void testSocketWriteNeverTimeouts() throws Exception { + // #write() should block if the buffers are full, and does not drop packets or throw + // SocketTimeoutException. + try (Socket sock = new Socket(); + ServerSocket serverSocket = new ServerSocket(0)) { + // Setting this option should not affect behaviour, as specified by the spec. + sock.setSoTimeout(TIMEOUT_MILLIS); + + // Set SO_SNDBUF and SO_RCVBUF to minimum value allowed by kernel. + sock.setSendBufferSize(1); + serverSocket.setReceiveBufferSize(1); + int actualSize = sock.getSendBufferSize() + serverSocket.getReceiveBufferSize(); + + sock.connect(serverSocket.getLocalSocketAddress()); + + CountDownLatch threadStarted = new CountDownLatch(1); + CountDownLatch writeCompleted = new CountDownLatch(1); + Thread thread = new Thread(() -> { + threadStarted.countDown(); + try { + // Should block + sock.getOutputStream().write(new byte[actualSize + 1]); + writeCompleted.countDown(); + } catch (IOException ignored) { + } finally { + writeCompleted.countDown(); + } + }); + + thread.start(); + + // Wait for the thread to start. + assertTrue(threadStarted.await(500, TimeUnit.MILLISECONDS)); + + // Wait for TIMEOUT_MILLIS + slop. If write does not complete by then, we assume it has + // blocked. + boolean blocked = + !writeCompleted.await(TIMEOUT_MILLIS * 2, TimeUnit.MILLISECONDS); + assertTrue(blocked); + + // Make sure the writing thread completes after the socket is closed. + sock.close(); + assertTrue(writeCompleted.await(5000, TimeUnit.MILLISECONDS)); + } + } + + @Test + public void testServerSocketAcceptTimeout() throws Exception { + // #accept() + checkOperationTimesOut(() -> new ServerSocket(0), + s -> { + s.setSoTimeout(TIMEOUT_MILLIS); + s.accept(); + }); + } + + @Test + public void testServerSocketChannelAcceptTimeout() throws Exception { + // #accept() + checkOperationTimesOut(() -> ServerSocketChannel.open(), + s -> { + s.bind(null, 0); + s.socket().setSoTimeout(TIMEOUT_MILLIS); + s.socket().accept(); + }); + } + + @Test + public void testDatagramSocketReceive() throws Exception { + checkOperationTimesOut(() -> new DatagramSocket(), s -> { + s.setSoTimeout(TIMEOUT_MILLIS); + s.receive(new DatagramPacket(EmptyArray.BYTE, 0)); + }); + } + + // TODO(yikong), http://b/35867657: + // Add tests for SocksSocketImpl once a mock Socks server is implemented. +} diff --git a/luni/src/test/java/libcore/java/net/URITest.java b/luni/src/test/java/libcore/java/net/URITest.java index 2c4a06a9d..67504d4aa 100644 --- a/luni/src/test/java/libcore/java/net/URITest.java +++ b/luni/src/test/java/libcore/java/net/URITest.java @@ -729,13 +729,26 @@ public void testUnderscore() throws Exception { assertEquals("a_b.c.d.net", uri.getHost()); } - // RFC1034#section-3.5 doesn't permit empty labels in hostnames, but we - // accepted this prior to N and the behavior is used by some apps. We need - // to keep the behavior for now for compatibility. + // RFC1034#section-3.5 doesn't permit empty labels in hostnames. This was accepted prior to N, + // but returns null in later releases. // http://b/25991669 + // http://b/29560247 public void testHostWithEmptyLabel() throws Exception { - assertEquals(".example.com", new URI("http://.example.com/").getHost()); - assertEquals("example..com", new URI("http://example..com/").getHost()); + assertNull(new URI("http://.example.com/").getHost()); + assertNull(new URI("http://example..com/").getHost()); + } + + public void test_JDK7171415() { + URI lower, mixed; + lower = URI.create("http://www.example.com/%2b"); + mixed = URI.create("http://wWw.ExAmPlE.com/%2B"); + assertTrue(lower.equals(mixed)); + assertEquals(lower.hashCode(), mixed.hashCode()); + + lower = URI.create("http://www.example.com/%2bbb"); + mixed = URI.create("http://wWw.ExAmPlE.com/%2BbB"); + assertFalse(lower.equals(mixed)); + assertFalse(lower.hashCode() == mixed.hashCode()); } // Adding a new test? Consider adding an equivalent test to URLTest.java diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java index fc5283b48..750e73a56 100644 --- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java +++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java @@ -16,26 +16,31 @@ package libcore.java.net; -import com.android.okhttp.AndroidShimResponseCache; - import com.google.mockwebserver.Dispatcher; import com.google.mockwebserver.MockResponse; import com.google.mockwebserver.MockWebServer; import com.google.mockwebserver.RecordedRequest; import com.google.mockwebserver.SocketPolicy; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; + +import com.android.okhttp.AndroidShimResponseCache; +import com.android.okhttp.internal.Platform; +import com.android.okhttp.internal.tls.TrustRootIndex; + +import junit.framework.TestCase; + import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Authenticator; import java.net.CacheRequest; import java.net.CacheResponse; +import java.net.CookieHandler; +import java.net.CookieManager; import java.net.HttpRetryException; import java.net.HttpURLConnection; -import java.net.Inet6Address; import java.net.InetAddress; import java.net.PasswordAuthentication; import java.net.ProtocolException; @@ -82,8 +87,8 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import libcore.java.security.TestKeyStore; -import libcore.java.util.AbstractResourceLeakageDetectorTestCase; import libcore.javax.net.ssl.TestSSLContext; + import tests.net.DelegatingSocketFactory; import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END; @@ -91,21 +96,21 @@ import static com.google.mockwebserver.SocketPolicy.FAIL_HANDSHAKE; import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END; import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.spy; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; -public final class URLConnectionTest extends AbstractResourceLeakageDetectorTestCase { +public final class URLConnectionTest extends TestCase { private MockWebServer server; private AndroidShimResponseCache cache; private String hostName; + private List testSSLContextsToClose; @Override protected void setUp() throws Exception { super.setUp(); server = new MockWebServer(); hostName = server.getHostName(); + testSSLContextsToClose = new ArrayList<>(); } @Override protected void tearDown() throws Exception { @@ -123,6 +128,9 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest cache.delete(); cache = null; } + for (TestSSLContext testSSLContext : testSSLContextsToClose) { + testSSLContext.close(); + } super.tearDown(); } @@ -534,7 +542,7 @@ public void testGetResponseCodeNoResponseBody() throws Exception { } public void testConnectViaHttps() throws IOException, InterruptedException { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); @@ -551,7 +559,7 @@ public void testConnectViaHttps() throws IOException, InterruptedException { } public void testConnectViaHttpsReusingConnections() throws IOException, InterruptedException { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); SSLSocketFactory clientSocketFactory = testSSLContext.clientContext.getSocketFactory(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); @@ -573,7 +581,7 @@ public void testConnectViaHttpsReusingConnections() throws IOException, Interrup public void testConnectViaHttpsReusingConnectionsDifferentFactories() throws IOException, InterruptedException { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); @@ -601,6 +609,7 @@ public void testConnectViaHttpsReusingConnectionsDifferentFactories() public void testConnectViaHttpsToUntrustedServer() throws IOException, InterruptedException { TestSSLContext testSSLContext = TestSSLContext.create(TestKeyStore.getClientCA2(), TestKeyStore.getServer()); + testSSLContextsToClose.add(testSSLContext); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); server.enqueue(new MockResponse()); // unused @@ -617,30 +626,50 @@ public void testConnectViaHttpsToUntrustedServer() throws IOException, Interrupt assertEquals(0, server.getRequestCount()); } + public void testConnectViaProxy_emptyPath() throws Exception { + // expected normalization http://android -> http://android/ per b/30107354 + checkConnectViaProxy( + ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY, "http://android.com", + "http://android.com/", "android.com"); + } + + public void testConnectViaProxy_complexUrlWithNoPath() throws Exception { + checkConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY, + "http://android.com:8080?height=100&width=42", + "http://android.com:8080/?height=100&width=42", + "android.com:8080"); + } + public void testConnectViaProxyUsingProxyArg() throws Exception { - testConnectViaProxy(ProxyConfig.CREATE_ARG); + checkConnectViaProxy(ProxyConfig.CREATE_ARG); } public void testConnectViaProxyUsingProxySystemProperty() throws Exception { - testConnectViaProxy(ProxyConfig.PROXY_SYSTEM_PROPERTY); + checkConnectViaProxy(ProxyConfig.PROXY_SYSTEM_PROPERTY); } public void testConnectViaProxyUsingHttpProxySystemProperty() throws Exception { - testConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); + checkConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); + } + + private void checkConnectViaProxy(ProxyConfig proxyConfig) throws Exception { + checkConnectViaProxy(proxyConfig, + "http://android.com/foo", "http://android.com/foo", "android.com"); } - private void testConnectViaProxy(ProxyConfig proxyConfig) throws Exception { + private void checkConnectViaProxy(ProxyConfig proxyConfig, String urlString, + String expectedUrlInRequestLine, String expectedHost) throws Exception { MockResponse mockResponse = new MockResponse().setBody("this response comes via a proxy"); server.enqueue(mockResponse); server.play(); - URL url = new URL("http://android.com/foo"); + URL url = new URL(urlString); HttpURLConnection connection = proxyConfig.connect(server, url); assertContent("this response comes via a proxy", connection); RecordedRequest request = server.takeRequest(); - assertEquals("GET http://android.com/foo HTTP/1.1", request.getRequestLine()); - assertContains(request.getHeaders(), "Host: android.com"); + assertEquals("GET " + expectedUrlInRequestLine + " HTTP/1.1", request.getRequestLine()); + assertContains(request.getHeaders(), "Host: " + expectedHost); } public void testContentDisagreesWithContentLengthHeader() throws IOException { @@ -679,7 +708,7 @@ public void testConnectViaHttpProxyToHttpsUsingHttpProxySystemProperty() throws } private void testConnectViaDirectProxyToHttps(ProxyConfig proxyConfig) throws Exception { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); @@ -717,7 +746,7 @@ public void testConnectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() throws * through a proxy. http://b/3097277 */ private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exception { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); @@ -737,7 +766,7 @@ private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exce RecordedRequest connect = server.takeRequest(); assertEquals("Connect line failure on proxy", "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); - assertContains(connect.getHeaders(), "Host: android.com"); + assertContains(connect.getHeaders(), "Host: android.com:443"); RecordedRequest get = server.takeRequest(); assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); @@ -750,7 +779,7 @@ private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exce * Tolerate bad https proxy response when using HttpResponseCache. http://b/6754912 */ public void testConnectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() throws Exception { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); initResponseCache(); @@ -776,7 +805,7 @@ public void testConnectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() th RecordedRequest connect = server.takeRequest(); assertEquals("CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); - assertContains(connect.getHeaders(), "Host: android.com"); + assertContains(connect.getHeaders(), "Host: android.com:443"); } private void initResponseCache() throws IOException { @@ -924,10 +953,14 @@ public void testEtagHeaders_cachedWithServerMiss() throws Exception { */ public void testProxyConnectIncludesProxyHeadersOnly() throws IOException, InterruptedException { + Authenticator.setDefault(new SimpleAuthenticator()); RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); + server.enqueue(new MockResponse() + .setResponseCode(407) + .addHeader("Proxy-Authenticate: Basic realm=\"localhost\"")); server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) .clearHeaders()); @@ -938,18 +971,34 @@ public void testProxyConnectIncludesProxyHeadersOnly() HttpsURLConnection connection = (HttpsURLConnection) url.openConnection( server.toProxyAddress()); connection.addRequestProperty("Private", "Secret"); - connection.addRequestProperty("Proxy-Authorization", "bar"); connection.addRequestProperty("User-Agent", "baz"); connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); connection.setHostnameVerifier(hostnameVerifier); assertContent("encrypted response from the origin server", connection); - RecordedRequest connect = server.takeRequest(); - assertContainsNoneMatching(connect.getHeaders(), "Private.*"); - assertContains(connect.getHeaders(), "Proxy-Authorization: bar"); - assertContains(connect.getHeaders(), "User-Agent: baz"); - assertContains(connect.getHeaders(), "Host: android.com"); - assertContains(connect.getHeaders(), "Proxy-Connection: Keep-Alive"); + // connect1 and connect2 are tunnel requests which potentially tunnel multiple requests; + // Thus we can't expect its headers to exactly match those of the wrapped request. + // See https://github.com/square/okhttp/commit/457fb428a729c50c562822571ea9b13e689648f3 + + { + RecordedRequest connect1 = server.takeRequest(); + List headers = connect1.getHeaders(); + assertContainsNoneMatching(headers, "Private.*"); + assertContainsNoneMatching(headers, "Proxy\\-Authorization.*"); + assertHeaderPresent(connect1, "User-Agent"); + assertContains(headers, "Host: android.com:443"); + assertContains(headers, "Proxy-Connection: Keep-Alive"); + } + + { + RecordedRequest connect2 = server.takeRequest(); + List headers = connect2.getHeaders(); + assertContainsNoneMatching(headers, "Private.*"); + assertHeaderPresent(connect2, "Proxy-Authorization"); + assertHeaderPresent(connect2, "User-Agent"); + assertContains(headers, "Host: android.com:443"); + assertContains(headers, "Proxy-Connection: Keep-Alive"); + } RecordedRequest get = server.takeRequest(); assertContains(get.getHeaders(), "Private: Secret"); @@ -958,7 +1007,7 @@ public void testProxyConnectIncludesProxyHeadersOnly() public void testProxyAuthenticateOnConnect() throws Exception { Authenticator.setDefault(new SimpleAuthenticator()); - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); server.enqueue(new MockResponse() .setResponseCode(407) @@ -993,7 +1042,7 @@ public void testProxyAuthenticateOnConnect() throws Exception { // Don't disconnect after building a tunnel with CONNECT // http://code.google.com/p/android/issues/detail?id=37221 public void testProxyWithConnectionClose() throws IOException { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) @@ -1031,6 +1080,67 @@ public void testDisconnectedConnection() throws IOException { } } + // http://b/33763156 + public void testDisconnectDuringConnect_getInputStream() throws IOException { + checkDisconnectDuringConnect(HttpURLConnection::getInputStream); + } + + // http://b/33763156 + public void testDisconnectDuringConnect_getOutputStream() throws IOException { + checkDisconnectDuringConnect(HttpURLConnection::getOutputStream); + } + + // http://b/33763156 + public void testDisconnectDuringConnect_getResponseCode() throws IOException { + checkDisconnectDuringConnect(HttpURLConnection::getResponseCode); + } + + // http://b/33763156 + public void testDisconnectDuringConnect_getResponseMessage() throws IOException { + checkDisconnectDuringConnect(HttpURLConnection::getResponseMessage); + } + + interface ConnectStrategy { + /** + * Causes the given {@code connection}, which was previously disconnected, + * to initiate the connection. + */ + void connect(HttpURLConnection connection) throws IOException; + } + + // http://b/33763156 + private void checkDisconnectDuringConnect(ConnectStrategy connectStrategy) throws IOException { + server.enqueue(new MockResponse().setBody("This should never be sent")); + server.play(); + + final AtomicReference connectionHolder = new AtomicReference<>(); + class DisconnectingCookieHandler extends CookieManager { + @Override + public Map> get(URI uri, Map> map) + throws IOException { + Map> result = super.get(uri, map); + connectionHolder.get().disconnect(); + return result; + } + } + CookieHandler defaultCookieHandler = CookieHandler.getDefault(); + try { + CookieHandler.setDefault(new DisconnectingCookieHandler()); + HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); + connectionHolder.set(connection); + try { + connectStrategy.connect(connection); + fail(); + } catch (IOException expected) { + assertEquals("Canceled", expected.getMessage()); + } finally { + connection.disconnect(); + } + } finally { + CookieHandler.setDefault(defaultCookieHandler); + } + } + public void testDisconnectBeforeConnect() throws IOException { server.enqueue(new MockResponse().setBody("A")); server.play(); @@ -1462,7 +1572,7 @@ public void testSecureChunkedStreaming() throws Exception { * http://code.google.com/p/android/issues/detail?id=12860 */ private void testSecureStreamingPost(StreamingMode streamingMode) throws Exception { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); server.enqueue(new MockResponse().setBody("Success!")); server.play(); @@ -1673,7 +1783,7 @@ private void testRedirected(TransferKind transferKind, boolean reuse) throws Exc } public void testRedirectedOnHttps() throws IOException, InterruptedException { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); server.enqueue(new MockResponse() .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) @@ -1695,7 +1805,7 @@ public void testRedirectedOnHttps() throws IOException, InterruptedException { } public void testNotRedirectedFromHttpsToHttp() throws IOException, InterruptedException { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); server.enqueue(new MockResponse() .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) @@ -1814,8 +1924,10 @@ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { // The first URI will be the initial request. We want to inspect the redirect. URI uri = proxySelectorUris.get(1); - // The HttpURLConnectionImpl converts %0 -> %250. i.e. it escapes the %. - assertEquals(redirectPath + "?foo=%250&bar=%00", uri.toString()); + // The proxy is selected by Address alone (not the whole target URI). + // In OkHttp, HttpEngine.createAddress() converts to an Address and the + // RouteSelector converts back to address.url(). + assertEquals(server2.getUrl("/").toString(), uri.toString()); } finally { ProxySelector.setDefault(originalSelector); server2.shutdown(); @@ -1932,7 +2044,7 @@ public void testHttpsWithCustomTrustManager() throws Exception { SSLSocketFactory defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); try { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); server.enqueue(new MockResponse().setBody("ABC")); server.enqueue(new MockResponse().setBody("DEF")); @@ -1983,22 +2095,15 @@ public void testConnectTimeouts() throws IOException { @Override protected Socket configureSocket(Socket socket) throws IOException { final int attemptNumber = socketCreationCount[0]++; - Answer socketConnectAnswer = new Answer() { - @Override public Object answer(InvocationOnMock invocation) - throws Throwable { - int timeoutArg = (int) invocation.getArguments()[1]; - socketConnectTimeouts[attemptNumber] = timeoutArg; - throw new SocketTimeoutException( - "Simulated timeout after " + timeoutArg); + Socket socketWrapper = new DelegatingSocket(socket) { + @Override + public void connect(SocketAddress endpoint, int timeout) + throws IOException { + socketConnectTimeouts[attemptNumber] = timeout; + throw new SocketTimeoutException("Simulated timeout after " + timeout); } }; - - Socket socketSpy = spy(socket); - // Create a partial mock that wraps the actual socket and intercepts the - // connect(SocketAddress, int) method. - doAnswer(socketConnectAnswer) - .when(socketSpy).connect(any(SocketAddress.class), anyInt()); - return socketSpy; + return socketWrapper; } }); @@ -2266,8 +2371,13 @@ public void testUrlCharacterMapping() throws Exception { testUrlToRequestMapping("$", "$", "$"); testUrlToUriMapping("&", "&", "&", "&", "&"); testUrlToRequestMapping("&", "&", "&"); - testUrlToUriMapping("'", "'", "'", "%27", "'"); - testUrlToRequestMapping("'", "'", "%27"); + + // http://b/30405333 - upstream OkHttp encodes single quote (') as %27 in query parameters + // but this breaks iTunes remote apps: iTunes currently does not accept %27 so we have a + // local patch to retain the historic Android behavior of not encoding single quote. + testUrlToUriMapping("'", "'", "'", "'", "'"); + testUrlToRequestMapping("'", "'", "'"); + testUrlToUriMapping("(", "(", "(", "(", "("); testUrlToRequestMapping("(", "(", "("); testUrlToUriMapping(")", ")", ")", ")", ")"); @@ -2668,6 +2778,14 @@ public void testInvalidIpv4Address() throws Exception { } } + public void testConnectIpv6() throws Exception { + server.enqueue(new MockResponse().setBody("testConnectIpv6 body")); + server.play(); + URL url = new URL("http://[::1]:" + server.getPort() + "/"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + assertContent("testConnectIpv6 body", connection); + } + // http://code.google.com/p/android/issues/detail?id=16895 public void testUrlWithSpaceInHost() throws Exception { URLConnection urlConnection = new URL("http://and roid.com/").openConnection(); @@ -2693,98 +2811,55 @@ public void testUrlWithSpaceInHostViaHttpProxy() throws Exception { } } - public void testSslFallback_allSupportedProtocols() throws Exception { - TestSSLContext testSSLContext = TestSSLContext.create(); - - String[] allSupportedProtocols = { "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" }; - SSLSocketFactory serverSocketFactory = - new LimitedProtocolsSocketFactory( - testSSLContext.serverContext.getSocketFactory(), - allSupportedProtocols); + /** Checks that if the first TLS handshake fails, no fallback is attempted. */ + private void checkNoFallbackOnFailedHandshake(SSLSocketFactory clientSocketFactory, + SSLSocketFactory serverSocketFactory, + String... expectedProtocols) + throws Exception { server.useHttps(serverSocketFactory, false); server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE)); - server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE)); - server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE)); server.enqueue(new MockResponse().setBody("This required fallbacks")); server.play(); HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // Keeps track of the client sockets created so that we can interrogate them. final boolean disableFallbackScsv = true; - FallbackTestClientSocketFactory clientSocketFactory = new FallbackTestClientSocketFactory( - new LimitedProtocolsSocketFactory( - testSSLContext.clientContext.getSocketFactory(), allSupportedProtocols), - disableFallbackScsv); - connection.setSSLSocketFactory(clientSocketFactory); - assertEquals("This required fallbacks", - readAscii(connection.getInputStream(), Integer.MAX_VALUE)); - - // Confirm the server accepted a single connection. - RecordedRequest retry = server.takeRequest(); - assertEquals(0, retry.getSequenceNumber()); - assertEquals("SSLv3", retry.getSslProtocol()); - - // Confirm the client fallback looks ok. - List createdSockets = clientSocketFactory.getCreatedSockets(); - assertEquals(4, createdSockets.size()); - TlsFallbackDisabledScsvSSLSocket clientSocket1 = - (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0); - assertSslSocket(clientSocket1, - false /* expectedWasFallbackScsvSet */, "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3"); - - TlsFallbackDisabledScsvSSLSocket clientSocket2 = - (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(1); - assertSslSocket(clientSocket2, - true /* expectedWasFallbackScsvSet */, "TLSv1.1", "TLSv1", "SSLv3"); - - TlsFallbackDisabledScsvSSLSocket clientSocket3 = - (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(2); - assertSslSocket(clientSocket3, true /* expectedWasFallbackScsvSet */, "TLSv1", "SSLv3"); - - TlsFallbackDisabledScsvSSLSocket clientSocket4 = - (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(3); - assertSslSocket(clientSocket4, true /* expectedWasFallbackScsvSet */, "SSLv3"); + FallbackTestClientSocketFactory fallbackTestClientSocketFactory = + new FallbackTestClientSocketFactory(clientSocketFactory, disableFallbackScsv); + connection.setSSLSocketFactory(fallbackTestClientSocketFactory); + try { + connection.getInputStream().read(); + fail(); + } catch (SSLHandshakeException expected) { + } + List createdSockets = fallbackTestClientSocketFactory.getCreatedSockets(); + assertEquals(1, createdSockets.size()); + assertSslSocket((TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0), + false /* expectedWasFallbackScsvSet */, expectedProtocols); } - public void testSslFallback_defaultProtocols() throws Exception { - TestSSLContext testSSLContext = TestSSLContext.create(); - - server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); - server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE)); - server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE)); - server.enqueue(new MockResponse().setBody("This required fallbacks")); - server.play(); - - HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); - // Keeps track of the client sockets created so that we can interrogate them. - final boolean disableFallbackScsv = true; - FallbackTestClientSocketFactory clientSocketFactory = new FallbackTestClientSocketFactory( - testSSLContext.clientContext.getSocketFactory(), - disableFallbackScsv); - connection.setSSLSocketFactory(clientSocketFactory); - assertEquals("This required fallbacks", - readAscii(connection.getInputStream(), Integer.MAX_VALUE)); - - // Confirm the server accepted a single connection. - RecordedRequest retry = server.takeRequest(); - assertEquals(0, retry.getSequenceNumber()); - assertEquals("TLSv1", retry.getSslProtocol()); - - // Confirm the client fallback looks ok. - List createdSockets = clientSocketFactory.getCreatedSockets(); - assertEquals(3, createdSockets.size()); - TlsFallbackDisabledScsvSSLSocket clientSocket1 = - (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0); - assertSslSocket(clientSocket1, - false /* expectedWasFallbackScsvSet */, "TLSv1.2", "TLSv1.1", "TLSv1"); + public void testNoSslFallback_specifiedProtocols() throws Exception { + String[] enabledProtocols = { "TLSv1.2", "TLSv1.1" }; + TestSSLContext testSSLContext = createDefaultTestSSLContext(); + SSLSocketFactory serverSocketFactory = + new LimitedProtocolsSocketFactory( + testSSLContext.serverContext.getSocketFactory(), + enabledProtocols); + SSLSocketFactory clientSocketFactory = new LimitedProtocolsSocketFactory( + testSSLContext.clientContext.getSocketFactory(), enabledProtocols); + checkNoFallbackOnFailedHandshake(clientSocketFactory, serverSocketFactory, + enabledProtocols); + } - TlsFallbackDisabledScsvSSLSocket clientSocket2 = - (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(1); - assertSslSocket(clientSocket2, true /* expectedWasFallbackScsvSet */, "TLSv1.1", "TLSv1"); + public void testNoSslFallback_defaultProtocols() throws Exception { + // Will need to be updated if the enabled protocols in Android's SSLSocketFactory change + String[] expectedEnabledProtocols = { "TLSv1.2", "TLSv1.1", "TLSv1" }; - TlsFallbackDisabledScsvSSLSocket clientSocket3 = - (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(2); - assertSslSocket(clientSocket3, true /* expectedWasFallbackScsvSet */, "TLSv1"); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); + SSLSocketFactory serverSocketFactory = testSSLContext.serverContext.getSocketFactory(); + SSLSocketFactory clientSocketFactory = testSSLContext.clientContext.getSocketFactory(); + checkNoFallbackOnFailedHandshake(clientSocketFactory, serverSocketFactory, + expectedEnabledProtocols); } private static void assertSslSocket(TlsFallbackDisabledScsvSSLSocket socket, @@ -2797,7 +2872,7 @@ private static void assertSslSocket(TlsFallbackDisabledScsvSSLSocket socket, } public void testInspectSslBeforeConnect() throws Exception { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); server.enqueue(new MockResponse()); server.play(); @@ -2832,7 +2907,7 @@ public void testInspectSslBeforeConnect() throws Exception { * http://code.google.com/p/android/issues/detail?id=24431 */ public void testInspectSslAfterConnect() throws Exception { - TestSSLContext testSSLContext = TestSSLContext.create(); + TestSSLContext testSSLContext = createDefaultTestSSLContext(); server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); server.enqueue(new MockResponse()); server.play(); @@ -2851,43 +2926,63 @@ public void testInspectSslAfterConnect() throws Exception { } } - // http://b/26769689 - public void testSSLSocketFactoryWithIpv6LiteralHostname() throws Exception { - TestSSLContext testSSLContext = TestSSLContext.create(); - server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); - server.enqueue(new MockResponse()); - server.play(); - - final AtomicReference hostNameUsed = new AtomicReference<>(null); - - SSLSocketFactory factory = new DelegatingSSLSocketFactory( - testSSLContext.clientContext.getSocketFactory()) { - @Override - public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose) - throws IOException { - hostNameUsed.set(host); - return (SSLSocket) delegate.createSocket(s, host, port, autoClose); - } - }; + /** + * Checks that OkHttp's certificate pinning logic is not used for the common case + * of HttpsUrlConnections. + * + *

OkHttp 2.7 introduced logic for Certificate Pinning. We deliberately don't + * expose any API surface that would interact with OkHttp's implementation because + * Android has its own API / implementation for certificate pinning. We can't + * easily test that there is *no* code path that would invoke OkHttp's certificate + * pinning logic, so this test only covers the *common* code path of a + * HttpsURLConnection as a sanity check. + * + *

To check whether OkHttp performs certificate pinning under the hood, this + * test disables two {@link Platform} methods. In OkHttp 2.7.5, these two methods + * are exclusively used in relation to certificate pinning. Android only provides + * the minimal implementation of these methods to get OkHttp's tests to pass, so + * they should never be invoked outside of OkHttp's tests. + */ + public void testTrustManagerAndTrustRootIndex_unusedForHttpsConnection() throws Exception { + Platform platform = Platform.getAndSetForTest(new PlatformWithoutTrustManager()); + try { + testConnectViaHttps(); + } finally { + Platform.getAndSetForTest(platform); + } + } - HttpsURLConnection urlConnection = (HttpsURLConnection) - new URL("https://[" + Inet6Address.getLoopbackAddress().getHostAddress() + "]:" - + server.getPort() + "/").openConnection(); - urlConnection.setSSLSocketFactory(factory); + /** + * Similar to {@link #testTrustManagerAndTrustRootIndex_unusedForHttpsConnection()}, + * but for the HTTP case. In the HTTP case, no certificate or trust management + * related logic should ever be involved at all, so some pretty basic things must + * be going wrong in order for this test to (unexpectedly) invoke the corresponding + * Platform methods. + */ + public void testTrustManagerAndTrustRootIndex_unusedForHttpConnection() throws Exception { + Platform platform = Platform.getAndSetForTest(new PlatformWithoutTrustManager()); try { - urlConnection.connect(); - fail(); - } catch (IOException expected) { - // We expect the connection to fail with a cert validation exception because we're - // using a literal address. + server.enqueue(new MockResponse().setBody("response").setResponseCode(200)); + server.play(); + HttpURLConnection urlConnection = + (HttpURLConnection) server.getUrl("/").openConnection(); + assertEquals(200, urlConnection.getResponseCode()); } finally { - urlConnection.disconnect(); + Platform.getAndSetForTest(platform); } + } - // Note that the square brackets around the literal address were crucial. Whatsapp - // wouldn't function properly without them. - assertEquals("[" + Inet6Address.getLoopbackAddress().getHostAddress() + "]", - hostNameUsed.get()); + /** + * A {@link Platform} that doesn't support two methods that, in OkHttp 2.7.5, + * are exclusively used to provide custom CertificatePinning. + */ + static class PlatformWithoutTrustManager extends Platform { + @Override public X509TrustManager trustManager(SSLSocketFactory sslSocketFactory) { + throw new AssertionError("Unexpected call"); + } + @Override public TrustRootIndex trustRootIndex(X509TrustManager trustManager) { + throw new AssertionError("Unexpected call"); + } } /** @@ -2916,6 +3011,11 @@ private void assertContent(String expected, URLConnection connection) throws IOE assertContent(expected, connection, Integer.MAX_VALUE); } + private static void assertHeaderPresent(RecordedRequest request, String headerName) { + assertNotNull(headerName + " missing: " + request.getHeaders(), + request.getHeader(headerName)); + } + private void assertContains(List list, String value) { assertTrue(list.toString(), list.contains(value)); } @@ -2932,6 +3032,12 @@ private Set newSet(String... elements) { return new HashSet(Arrays.asList(elements)); } + private TestSSLContext createDefaultTestSSLContext() { + TestSSLContext result = TestSSLContext.create(); + testSSLContextsToClose.add(result); + return result; + } + enum TransferKind { CHUNKED() { @Override void setBody(MockResponse response, byte[] content, int chunkSize) @@ -3189,6 +3295,64 @@ public SSLSocket createSocket(InetAddress address, int port, } } + /** + * A Socket that forwards all calls to public or protected methods, except for those + * that Socket inherits from Object, to a delegate. + */ + private static abstract class DelegatingSocket extends Socket { + private final Socket delegate; + + public DelegatingSocket(Socket delegate) { + if (delegate == null) { + throw new NullPointerException(); + } + this.delegate = delegate; + } + + @Override public void bind(SocketAddress bindpoint) throws IOException { delegate.bind(bindpoint); } + @Override public void close() throws IOException { delegate.close(); } + @Override public void connect(SocketAddress endpoint) throws IOException { delegate.connect(endpoint); } + @Override public void connect(SocketAddress endpoint, int timeout) throws IOException { delegate.connect(endpoint, timeout); } + @Override public SocketChannel getChannel() { return delegate.getChannel(); } + @Override public FileDescriptor getFileDescriptor$() { return delegate.getFileDescriptor$(); } + @Override public InetAddress getInetAddress() { return delegate.getInetAddress(); } + @Override public InputStream getInputStream() throws IOException { return delegate.getInputStream(); } + @Override public boolean getKeepAlive() throws SocketException { return delegate.getKeepAlive(); } + @Override public InetAddress getLocalAddress() { return delegate.getLocalAddress(); } + @Override public int getLocalPort() { return delegate.getLocalPort(); } + @Override public SocketAddress getLocalSocketAddress() { return delegate.getLocalSocketAddress(); } + @Override public boolean getOOBInline() throws SocketException { return delegate.getOOBInline(); } + @Override public OutputStream getOutputStream() throws IOException { return delegate.getOutputStream(); } + @Override public int getPort() { return delegate.getPort(); } + @Override public int getReceiveBufferSize() throws SocketException { return delegate.getReceiveBufferSize(); } + @Override public SocketAddress getRemoteSocketAddress() { return delegate.getRemoteSocketAddress(); } + @Override public boolean getReuseAddress() throws SocketException { return delegate.getReuseAddress(); } + @Override public int getSendBufferSize() throws SocketException { return delegate.getSendBufferSize(); } + @Override public int getSoLinger() throws SocketException { return delegate.getSoLinger(); } + @Override public int getSoTimeout() throws SocketException { return delegate.getSoTimeout(); } + @Override public boolean getTcpNoDelay() throws SocketException { return delegate.getTcpNoDelay(); } + @Override public int getTrafficClass() throws SocketException { return delegate.getTrafficClass(); } + @Override public boolean isBound() { return delegate.isBound(); } + @Override public boolean isClosed() { return delegate.isClosed(); } + @Override public boolean isConnected() { return delegate.isConnected(); } + @Override public boolean isInputShutdown() { return delegate.isInputShutdown(); } + @Override public boolean isOutputShutdown() { return delegate.isOutputShutdown(); } + @Override public void sendUrgentData(int data) throws IOException { delegate.sendUrgentData(data); } + @Override public void setKeepAlive(boolean on) throws SocketException { delegate.setKeepAlive(on); } + @Override public void setOOBInline(boolean on) throws SocketException { delegate.setOOBInline(on); } + @Override public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { delegate.setPerformancePreferences(connectionTime, latency, bandwidth); } + @Override public void setReceiveBufferSize(int size) throws SocketException { delegate.setReceiveBufferSize(size); } + @Override public void setReuseAddress(boolean on) throws SocketException { delegate.setReuseAddress(on); } + @Override public void setSendBufferSize(int size) throws SocketException { delegate.setSendBufferSize(size); } + @Override public void setSoLinger(boolean on, int linger) throws SocketException { delegate.setSoLinger(on, linger); } + @Override public void setSoTimeout(int timeout) throws SocketException { delegate.setSoTimeout(timeout); } + @Override public void setTcpNoDelay(boolean on) throws SocketException { delegate.setTcpNoDelay(on); } + @Override public void setTrafficClass(int tc) throws SocketException { delegate.setTrafficClass(tc); } + @Override public void shutdownInput() throws IOException { delegate.shutdownInput(); } + @Override public void shutdownOutput() throws IOException { delegate.shutdownOutput(); } + @Override public String toString() { return delegate.toString(); } + } + /** * An {@link javax.net.ssl.SSLSocket} that delegates all calls. */ diff --git a/luni/src/test/java/libcore/java/net/URLStreamHandlerFactoryTest.java b/luni/src/test/java/libcore/java/net/URLStreamHandlerFactoryTest.java index de50e164d..21c29710f 100644 --- a/luni/src/test/java/libcore/java/net/URLStreamHandlerFactoryTest.java +++ b/luni/src/test/java/libcore/java/net/URLStreamHandlerFactoryTest.java @@ -21,6 +21,8 @@ import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; +import junit.framework.Assert; +import junit.framework.AssertionFailedError; import junit.framework.TestCase; import libcore.java.net.customstreamhandler.http.Handler; @@ -61,13 +63,23 @@ public void testCreateURLStreamHandler() throws Exception { try { URL.setURLStreamHandlerFactory(shf); fail(); + } catch (AssertionFailedError error) { + // Rethrow the error thrown by fail to avoid it being caught by the more general catch + // statement below. + throw error; } catch (Error expected) { + // The setURLStreamHandlerFactory is behaving correctly by throwing an Error. } try { URL.setURLStreamHandlerFactory(null); fail(); + } catch (AssertionFailedError error) { + // Rethrow the error thrown by fail to avoid it being caught by the more general catch + // statement below. + throw error; } catch (Error expected) { + // The setURLStreamHandlerFactory is behaving correctly by throwing an Error. } } diff --git a/luni/src/test/java/libcore/java/net/URLTest.java b/luni/src/test/java/libcore/java/net/URLTest.java index 8ba14606e..3a14063e2 100644 --- a/luni/src/test/java/libcore/java/net/URLTest.java +++ b/luni/src/test/java/libcore/java/net/URLTest.java @@ -726,6 +726,11 @@ public void onNetwork() { fail("Blockguard.Policy.onNetwork"); } + @Override + public void onUnbufferedIO() { + fail("Blockguard.Policy.onUnbufferedIO"); + } + @Override public int getPolicyMask() { return 0; diff --git a/luni/src/test/java/libcore/java/nio/BufferTest.java b/luni/src/test/java/libcore/java/nio/BufferTest.java index 3ef549b94..c54d97890 100644 --- a/luni/src/test/java/libcore/java/nio/BufferTest.java +++ b/luni/src/test/java/libcore/java/nio/BufferTest.java @@ -955,7 +955,7 @@ public void testFreed() { ByteBuffer b2 = b1.duplicate(); NioUtils.freeDirectBuffer(b1); for (ByteBuffer b: new ByteBuffer[] { b1, b2 }) { - //assertFalse(b.isAccessible()); + assertFalse(b.isAccessible()); try { b.compact(); fail(); @@ -973,7 +973,6 @@ public void testFreed() { } } - /* setAccessible is not available in OpenJdk's buffers. public void testAccess() { ByteBuffer b1 = ByteBuffer.allocate(1); ByteBuffer b2 = b1.duplicate(); @@ -1057,7 +1056,7 @@ public void testAccess() { testAsMethods(b); testGetMethods(b); } - }*/ + } private void testPutMethods(ByteBuffer b) { b.position(0); @@ -1365,4 +1364,105 @@ private void testBuffersIndependentLimit(ByteBuffer b) { d.put(1, (double)1); b.limit(0); d.put(1, (double)1); } + + // http://b/32655865 + public void test_ByteBufferAsXBuffer_ByteOrder() { + ByteBuffer byteBuffer = ByteBuffer.allocate(10); + // Fill a ByteBuffer with different bytes that make it easy to tell byte ordering issues. + for (int i = 0; i < 10; i++) { + byteBuffer.put((byte)i); + } + byteBuffer.rewind(); + // Obtain a big-endian and little-endian copy of the source array. + ByteBuffer bigEndian = byteBuffer.duplicate().order(ByteOrder.BIG_ENDIAN); + ByteBuffer littleEndian = byteBuffer.duplicate().order(ByteOrder.LITTLE_ENDIAN); + + // Check each type longer than a byte to confirm the ordering differs. + // asXBuffer. + assertFalse(bigEndian.asShortBuffer().get() == littleEndian.asShortBuffer().get()); + assertFalse(bigEndian.asIntBuffer().get() == littleEndian.asIntBuffer().get()); + assertFalse(bigEndian.asLongBuffer().get() == littleEndian.asLongBuffer().get()); + assertFalse(bigEndian.asDoubleBuffer().get() == littleEndian.asDoubleBuffer().get()); + assertFalse(bigEndian.asCharBuffer().get() == littleEndian.asCharBuffer().get()); + assertFalse(bigEndian.asFloatBuffer().get() == littleEndian.asFloatBuffer().get()); + + // asXBuffer().asReadOnlyBuffer() + assertFalse(bigEndian.asShortBuffer().asReadOnlyBuffer().get() == + littleEndian.asShortBuffer().asReadOnlyBuffer().get()); + assertFalse(bigEndian.asIntBuffer().asReadOnlyBuffer().get() == + littleEndian.asIntBuffer().asReadOnlyBuffer().get()); + assertFalse(bigEndian.asLongBuffer().asReadOnlyBuffer().get() == + littleEndian.asLongBuffer().asReadOnlyBuffer().get()); + assertFalse(bigEndian.asDoubleBuffer().asReadOnlyBuffer().get() == + littleEndian.asDoubleBuffer().asReadOnlyBuffer().get()); + assertFalse(bigEndian.asCharBuffer().asReadOnlyBuffer().get() == + littleEndian.asCharBuffer().asReadOnlyBuffer().get()); + assertFalse(bigEndian.asFloatBuffer().asReadOnlyBuffer().get() == + littleEndian.asFloatBuffer().asReadOnlyBuffer().get()); + + // asXBuffer().duplicate() + assertFalse(bigEndian.asShortBuffer().duplicate().get() == + littleEndian.asShortBuffer().duplicate().get()); + assertFalse(bigEndian.asIntBuffer().duplicate().get() == + littleEndian.asIntBuffer().duplicate().get()); + assertFalse(bigEndian.asLongBuffer().duplicate().get() == + littleEndian.asLongBuffer().duplicate().get()); + assertFalse(bigEndian.asDoubleBuffer().duplicate().get() == + littleEndian.asDoubleBuffer().duplicate().get()); + assertFalse(bigEndian.asCharBuffer().duplicate().get() == + littleEndian.asCharBuffer().duplicate().get()); + assertFalse(bigEndian.asFloatBuffer().duplicate().get() == + littleEndian.asFloatBuffer().duplicate().get()); + + // asXBuffer().slice() + assertFalse(bigEndian.asShortBuffer().slice().get() == + littleEndian.asShortBuffer().slice().get()); + assertFalse(bigEndian.asIntBuffer().slice().get() == + littleEndian.asIntBuffer().slice().get()); + assertFalse(bigEndian.asLongBuffer().slice().get() == + littleEndian.asLongBuffer().slice().get()); + assertFalse(bigEndian.asDoubleBuffer().slice().get() == + littleEndian.asDoubleBuffer().slice().get()); + assertFalse(bigEndian.asCharBuffer().slice().get() == + littleEndian.asCharBuffer().slice().get()); + assertFalse(bigEndian.asFloatBuffer().slice().get() == + littleEndian.asFloatBuffer().slice().get()); + } + + // http://b/32655865 + public void test_ByteBufferAsXBuffer_ByteOrder_2() { + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(10); + byteBuffer.order(ByteOrder.BIG_ENDIAN); + // Fill a ByteBuffer with different bytes that make it easy to tell byte ordering issues. + for (int i = 0; i < 10; i++) { + byteBuffer.put((byte)i); + } + byteBuffer.rewind(); + + // Create BIG_ENDIAN views of the buffer. + ShortBuffer sb_be = byteBuffer.asShortBuffer(); + LongBuffer lb_be = byteBuffer.asLongBuffer(); + IntBuffer ib_be = byteBuffer.asIntBuffer(); + DoubleBuffer db_be = byteBuffer.asDoubleBuffer(); + CharBuffer cb_be = byteBuffer.asCharBuffer(); + FloatBuffer fb_be = byteBuffer.asFloatBuffer(); + + // Change the order of the underlying buffer. + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + + // Create LITTLE_ENDIAN views of the buffer. + ShortBuffer sb_le = byteBuffer.asShortBuffer(); + LongBuffer lb_le = byteBuffer.asLongBuffer(); + IntBuffer ib_le = byteBuffer.asIntBuffer(); + DoubleBuffer db_le = byteBuffer.asDoubleBuffer(); + CharBuffer cb_le = byteBuffer.asCharBuffer(); + FloatBuffer fb_le = byteBuffer.asFloatBuffer(); + + assertFalse(sb_be.get() == sb_le.get()); + assertFalse(lb_be.get() == lb_le.get()); + assertFalse(ib_be.get() == ib_le.get()); + assertFalse(db_be.get() == db_le.get()); + assertFalse(cb_be.get() == cb_le.get()); + assertFalse(fb_be.get() == fb_le.get()); + } } diff --git a/luni/src/test/java/libcore/java/nio/channels/AcceptPendingExceptionTest.java b/luni/src/test/java/libcore/java/nio/channels/AcceptPendingExceptionTest.java new file mode 100644 index 000000000..12567c405 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/AcceptPendingExceptionTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.channels; + +import junit.framework.TestCase; + +import java.nio.channels.AcceptPendingException; + +public class AcceptPendingExceptionTest extends TestCase { + + /** + * java.nio.channels.AcceptPendingException#AcceptPendingException() + */ + public void test_empty() { + AcceptPendingException e = new AcceptPendingException(); + assertTrue(e instanceof IllegalStateException); + assertNull(e.getMessage()); + assertNull(e.getLocalizedMessage()); + assertNull(e.getCause()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/AsynchronousChannelGroupTest.java b/luni/src/test/java/libcore/java/nio/channels/AsynchronousChannelGroupTest.java new file mode 100644 index 000000000..0ddf0bc78 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/AsynchronousChannelGroupTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.channels; + +import junit.framework.TestCase; + +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.ShutdownChannelGroupException; +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + + +public class AsynchronousChannelGroupTest extends TestCase { + + // Maximum time to wait in seconds for the termination. + private static final int TIMEOUT = 2; + + public void test_withFixedThreadPool() throws Exception { + AsynchronousChannelGroup acg = AsynchronousChannelGroup.withFixedThreadPool(1, + new TestThreadFactory()); + + assertEquals(AsynchronousChannelProvider.provider(), acg.provider()); + assertFalse(acg.isShutdown()); + assertFalse(acg.isTerminated()); + + // Close the channel. + acg.shutdownNow(); + } + + public void test_withFixedThreadPool_IllegalArgumentException() throws Exception { + try { + int invalidPoolSize = -1; + AsynchronousChannelGroup acg = AsynchronousChannelGroup.withFixedThreadPool( + invalidPoolSize, new TestThreadFactory()); + fail(); + } catch (IllegalArgumentException expected) {} + } + + public void test_withCachedThreadPool() throws Exception { + AsynchronousChannelGroup acg = AsynchronousChannelGroup.withCachedThreadPool( + Executors.newFixedThreadPool(5), 1); + + assertEquals(AsynchronousChannelProvider.provider(), acg.provider()); + assertFalse(acg.isShutdown()); + assertFalse(acg.isTerminated()); + + // Check with the negative initialSize value. It should not throw any error. + AsynchronousChannelGroup.withCachedThreadPool(Executors.newFixedThreadPool(5), -1); + + // Close the channel. + acg.shutdownNow(); + } + + public void test_withThreadPool() throws Exception { + AsynchronousChannelGroup acg = AsynchronousChannelGroup.withThreadPool( + Executors.newFixedThreadPool(5)); + + assertEquals(AsynchronousChannelProvider.provider(), acg.provider()); + assertFalse(acg.isShutdown()); + assertFalse(acg.isTerminated()); + + // Close the group. + acg.shutdownNow(); + } + + public void test_shutdown() throws Exception { + AsynchronousChannelGroup acg = AsynchronousChannelGroup.withCachedThreadPool( + Executors.newFixedThreadPool(5), 1); + + // Bind a channel to the group. + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(acg); + + // Shutdown channel group. + acg.shutdown(); + + assertTrue(acg.isShutdown()); + + // Bounded channel should still be open. + assertTrue(assc.isOpen()); + + // It should not be possible to bind a new channel. + try { + AsynchronousServerSocketChannel.open(acg); + fail(); + } catch (ShutdownChannelGroupException expected) {} + + // ExecutorService hasn't yet terminated as the channel is still open. + assertFalse(acg.awaitTermination(2, TimeUnit.SECONDS)); + + // Check invoking shutdown twice. + acg.shutdown(); + + // Close the group and the associated channel. + acg.shutdownNow(); + } + + public void test_shutdownNow() throws Exception { + AsynchronousChannelGroup acg = AsynchronousChannelGroup.withCachedThreadPool( + Executors.newFixedThreadPool(5), 1); + + // Bind a channel to the group. + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(acg); + + // Close the group as well as the channel bounded to it. + acg.shutdownNow(); + + assertTrue(acg.isShutdown()); + assertFalse(assc.isOpen()); + + // It should not be possible to bind a new channel. + try { + AsynchronousServerSocketChannel.open(acg); + fail(); + } catch (ShutdownChannelGroupException expected) {} + + acg.shutdownNow(); + } + + public void test_isTerminated() throws Exception { + ExecutorService executorService = Executors.newFixedThreadPool(5); + AsynchronousChannelGroup acg = AsynchronousChannelGroup.withCachedThreadPool( + executorService, 1); + + // Bind a channel to the group. + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(acg); + + assertFalse(acg.isTerminated()); + + acg.shutdownNow(); + + // Wait for the termination. + assertTrue(acg.awaitTermination(TIMEOUT, TimeUnit.SECONDS)); + assertTrue(acg.isTerminated()); + assertTrue(executorService.isTerminated()); + } + + public void test_awaitTermination() throws Exception { + AsynchronousChannelGroup acg = AsynchronousChannelGroup.withCachedThreadPool( + Executors.newFixedThreadPool(5), 1); + + // Bind a channel to the group. + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(acg); + + // Timeout as the group hasn't yet shutdown. + assertFalse(acg.awaitTermination(TIMEOUT, TimeUnit.SECONDS)); + + acg.shutdownNow(); + + assertTrue(acg.awaitTermination(TIMEOUT, TimeUnit.SECONDS)); + assertTrue(acg.isTerminated()); + } + + private static class TestThreadFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(false); + return t; + } + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/AsynchronousFileChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/AsynchronousFileChannelTest.java new file mode 100644 index 000000000..3b072f2fe --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/AsynchronousFileChannelTest.java @@ -0,0 +1,704 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.channels; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.CompletionHandler; +import java.nio.channels.NonReadableChannelException; +import java.nio.channels.NonWritableChannelException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.*; + +@RunWith(JUnit4.class) +public class AsynchronousFileChannelTest { + + @Test + public void testOpen_create() throws Throwable { + Path tempDir = Files.createTempDirectory("ASFCTest_test_open_create"); + + Path newFile = tempDir.resolve("newFile"); + AsynchronousFileChannel channel = AsynchronousFileChannel.open(newFile, + StandardOpenOption.CREATE, StandardOpenOption.WRITE); + assertTrue(channel.isOpen()); + assertEquals(0, channel.size()); + channel.close(); + } + + @Test + public void testOpen_existing() throws Throwable { + // Demonstrates a few warts related to re-opening files that already exist. + Path tempDir = Files.createTempDirectory("ASFCTest_test_open_existing"); + Path newFile = tempDir.resolve("newFile"); + + // Create a new file. + AsynchronousFileChannel channel = AsynchronousFileChannel.open(newFile, + StandardOpenOption.CREATE, StandardOpenOption.WRITE); + channel.close(); + + // This should fail, but it doesn't.. + AsynchronousFileChannel.open(newFile, StandardOpenOption.CREATE_NEW); + // ..unless it's paired with a write. + try { + AsynchronousFileChannel.open(newFile, StandardOpenOption.CREATE_NEW, + StandardOpenOption.WRITE); + fail(); + } catch (FileAlreadyExistsException expected) { + } + + // Create should still work, though. + channel = AsynchronousFileChannel.open(newFile, StandardOpenOption.CREATE, + StandardOpenOption.WRITE); + channel.close(); + } + + @Test + public void testOpen_nonexistent() throws Throwable { + Path tempDir = Files.createTempDirectory("ASFCTest_test_open_nonexistent"); + + Path nonExistent = tempDir.resolve("nonExistentFile"); + try { + AsynchronousFileChannel.open(nonExistent, StandardOpenOption.READ); + fail(); + } catch (NoSuchFileException expected) { + } + + try { + AsynchronousFileChannel.open(nonExistent, StandardOpenOption.WRITE); + fail(); + } catch (NoSuchFileException expected) { + } + + // The following three cases haven't clearly been mentioned in the documentation. + // CREATE / CREATE_NEW fail unless they're paired with WRITE. + // + // CREATE will succeed without WRITE in the case that the file already exists, which + // seems like a wart. + try { + AsynchronousFileChannel.open(nonExistent, StandardOpenOption.CREATE); + fail(); + } catch (NoSuchFileException expected) { + } + + try { + AsynchronousFileChannel.open(nonExistent, StandardOpenOption.CREATE, + StandardOpenOption.READ); + fail(); + } catch (NoSuchFileException expected) { + } + + try { + AsynchronousFileChannel.open(nonExistent, StandardOpenOption.CREATE_NEW); + fail(); + } catch (NoSuchFileException expected) { + } + } + + private static File createTemporaryFile(int size) throws IOException { + if (size % 256 != 0) { + throw new IllegalArgumentException("size % 256 != 0: " + size); + } + + File temp = File.createTempFile("AFCTest_tempfile", ""); + byte[] buf = new byte[256]; + + try (FileOutputStream fos = new FileOutputStream(temp)) { + int bytesWritten = 0; + while (bytesWritten < size) { + fos.write(buf); + bytesWritten += buf.length; + } + } + + return temp; + } + + private static File createTemporaryFile(byte[] contents) throws IOException { + File temp = File.createTempFile("AFCTest_tempfile", ""); + + try (FileOutputStream fos = new FileOutputStream(temp)) { + fos.write(contents); + } + + return temp; + } + + @Test + public void testOpen_truncate() throws Throwable { + File temp = createTemporaryFile(256); + assertEquals(256, temp.length()); + + AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); + afc.close(); + + assertEquals(0, temp.length()); + } + + @Test + public void testOpen_deleteOnClose() throws Throwable { + File temp = createTemporaryFile(256); + assertTrue(temp.exists()); + + AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.READ); + assertEquals(256, afc.size()); + afc.close(); + + assertFalse(temp.exists()); + } + + @Test + public void testRead_Future() throws Throwable { + byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; + File temp = createTemporaryFile(contents); + + byte[] readBuf = new byte[4]; + ByteBuffer buf = ByteBuffer.wrap(readBuf); + + AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.WRITE); + try { + afc.read(buf, 0); + fail(); + } catch (NonReadableChannelException exception) { + } + + afc.close(); + + afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.READ); + + Future fut = afc.read(buf, 0); + assertEquals(4, (int) fut.get()); + buf.flip(); + assertEquals('a', readBuf[0]); + assertEquals('b', readBuf[1]); + assertEquals('c', readBuf[2]); + assertEquals('d', readBuf[3]); + + // Short read: at the end of the file. + fut = afc.read(buf, 6); + assertEquals(2, (int) fut.get()); + assertEquals('g', readBuf[0]); + assertEquals('h', readBuf[1]); + + // Reads past the end of the file. + fut = afc.read(buf, 8); + assertEquals(-1, (int) fut.get()); + + fut = afc.read(buf, 9); + assertEquals(-1, (int) fut.get()); + + try { + afc.read(buf, -1); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + afc.read(null, 1); + fail(); + } catch (NullPointerException expected) { + } + + try { + afc.read(buf.asReadOnlyBuffer(), 1); + fail(); + } catch (IllegalArgumentException expected) { + } + + afc.close(); + } + + static class RecordingHandler implements CompletionHandler { + public String attachment; + public int result; + public Throwable exc; + + private final CountDownLatch cdl = new CountDownLatch(1); + + @Override + public void completed(Integer result, String attachment) { + this.result = result; + this.attachment = attachment; + + cdl.countDown(); + } + + @Override + public void failed(Throwable exc, String attachment) { + this.exc = exc; + this.attachment = attachment; + } + + public boolean awaitCompletion() throws InterruptedException { + return cdl.await(10, TimeUnit.SECONDS); + } + } + + @Test + public void testRead_CompletionListener() throws Throwable { + byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; + File temp = createTemporaryFile(contents); + + byte[] readBuf = new byte[4]; + ByteBuffer buf = ByteBuffer.wrap(readBuf); + + AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.WRITE); + String attachment = "ATTACHMENT"; + RecordingHandler handler = new RecordingHandler(); + + try { + afc.read(buf, 0, attachment, handler); + fail(); + } catch (NonReadableChannelException exception) { + } + + afc.close(); + + afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.READ); + afc.read(buf, 0, attachment, handler); + assertTrue(handler.awaitCompletion()); + assertEquals(4, handler.result); + assertSame(attachment, handler.attachment); + buf.flip(); + assertEquals('a', readBuf[0]); + assertEquals('b', readBuf[1]); + assertEquals('c', readBuf[2]); + assertEquals('d', readBuf[3]); + + // Short read: at the end of the file. + handler = new RecordingHandler(); + attachment = "ATTACHMENT2"; + afc.read(buf, 6, attachment, handler); + assertTrue(handler.awaitCompletion()); + assertEquals(2, handler.result); + assertSame(attachment, handler.attachment); + assertEquals('g', readBuf[0]); + assertEquals('h', readBuf[1]); + + // Reads past the end of the file. + handler = new RecordingHandler(); + attachment = "ATTACHMENT3"; + afc.read(buf, 8, attachment, handler); + assertTrue(handler.awaitCompletion()); + assertEquals(-1, handler.result); + assertSame(attachment, handler.attachment); + + handler = new RecordingHandler(); + attachment = "ATTACHMENT4"; + afc.read(buf, 9, attachment, handler); + assertTrue(handler.awaitCompletion()); + assertEquals(-1, handler.result); + assertSame(attachment, handler.attachment); + + handler = new RecordingHandler(); + attachment = "ATTACHMENT5"; + try { + afc.read(buf, -1, attachment, handler); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + afc.read(null, 1, attachment, handler); + fail(); + } catch (NullPointerException expected) { + } + + try { + afc.read(buf.asReadOnlyBuffer(), 1, attachment, handler); + fail(); + } catch (IllegalArgumentException expected) { + } + + afc.close(); + } + + @Test + public void testWrite_Future() throws Throwable { + byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; + File temp = createTemporaryFile(contents); + + byte[] readBuf = new byte[4]; + ByteBuffer buf = ByteBuffer.wrap(readBuf); + + AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.READ); + try { + afc.write(buf, 0); + fail(); + } catch (NonWritableChannelException exception) { + } + + afc.close(); + + afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.WRITE, StandardOpenOption.READ); + + assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y'}), 0).get()); + + Future fut = afc.read(buf, 0); + + assertEquals(4, (int) fut.get()); + buf.flip(); + assertEquals('x', readBuf[0]); + assertEquals('y', readBuf[1]); + assertEquals('c', readBuf[2]); + assertEquals('d', readBuf[3]); + + // Write that expands beyond the end of the file. + assertEquals(3, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y', 'z'}), 6).get()); + assertEquals(9, afc.size()); + + buf.rewind(); + fut = afc.read(buf, 6); + assertEquals(3, (int) fut.get()); + buf.flip(); + assertEquals('x', readBuf[0]); + assertEquals('y', readBuf[1]); + assertEquals('z', readBuf[2]); + + // Writes at the end of the file. + assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y' }), 9).get()); + assertEquals(11, afc.size()); + buf.rewind(); + fut = afc.read(buf, 9); + assertEquals(2, (int) fut.get()); + buf.flip(); + assertEquals('x', readBuf[0]); + assertEquals('y', readBuf[1]); + + // Writes past the end of the file. + assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { '0', '2' }), 13).get()); + assertEquals(15, afc.size()); + + // This is broken behaviour. At this point, there are 4 readable bytes in the file + // and our buffer should be filled with {0, 0, 48, 50}... + buf.rewind(); + fut = afc.read(buf, 11); + assertEquals(2, (int) fut.get()); + buf.flip(); + assertEquals(0, readBuf[0]); + assertEquals(0, readBuf[1]); + + // ... if we explicitly read at position 13, things are somehow fine again. + buf.rewind(); + fut = afc.read(buf, 13); + assertEquals(2, (int) fut.get()); + buf.flip(); + assertEquals('0', readBuf[0]); + assertEquals('2', readBuf[1]); + + try { + afc.write(buf, -1); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + afc.write(null, 1); + fail(); + } catch (NullPointerException expected) { + } + + afc.close(); + } + + @Test + public void testWrite_CompletionListener() throws Throwable { + byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; + File temp = createTemporaryFile(contents); + + byte[] readBuf = new byte[4]; + ByteBuffer buf = ByteBuffer.wrap(readBuf); + + AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.READ); + + String attachment = "ATTACHMENT"; + RecordingHandler handler = new RecordingHandler(); + try { + afc.write(buf, 0, attachment, handler); + fail(); + } catch (NonWritableChannelException exception) { + } + + afc.close(); + + afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.WRITE, StandardOpenOption.READ); + + attachment = "ATTACHMENT"; + handler = new RecordingHandler(); + afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y'}), 0, attachment, handler); + assertTrue(handler.awaitCompletion()); + assertSame(attachment, handler.attachment); + assertEquals(2, handler.result); + + Future fut = afc.read(buf, 0); + + assertEquals(4, (int) fut.get()); + buf.flip(); + assertEquals('x', readBuf[0]); + assertEquals('y', readBuf[1]); + assertEquals('c', readBuf[2]); + assertEquals('d', readBuf[3]); + + // Write that expands beyond the end of the file. + attachment = "ATTACHMENT2"; + handler = new RecordingHandler(); + afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y', 'z'}), 6, attachment, handler); + assertTrue(handler.awaitCompletion()); + assertSame(attachment, handler.attachment); + assertEquals(3, handler.result); + + assertEquals(9, afc.size()); + + buf.rewind(); + fut = afc.read(buf, 6); + assertEquals(3, (int) fut.get()); + buf.flip(); + assertEquals('x', readBuf[0]); + assertEquals('y', readBuf[1]); + assertEquals('z', readBuf[2]); + + // Writes at the end of the file. + attachment = "ATTACHMENT3"; + handler = new RecordingHandler(); + afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y' }), 9, attachment, handler); + assertTrue(handler.awaitCompletion()); + assertSame(attachment, handler.attachment); + assertEquals(2, handler.result); + + assertEquals(11, afc.size()); + buf.rewind(); + fut = afc.read(buf, 9); + assertEquals(2, (int) fut.get()); + buf.flip(); + assertEquals('x', readBuf[0]); + assertEquals('y', readBuf[1]); + + // Writes past the end of the file. + attachment = "ATTACHMENT4"; + handler = new RecordingHandler(); + afc.write(ByteBuffer.wrap(new byte[] { '0', '2' }), 13, attachment, handler); + assertTrue(handler.awaitCompletion()); + assertSame(attachment, handler.attachment); + assertEquals(2, handler.result); + + assertEquals(15, afc.size()); + + // This is broken behaviour. At this point, there are 4 readable bytes in the file + // and our buffer should be filled with {0, 0, 48, 50}... + buf.rewind(); + fut = afc.read(buf, 11); + assertEquals(2, (int) fut.get()); + buf.flip(); + assertEquals(0, readBuf[0]); + assertEquals(0, readBuf[1]); + + // ... if we explicitly read at position 13, things are somehow fine again. + buf.rewind(); + fut = afc.read(buf, 13); + assertEquals(2, (int) fut.get()); + buf.flip(); + assertEquals('0', readBuf[0]); + assertEquals('2', readBuf[1]); + + try { + afc.write(buf, -1); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + afc.write(null, 1); + fail(); + } catch (NullPointerException expected) { + } + + afc.close(); + } + + @Test + public void testWrite_Append() throws Throwable { + File temp = createTemporaryFile(256); + + try { + AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.WRITE, StandardOpenOption.APPEND); + fail(); + } catch (UnsupportedOperationException expected) { + } + } + + @Test + public void testSize() throws Throwable { + File temp = createTemporaryFile(256); + assertEquals(256, temp.length()); + + AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.WRITE); + // Test initial size. + assertEquals(256, afc.size()); + + // Test that the size is updated after a write at the end of the file. + ByteBuffer buf = ByteBuffer.allocate(16); + assertEquals(16, (int) afc.write(buf, 256).get()); + assertEquals(272, afc.size()); + + // Test that the size is updated after a truncate. + afc.truncate(16); + assertEquals(16, afc.size()); + afc.close(); + } + + @Test + public void testTruncate() throws Throwable { + File temp = createTemporaryFile(256); + assertEquals(256, temp.length()); + + AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(), + StandardOpenOption.WRITE); + afc.truncate(128); + assertEquals(128, afc.size()); + assertEquals(128, temp.length()); + + afc.truncate(0); + assertEquals(0, afc.size()); + assertEquals(0, temp.length()); + + // Should be a no-op if the length is greater than the current length. + afc.truncate(128); + assertEquals(0, afc.size()); + assertEquals(0, temp.length()); + + try { + afc.truncate(-1); + fail(); + } catch (IllegalArgumentException expected) { + } + + afc.close(); + + // Attempts to truncate a file that's not writeable should throw a NWCE. + temp = createTemporaryFile(256); + afc = AsynchronousFileChannel.open(temp.toPath(), StandardOpenOption.READ); + try { + afc.truncate(128); + fail(); + } catch (NonWritableChannelException expected) { + } + + try { + afc.truncate(384); + fail(); + } catch (NonWritableChannelException expected) { + } + + afc.close(); + } + + @Test + public void testCustomExecutor() throws Throwable { + AtomicReference serviceThread = new AtomicReference<>(); + ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + serviceThread.set(new Thread(r)); + return serviceThread.get(); + } + }); + + Set openOptions = new HashSet<>(); + openOptions.add(StandardOpenOption.READ); + openOptions.add(StandardOpenOption.WRITE); + + AsynchronousFileChannel afc = AsynchronousFileChannel.open( + Files.createTempFile("AFCTest_testCustomExecutor", ""), + openOptions, executorService); + + final LinkedBlockingQueue observedThreads = new LinkedBlockingQueue<>(); + CompletionHandler handler = new CompletionHandler() { + @Override + public void completed(Integer result, String attachment) { + assertTrue(observedThreads.offer(Thread.currentThread())); + } + + @Override + public void failed(Throwable exc, String attachment) { + assertTrue(observedThreads.offer(Thread.currentThread())); + } + }; + + afc.write(ByteBuffer.allocate(16), 0, "foo", handler); + assertSame(serviceThread.get(), observedThreads.take()); + assertEquals(0, observedThreads.size()); + + afc.read(ByteBuffer.allocate(16), 0, "foo", handler); + assertSame(serviceThread.get(), observedThreads.take()); + assertEquals(0, observedThreads.size()); + } + + @Test + public void testForce() throws Throwable { + Path tempDir = Files.createTempFile("ASFCTest_test_force", ""); + + AsynchronousFileChannel afc = AsynchronousFileChannel.open(tempDir, + StandardOpenOption.CREATE, StandardOpenOption.WRITE); + // Test that force can be called, not much else can be tested. + assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y'}), 0).get()); + afc.force(false); + assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y'}), 0).get()); + afc.force(true); + afc.close(); + + try { + afc.force(true); + fail(); + } catch(ClosedChannelException expected) {} + } + +} diff --git a/luni/src/test/java/libcore/java/nio/channels/AsynchronousServerSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/AsynchronousServerSocketChannelTest.java new file mode 100644 index 000000000..025abb64f --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/AsynchronousServerSocketChannelTest.java @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.nio.channels; + +import org.junit.Rule; + +import java.io.IOException; +import java.net.BindException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.UnresolvedAddressException; +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.LeakageDetectorRule; + +public class AsynchronousServerSocketChannelTest extends TestCaseWithRules { + + @Rule + public LeakageDetectorRule leakageDetectorRule = ResourceLeakageDetector.getRule(); + + public void test_bind() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + assertTrue(assc.isOpen()); + assertNull(assc.getLocalAddress()); + assc.bind(new InetSocketAddress(0)); + assertNotNull(assc.getLocalAddress()); + try { + assc.bind(new InetSocketAddress(0)); + fail(); + } catch (AlreadyBoundException expected) {} + + assc.close(); + assertFalse(assc.isOpen()); + } + + public void test_bind_null() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + assertTrue(assc.isOpen()); + assertNull(assc.getLocalAddress()); + assc.bind(null); + assertNotNull(assc.getLocalAddress()); + try { + assc.bind(null); + fail(); + } catch (AlreadyBoundException expected) {} + + assc.close(); + assertFalse(assc.isOpen()); + } + + public void test_bind_unresolvedAddress() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + try { + assc.bind(new InetSocketAddress("unresolvedname", 31415)); + fail(); + } catch (UnresolvedAddressException expected) {} + + assertNull(assc.getLocalAddress()); + assertTrue(assc.isOpen()); + assc.close(); + } + + public void test_bind_used() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + ServerSocket ss = new ServerSocket(0); + try { + assc.bind(ss.getLocalSocketAddress()); + fail(); + } catch (BindException expected) {} + assertNull(assc.getLocalAddress()); + + ss.close(); + assc.close(); + } + + public void test_futureAccept() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + assc.bind(new InetSocketAddress(0)); + + Future acceptFuture = assc.accept(); + + Socket s = new Socket(); + s.connect(assc.getLocalAddress()); + + AsynchronousSocketChannel asc = acceptFuture.get(1000, TimeUnit.MILLISECONDS); + + assertTrue(s.isConnected()); + assertNotNull(asc.getLocalAddress()); + assertEquals(asc.getLocalAddress(), s.getRemoteSocketAddress()); + assertNotNull(asc.getRemoteAddress()); + assertEquals(asc.getRemoteAddress(), s.getLocalSocketAddress()); + + asc.close(); + s.close(); + assc.close(); + } + + public void test_completionHandlerAccept() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + assc.bind(new InetSocketAddress(0)); + + FutureLikeCompletionHandler acceptCompletionHandler = + new FutureLikeCompletionHandler(); + + assc.accept(null /* attachment */, acceptCompletionHandler); + + Socket s = new Socket(); + s.connect(assc.getLocalAddress()); + AsynchronousSocketChannel asc = acceptCompletionHandler.get(1000); + + assertNotNull(asc); + assertTrue(s.isConnected()); + assertNotNull(asc.getLocalAddress()); + assertEquals(asc.getLocalAddress(), s.getRemoteSocketAddress()); + assertNotNull(asc.getRemoteAddress()); + assertEquals(asc.getRemoteAddress(), s.getLocalSocketAddress()); + + assertNull(acceptCompletionHandler.getAttachment()); + + asc.close(); + s.close(); + assc.close(); + } + + public void test_completionHandlerAccept_attachment() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + assc.bind(new InetSocketAddress(0)); + + FutureLikeCompletionHandler acceptCompletionHandler = + new FutureLikeCompletionHandler(); + + Integer attachment = new Integer(123); + assc.accept(attachment, acceptCompletionHandler); + + Socket s = new Socket(); + s.connect(assc.getLocalAddress()); + AsynchronousSocketChannel asc = acceptCompletionHandler.get(1000); + + assertNotNull(asc); + assertTrue(s.isConnected()); + + assertEquals(attachment, acceptCompletionHandler.getAttachment()); + + asc.close(); + s.close(); + assc.close(); + } + + public void test_completionHandlerAccept_nyb() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + + FutureLikeCompletionHandler acceptCompletionHandler = + new FutureLikeCompletionHandler(); + try { + assc.accept(null /* attachment */, acceptCompletionHandler); + fail(); + } catch(NotYetBoundException expected) {} + + assc.close(); + } + + public void test_completionHandlerAccept_npe() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + assc.bind(new InetSocketAddress(0)); + + try { + assc.accept(null /* attachment */, null /* completionHandler */); + fail(); + } catch(NullPointerException expected) {} + + assc.close(); + } + + + public void test_options() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + + assc.setOption(StandardSocketOptions.SO_RCVBUF, 5000); + assertEquals(5000, (long)assc.getOption(StandardSocketOptions.SO_RCVBUF)); + + assc.setOption(StandardSocketOptions.SO_REUSEADDR, true); + assertTrue(assc.getOption(StandardSocketOptions.SO_REUSEADDR)); + + assc.close(); + } + + public void test_options_iae() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + + try { + assc.setOption(StandardSocketOptions.SO_KEEPALIVE, true); + fail(); + } catch (UnsupportedOperationException expected) {} + + assc.close(); + } + + public void test_group() throws Throwable { + AsynchronousChannelProvider provider = + AsynchronousChannelProvider.provider(); + AsynchronousChannelGroup group = + provider.openAsynchronousChannelGroup(2, Executors.defaultThreadFactory()); + + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(group); + assertNull(assc.getLocalAddress()); + assc.bind(new InetSocketAddress(0)); + assertNotNull(assc.getLocalAddress()); + assertEquals(provider, assc.provider()); + assc.close(); + } + + public void test_close() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + assc.bind(new InetSocketAddress(0)); + assc.close(); + + Future acceptFuture = assc.accept(); + try { + acceptFuture.get(1000, TimeUnit.MILLISECONDS); + fail(); + } catch(ExecutionException expected) { + assertTrue(expected.getCause() instanceof ClosedChannelException); + } + + FutureLikeCompletionHandler acceptCompletionHandler = + new FutureLikeCompletionHandler(); + assc.accept(null /* attachment */, acceptCompletionHandler); + try { + acceptCompletionHandler.get(1000); + fail(); + } catch(ClosedChannelException expected) {} + + try { + assc.bind(new InetSocketAddress(0)); + fail(); + } catch(ClosedChannelException expected) {} + + try { + assc.setOption(StandardSocketOptions.SO_REUSEADDR, true); + fail(); + } catch(ClosedChannelException expected) {} + + // Try second close + assc.close(); + } + + public void test_future_concurrent_close() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + assc.bind(new InetSocketAddress(0)); + + final AtomicReference killerThreadException = + new AtomicReference(null); + final Thread killer = new Thread(new Runnable() { + public void run() { + try { + Thread.sleep(2000); + assc.close(); + } catch (Exception ex) { + killerThreadException.set(ex); + } + } + }); + killer.start(); + Future acceptFuture = assc.accept(); + try { + // This may timeout on slow devices, they may need more time for the killer thread to + // do its thing. + acceptFuture.get(10000, TimeUnit.MILLISECONDS); + fail(); + } catch(ExecutionException expected) { + assertTrue(expected.getCause() instanceof ClosedChannelException); + } + + assertNull(killerThreadException.get()); + } + + public void test_completionHandler_concurrent_close() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + assc.bind(new InetSocketAddress(0)); + + final AtomicReference killerThreadException = + new AtomicReference(null); + final Thread killer = new Thread(new Runnable() { + public void run() { + try { + Thread.sleep(2000); + assc.close(); + } catch (Exception ex) { + killerThreadException.set(ex); + } + } + }); + killer.start(); + + FutureLikeCompletionHandler acceptCompletionHandler = + new FutureLikeCompletionHandler(); + + assc.accept(null /* attachment */, acceptCompletionHandler); + + try { + // This may timeout on slow devices, they may need more time for the killer thread to + // do its thing. + acceptCompletionHandler.get(10000); + fail(); + } catch(AsynchronousCloseException expected) {} + + assertNull(killerThreadException.get()); + } + + public void test_supportedOptions() throws Throwable { + AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(); + + Set> supportedOptions = assc.supportedOptions(); + assertEquals(2, supportedOptions.size()); + + assertTrue(supportedOptions.contains(StandardSocketOptions.SO_REUSEADDR)); + assertTrue(supportedOptions.contains(StandardSocketOptions.SO_RCVBUF)); + + // supportedOptions should work after close according to spec + assc.close(); + supportedOptions = assc.supportedOptions(); + assertEquals(2, supportedOptions.size()); + } + + public void test_closeGuardSupport() throws IOException { + try (AsynchronousServerSocketChannel asc = AsynchronousServerSocketChannel.open()) { + leakageDetectorRule.assertUnreleasedResourceCount(asc, 1); + } + } + + public void test_closeGuardSupport_group() throws IOException { + AsynchronousChannelProvider provider = + AsynchronousChannelProvider.provider(); + AsynchronousChannelGroup group = + provider.openAsynchronousChannelGroup(2, Executors.defaultThreadFactory()); + + try (AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(group)) { + leakageDetectorRule.assertUnreleasedResourceCount(assc, 1); + } + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/AsynchronousSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/AsynchronousSocketChannelTest.java new file mode 100644 index 000000000..998353977 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/AsynchronousSocketChannelTest.java @@ -0,0 +1,925 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.nio.channels; + +import org.junit.Rule; + +import java.io.IOException; +import java.net.BindException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.UnresolvedAddressException; +import java.nio.channels.UnsupportedAddressTypeException; +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.LeakageDetectorRule; + +public class AsynchronousSocketChannelTest extends TestCaseWithRules { + + @Rule + public LeakageDetectorRule leakageDetectorRule = ResourceLeakageDetector.getRule(); + + // Comfortably smaller than the default TCP socket buffer size to avoid blocking on write. + final int NON_BLOCKING_MESSAGE_SIZE = 32; + + public void test_connect() throws Exception { + ServerSocket ss = new ServerSocket(0); + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + assertEquals(asc.provider(), AsynchronousChannelProvider.provider()); + assertTrue(asc.isOpen()); + assertNull(asc.getRemoteAddress()); + assertNull(asc.getLocalAddress()); + + // Connect + InetSocketAddress remoteAddress = new InetSocketAddress("localhost", ss.getLocalPort()); + Future connectFuture = asc.connect(remoteAddress); + connectFuture.get(1000, TimeUnit.MILLISECONDS); + Socket s = ss.accept(); + + assertNotNull(asc.getLocalAddress()); + assertEquals(asc.getLocalAddress(), s.getRemoteSocketAddress()); + assertNotNull(asc.getRemoteAddress()); + assertEquals(asc.getRemoteAddress(), s.getLocalSocketAddress()); + + assertTrue(asc.isOpen()); + + asc.close(); + ss.close(); + s.close(); + } + + public void test_bind() throws Exception { + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + assertNull(asc.getLocalAddress()); + assertNull(asc.getRemoteAddress()); + assertTrue(asc.isOpen()); + + asc.bind(new InetSocketAddress(0)); + + assertNotNull(asc.getLocalAddress()); + assertNull(asc.getRemoteAddress()); + assertTrue(asc.isOpen()); + + asc.close(); + } + + static class MySocketAddress extends SocketAddress { + final static long serialVersionUID = 0; + } + + public void test_bind_unsupportedAddress() throws Exception { + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + try { + asc.bind(new MySocketAddress()); + fail(); + } catch (UnsupportedAddressTypeException expected) {} + + assertNull(asc.getLocalAddress()); + assertNull(asc.getRemoteAddress()); + assertTrue(asc.isOpen()); + + asc.close(); + } + + + public void test_bind_unresolvedAddress() throws Exception { + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + try { + asc.bind(new InetSocketAddress("unresolvedname", 31415)); + fail(); + } catch (UnresolvedAddressException expected) {} + + assertNull(asc.getLocalAddress()); + assertNull(asc.getRemoteAddress()); + assertTrue(asc.isOpen()); + + asc.close(); + } + + public void test_bind_usedAddress() throws Exception { + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + ServerSocket ss = new ServerSocket(0); + try { + asc.bind(ss.getLocalSocketAddress()); + fail(); + } catch (BindException expected) {} + + assertNull(asc.getLocalAddress()); + assertNull(asc.getRemoteAddress()); + assertTrue(asc.isOpen()); + + ss.close(); + asc.close(); + } + + public void test_bind_null() throws Exception { + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + asc.bind(null); + + assertNotNull(asc.getLocalAddress()); + assertNull(asc.getRemoteAddress()); + assertTrue(asc.isOpen()); + + asc.close(); + } + + public void test_connect_unresolvedAddress() throws Exception { + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + try { + asc.connect(new InetSocketAddress("unresolvedname", 31415)); + fail(); + } catch (UnresolvedAddressException expected) {} + + assertNull(asc.getRemoteAddress()); + assertTrue(asc.isOpen()); + asc.close(); + } + + public void test_close() throws Throwable { + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + assertTrue(asc.isOpen()); + + asc.close(); + assertFalse(asc.isOpen()); + + try { + asc.getRemoteAddress(); + fail(); + } catch (ClosedChannelException expected) {} + try { + asc.getLocalAddress(); + fail(); + } catch (ClosedChannelException expected) {} + + ByteBuffer tmp = createTestByteBuffer(16, false); + FutureLikeCompletionHandler intCompletionHandler = null; + FutureLikeCompletionHandler longCompletionHandler = null; + + Future readFuture = asc.read(tmp); + try { + readFuture.get(1000, TimeUnit.MILLISECONDS); + fail(); + } catch (ExecutionException expected) { + assertTrue(expected.getCause() instanceof ClosedChannelException); + } + + longCompletionHandler = new FutureLikeCompletionHandler<>(); + asc.read(new ByteBuffer[]{tmp}, 0, 1, 100L, TimeUnit.MILLISECONDS, null, longCompletionHandler); + try { + longCompletionHandler.get(1000); + fail(); + } catch (ClosedChannelException expected) {} + + + intCompletionHandler = new FutureLikeCompletionHandler<>(); + asc.read(tmp, null, intCompletionHandler); + try { + intCompletionHandler.get(1000); + fail(); + } catch (ClosedChannelException expected) {} + + + intCompletionHandler = new FutureLikeCompletionHandler<>(); + asc.read(tmp, 100, TimeUnit.MILLISECONDS, null, intCompletionHandler); + try { + intCompletionHandler.get(100); + fail(); + } catch (ClosedChannelException expected) {} + + Future writeFuture = asc.write(tmp); + try { + writeFuture.get(1000, TimeUnit.MILLISECONDS); + fail(); + } catch (ExecutionException expected) { + assertTrue(expected.getCause() instanceof ClosedChannelException); + } + + longCompletionHandler = new FutureLikeCompletionHandler<>(); + asc.write(new ByteBuffer[]{tmp}, 0, 1, 100, TimeUnit.MILLISECONDS, null, longCompletionHandler); + try { + longCompletionHandler.get(1000); + fail(); + } catch (ClosedChannelException expected) {} + + intCompletionHandler = new FutureLikeCompletionHandler<>(); + asc.write(tmp, null, intCompletionHandler); + try { + intCompletionHandler.get(1000); + fail(); + } catch (ClosedChannelException expected) {} + + intCompletionHandler = new FutureLikeCompletionHandler<>(); + asc.write(tmp, 100, TimeUnit.MILLISECONDS, null, intCompletionHandler); + try { + intCompletionHandler.get(1000); + fail(); + } catch (ClosedChannelException expected) {} + + try { + asc.setOption(StandardSocketOptions.SO_REUSEADDR, true); + fail(); + } catch(ClosedChannelException expected) {} + + // Try second close + asc.close(); + } + + public void test_futureReadWrite_HeapButeBuffer() throws Exception { + test_futureReadWrite(false /* useDirectByteBuffer */); + } + + public void test_futureReadWrite_DirectByteBuffer() throws Exception { + test_futureReadWrite(true /* useDirectByteBuffer */); + } + + private void test_futureReadWrite(boolean useDirectByteBuffer) throws Exception { + ServerSocket ss = new ServerSocket(0); + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + // Connect + Future connectFuture = asc.connect(ss.getLocalSocketAddress()); + connectFuture.get(1000, TimeUnit.MILLISECONDS); + assertNotNull(asc.getRemoteAddress()); + assertTrue(connectFuture.isDone()); + + // Accept & write data + final int messageSize = NON_BLOCKING_MESSAGE_SIZE; + final ByteBuffer sendData = createTestByteBuffer(messageSize, useDirectByteBuffer); + Socket sss = ss.accept(); + // Small message, won't block on write + sss.getOutputStream().write(sendData.array(), sendData.arrayOffset(), messageSize); + + // Read data from async channel and call #get on result future + ByteBuffer receivedData = createTestByteBuffer(messageSize, useDirectByteBuffer); + assertEquals(messageSize, (int)asc.read(receivedData).get(1000, TimeUnit.MILLISECONDS)); + + // Compare results + receivedData.flip(); + assertEquals(sendData, receivedData); + + // Write data to async channel and call #get on result future + assertEquals(messageSize, (int)asc.write(sendData).get(1000, TimeUnit.MILLISECONDS)); + + // Read data and compare with original + byte[] readArray = new byte[messageSize]; + assertEquals(messageSize, sss.getInputStream().read(readArray)); + + // Compare results + sendData.flip(); + assertEquals(sendData, ByteBuffer.wrap(readArray)); + + asc.close(); + sss.close(); + ss.close(); + } + + public void test_completionHandlerReadWrite_HeapByteBuffer() throws Throwable { + test_completionHandlerReadWrite(false /* useDirectByteBuffer */); + } + + public void test_completionHandlerReadWrite_DirectByteBuffer() throws Throwable { + test_completionHandlerReadWrite(true /* useDirectByteBuffer */); + } + + private void test_completionHandlerReadWrite(boolean useDirectByteBuffer) throws Throwable { + ServerSocket ss = new ServerSocket(0); + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + // Connect + FutureLikeCompletionHandler connectCompletionHandler = + new FutureLikeCompletionHandler<>(); + Object attachment = new Integer(1); + asc.connect(ss.getLocalSocketAddress(), attachment, connectCompletionHandler); + connectCompletionHandler.get(1000); + assertNotNull(asc.getRemoteAddress()); + assertEquals(attachment, connectCompletionHandler.getAttachment()); + + // Accept & write data + final int messageSize = NON_BLOCKING_MESSAGE_SIZE; + ByteBuffer sendData = createTestByteBuffer(messageSize, useDirectByteBuffer); + Socket sss = ss.accept(); + // Small message, won't block on write + sss.getOutputStream().write(sendData.array(), sendData.arrayOffset(), messageSize); + + // Read data from async channel + ByteBuffer receivedData = createTestByteBuffer(messageSize, useDirectByteBuffer); + FutureLikeCompletionHandler readCompletionHandler = + new FutureLikeCompletionHandler<>(); + asc.read(receivedData, attachment, readCompletionHandler); + assertEquals(messageSize, (int)readCompletionHandler.get(1000)); + assertEquals(attachment, readCompletionHandler.getAttachment()); + + // Compare results + receivedData.flip(); + assertEquals(sendData, receivedData); + + // Write data to async channel + FutureLikeCompletionHandler writeCompletionHandler = + new FutureLikeCompletionHandler<>(); + asc.write(sendData, attachment, writeCompletionHandler); + assertEquals(messageSize, (int)writeCompletionHandler.get(1000)); + assertEquals(attachment, writeCompletionHandler.getAttachment()); + + // Read data and compare with original + byte[] readArray = new byte[messageSize]; + assertEquals(messageSize, sss.getInputStream().read(readArray)); + sendData.flip(); + assertEquals(sendData, ByteBuffer.wrap(readArray)); + + asc.close(); + sss.close(); + ss.close(); + } + + + public void test_scatterReadWrite_HeapByteBuffer() throws Throwable { + test_scatterReadWrite(false /* useDirectByteBuffer */); + } + + public void test_scatterReadWrite_DirectByteBuffer() throws Throwable { + test_scatterReadWrite(true /* useDirectByteBuffer */); + } + + private void test_scatterReadWrite(boolean useDirectByteBuffer) throws Throwable { + ServerSocket ss = new ServerSocket(0); + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + // Connect + FutureLikeCompletionHandler connectCompletionHandler = + new FutureLikeCompletionHandler<>(); + Object attachment = new Integer(1); + asc.connect(ss.getLocalSocketAddress(), attachment, connectCompletionHandler); + connectCompletionHandler.get(1000); + assertNotNull(asc.getRemoteAddress()); + assertEquals(attachment, connectCompletionHandler.getAttachment()); + + // Accept & write data + final int messageSize = NON_BLOCKING_MESSAGE_SIZE; + ByteBuffer sendData1 = createTestByteBuffer(messageSize, useDirectByteBuffer, 0); + ByteBuffer sendData2 = createTestByteBuffer(messageSize, useDirectByteBuffer, 6); + Socket sss = ss.accept(); + + // Small message, won't block on write + sss.getOutputStream().write(sendData1.array(), sendData1.arrayOffset(), messageSize); + sss.getOutputStream().write(sendData2.array(), sendData2.arrayOffset(), messageSize); + + // Read data from async channel + ByteBuffer receivedData1 = createTestByteBuffer(messageSize, useDirectByteBuffer); + ByteBuffer receivedData2 = createTestByteBuffer(messageSize, useDirectByteBuffer); + FutureLikeCompletionHandler readCompletionHandler = + new FutureLikeCompletionHandler<>(); + + asc.read(new ByteBuffer[]{receivedData1, receivedData2}, 0, 2, + 1000L, TimeUnit.MILLISECONDS, attachment, readCompletionHandler); + assertEquals(messageSize * 2L, (long)readCompletionHandler.get(1000)); + assertEquals(attachment, readCompletionHandler.getAttachment()); + + // Compare results + receivedData1.flip(); + assertEquals(sendData1, receivedData1); + + receivedData2.flip(); + assertEquals(sendData2, receivedData2); + + // Write data to async channel + FutureLikeCompletionHandler writeCompletionHandler = + new FutureLikeCompletionHandler<>(); + asc.write(new ByteBuffer[]{sendData1, sendData2}, 0, 2, + 1000L, TimeUnit.MILLISECONDS, attachment, writeCompletionHandler); + assertEquals(messageSize*2L, (long)writeCompletionHandler.get(1000)); + assertEquals(attachment, writeCompletionHandler.getAttachment()); + + // Read data and compare with original + byte[] readArray = new byte[messageSize]; + assertEquals(messageSize, sss.getInputStream().read(readArray)); + sendData1.flip(); + assertEquals(sendData1, ByteBuffer.wrap(readArray)); + + assertEquals(messageSize, sss.getInputStream().read(readArray)); + sendData2.flip(); + assertEquals(sendData2, ByteBuffer.wrap(readArray)); + + asc.close(); + sss.close(); + ss.close(); + } + + public void test_completionHandler_connect_npe() throws Exception { + ServerSocket ss = new ServerSocket(0); + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + FutureLikeCompletionHandler connectCompletionHandler = + new FutureLikeCompletionHandler<>(); + // 1st argument NPE + try { + asc.connect(null, null, connectCompletionHandler); + fail(); + } catch(IllegalArgumentException expected) {} + + // 3rd argument NPE + try { + asc.connect(ss.getLocalSocketAddress(), null, null); + fail(); + } catch(NullPointerException expected) {} + + asc.close(); + ss.close(); + } + + public void test_read_npe() throws Throwable { + ServerSocket ss = new ServerSocket(0); + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + // Connect + FutureLikeCompletionHandler connectCompletionHandler = + new FutureLikeCompletionHandler<>(); + asc.connect(ss.getLocalSocketAddress(), null, + connectCompletionHandler); + connectCompletionHandler.get(1000); + assertNotNull(asc.getRemoteAddress()); + + // Read data from async channel + ByteBuffer receivedData = createTestByteBuffer(32, false); + FutureLikeCompletionHandler intCompletionHandler = + new FutureLikeCompletionHandler<>(); + FutureLikeCompletionHandler longCompletionHandler = + new FutureLikeCompletionHandler<>(); + + // 1st argument NPE + try { + asc.read(null, null, intCompletionHandler); + fail(); + } catch(NullPointerException expected) {} + + // 3rd argument NPE + try { + asc.read(receivedData, null, null); + fail(); + } catch(NullPointerException expected) {} + + // With timeout, 1st argument NPE + try { + asc.read(null, 100, TimeUnit.MILLISECONDS, null, intCompletionHandler); + fail(); + } catch(NullPointerException expected) {} + + // With timeout, 5rd argument NPE + try { + asc.read(receivedData, 100, TimeUnit.MILLISECONDS, null, null); + fail(); + } catch(NullPointerException expected) {} + + // Scatter read, 1st argument NPE + try { + asc.read(null, 0, 1, 0, TimeUnit.MILLISECONDS, null, longCompletionHandler); + fail(); + } catch(NullPointerException expected) {} + + // Scatter read, 1st argument NPE + try { + asc.read(new ByteBuffer[]{null}, 0, 1, 0, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(NullPointerException expected) {} + + // Scatter read, last argument NPE + try { + asc.read(new ByteBuffer[]{receivedData}, 0, 1, 0, TimeUnit.MILLISECONDS, null, + null); + fail(); + } catch(NullPointerException expected) {} + + asc.close(); + ss.close(); + } + + public void test_read_not_connected() throws Throwable { + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + ByteBuffer receivedData = createTestByteBuffer(32, false); + FutureLikeCompletionHandler intCompletionHandler = + new FutureLikeCompletionHandler<>(); + FutureLikeCompletionHandler longCompletionHandler = + new FutureLikeCompletionHandler<>(); + + try { + asc.read(receivedData); + fail(); + } catch(NotYetConnectedException expected) {} + + try { + asc.read(receivedData, null, intCompletionHandler); + fail(); + } catch(NotYetConnectedException expected) {} + + try { + asc.read(receivedData, 100, TimeUnit.MILLISECONDS, null, intCompletionHandler); + fail(); + } catch(NotYetConnectedException expected) {} + + try { + asc.read(new ByteBuffer[] {receivedData}, 0, 1, 100, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(NotYetConnectedException expected) {} + + asc.close(); + } + + public void test_read_failures() throws Throwable { + ServerSocket ss = new ServerSocket(0); + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + // Connect + FutureLikeCompletionHandler connectCompletionHandler = + new FutureLikeCompletionHandler<>(); + asc.connect(ss.getLocalSocketAddress(), null, + connectCompletionHandler); + connectCompletionHandler.get(1000); + assertNotNull(asc.getRemoteAddress()); + + ByteBuffer receivedData = createTestByteBuffer(32, false); + FutureLikeCompletionHandler intCompletionHandler = + new FutureLikeCompletionHandler<>(); + FutureLikeCompletionHandler longCompletionHandler = + new FutureLikeCompletionHandler<>(); + + ByteBuffer readOnly = receivedData.asReadOnlyBuffer(); + + // Read-only future read + try { + asc.read(readOnly); + fail(); + } catch(IllegalArgumentException expected) {} + + // Scatter-read read-only + try { + asc.read(new ByteBuffer[] {readOnly}, 0, 1, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(IllegalArgumentException expected) {} + + // Scatter-read bad offset + try { + asc.read(new ByteBuffer[] {receivedData}, -2, 1, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(IndexOutOfBoundsException expected) {} + try { + asc.read(new ByteBuffer[] {receivedData}, 3, 1, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(IndexOutOfBoundsException expected) {} + // Scatter-read bad length + try { + asc.read(new ByteBuffer[] {receivedData}, 0, -1, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(IndexOutOfBoundsException expected) {} + try { + asc.read(new ByteBuffer[] {receivedData}, 0, 3, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(IndexOutOfBoundsException expected) {} + + // Completion-handler read-only + try { + asc.read(readOnly, null, intCompletionHandler); + fail(); + } catch(IllegalArgumentException expected) {} + try { + asc.read(readOnly, 100L, TimeUnit.MILLISECONDS, null, intCompletionHandler); + fail(); + } catch(IllegalArgumentException expected) {} + + asc.close(); + ss.close(); + } + + public void test_write_npe() throws Throwable { + ServerSocket ss = new ServerSocket(0); + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + // Connect + FutureLikeCompletionHandler connectCompletionHandler = + new FutureLikeCompletionHandler<>(); + asc.connect(ss.getLocalSocketAddress(), null, + connectCompletionHandler); + connectCompletionHandler.get(1000); + assertNotNull(asc.getRemoteAddress()); + + // Read data from async channel + ByteBuffer receivedData = createTestByteBuffer(32, false); + FutureLikeCompletionHandler intCompletionHandler = + new FutureLikeCompletionHandler<>(); + FutureLikeCompletionHandler longCompletionHandler = + new FutureLikeCompletionHandler<>(); + + // 1st argument NPE + try { + asc.write(null, null, intCompletionHandler); + fail(); + } catch(NullPointerException expected) {} + + // 3rd argument NPE + try { + asc.write(receivedData, null, null); + fail(); + } catch(NullPointerException expected) {} + + // With timeout, 1st argument NPE + try { + asc.write(null, 100, TimeUnit.MILLISECONDS, null, intCompletionHandler); + fail(); + } catch(NullPointerException expected) {} + + // With timeout, 5rd argument NPE + try { + asc.write(receivedData, 100, TimeUnit.MILLISECONDS, null, null); + fail(); + } catch(NullPointerException expected) {} + + // Scatter write, 1st argument NPE. + try { + asc.write((ByteBuffer[])null, 0, 1, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(NullPointerException expected) {} + + // Scatter write, 1st argument NPE in array + // Surprise, it doesn't throw (not symmetric with scatter read) + asc.write(new ByteBuffer[]{null}, 0, 1, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + + // Scatter write, last argument NPE + try { + asc.write(new ByteBuffer[]{receivedData}, 0, 1, 0, TimeUnit.MILLISECONDS, null, + null); + fail(); + } catch(NullPointerException expected) {} + + asc.close(); + ss.close(); + } + + public void test_write_not_connected() throws Throwable { + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + ByteBuffer receivedData = createTestByteBuffer(32, false); + FutureLikeCompletionHandler intCompletionHandler = + new FutureLikeCompletionHandler<>(); + FutureLikeCompletionHandler longCompletionHandler = + new FutureLikeCompletionHandler<>(); + + try { + asc.write(receivedData); + fail(); + } catch(NotYetConnectedException expected) {} + + try { + asc.write(receivedData, null, intCompletionHandler); + fail(); + } catch(NotYetConnectedException expected) {} + + try { + asc.write(receivedData, 100, TimeUnit.MILLISECONDS, null, intCompletionHandler); + fail(); + } catch(NotYetConnectedException expected) {} + + try { + asc.write(new ByteBuffer[] {receivedData}, 0, 1, 100, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(NotYetConnectedException expected) {} + + asc.close(); + } + + public void test_write_failures() throws Throwable { + ServerSocket ss = new ServerSocket(0); + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + // Connect + FutureLikeCompletionHandler connectCompletionHandler = + new FutureLikeCompletionHandler<>(); + asc.connect(ss.getLocalSocketAddress(), null, + connectCompletionHandler); + connectCompletionHandler.get(1000); + assertNotNull(asc.getRemoteAddress()); + + ByteBuffer receivedData = createTestByteBuffer(32, false); + FutureLikeCompletionHandler intCompletionHandler = + new FutureLikeCompletionHandler<>(); + FutureLikeCompletionHandler longCompletionHandler = + new FutureLikeCompletionHandler<>(); + + // Scatter-write bad offset + try { + asc.write(new ByteBuffer[] {receivedData}, -2, 1, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(IndexOutOfBoundsException expected) {} + try { + asc.write(new ByteBuffer[] {receivedData}, 3, 1, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(IndexOutOfBoundsException expected) {} + // Scatter-write bad length + try { + asc.write(new ByteBuffer[] {receivedData}, 0, -1, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(IndexOutOfBoundsException expected) {} + try { + asc.write(new ByteBuffer[] {receivedData}, 0, 3, 100L, TimeUnit.MILLISECONDS, null, + longCompletionHandler); + fail(); + } catch(IndexOutOfBoundsException expected) {} + + asc.close(); + ss.close(); + } + + + public void test_shutdown() throws Exception { + ServerSocket ss = new ServerSocket(0); + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + // Connect + Future connectFuture = asc.connect(ss.getLocalSocketAddress()); + connectFuture.get(1000, TimeUnit.MILLISECONDS); + assertNotNull(asc.getRemoteAddress()); + + // Accept & write data + final int messageSize = NON_BLOCKING_MESSAGE_SIZE; + ByteBuffer sendData = createTestByteBuffer(messageSize, false); + Socket sss = ss.accept(); + // Small message, won't block on write + sss.getOutputStream().write(sendData.array()); + + // Shutdown input, expect -1 from read + asc.shutdownInput(); + ByteBuffer receivedData = createTestByteBuffer(messageSize, false); + + // We did write something into the socket, #shutdownInput javadocs + // say that "...effect on an outstanding read operation is system dependent and + // therefore not specified...". It looks like on android/linux the data in + // received buffer is discarded. + assertEquals(-1, (int)asc.read(receivedData).get(1000, TimeUnit.MILLISECONDS)); + assertEquals(-1, (int)asc.read(receivedData).get(1000, TimeUnit.MILLISECONDS)); + + // But we can still write! + assertEquals(32, (int)asc.write(sendData).get(1000, TimeUnit.MILLISECONDS)); + byte[] readArray = new byte[32]; + assertEquals(32, sss.getInputStream().read(readArray)); + assertTrue(Arrays.equals(sendData.array(), readArray)); + + // Shutdown output, expect ClosedChannelException from write + asc.shutdownOutput(); + try { + assertEquals(-1, (int)asc.write(sendData).get(1000, TimeUnit.MILLISECONDS)); + fail(); + } catch(ExecutionException expected) { + assertTrue(expected.getCause() instanceof ClosedChannelException); + } + try { + assertEquals(-1, (int)asc.write(sendData).get(1000, TimeUnit.MILLISECONDS)); + fail(); + } catch(ExecutionException expected) { + assertTrue(expected.getCause() instanceof ClosedChannelException); + } + + // shutdownInput() & shudownOutput() != closed, shocking! + assertNotNull(asc.getRemoteAddress()); + assertTrue(asc.isOpen()); + + asc.close(); + sss.close(); + ss.close(); + } + + public void test_options() throws Exception { + try (AsynchronousSocketChannel asc = AsynchronousSocketChannel.open()) { + + asc.setOption(StandardSocketOptions.SO_SNDBUF, 5000); + assertEquals(5000, (long) asc.getOption(StandardSocketOptions.SO_SNDBUF)); + + asc.setOption(StandardSocketOptions.SO_RCVBUF, 5000); + assertEquals(5000, (long) asc.getOption(StandardSocketOptions.SO_RCVBUF)); + + asc.setOption(StandardSocketOptions.SO_KEEPALIVE, true); + assertTrue(asc.getOption(StandardSocketOptions.SO_KEEPALIVE)); + + asc.setOption(StandardSocketOptions.SO_REUSEADDR, true); + assertTrue(asc.getOption(StandardSocketOptions.SO_REUSEADDR)); + + asc.setOption(StandardSocketOptions.TCP_NODELAY, true); + assertTrue(asc.getOption(StandardSocketOptions.TCP_NODELAY)); + } + } + + public void test_options_iae() throws Exception { + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + + try { + asc.setOption(StandardSocketOptions.IP_TOS, 5); + fail(); + } catch (UnsupportedOperationException expected) {} + + asc.close(); + } + + public void test_supportedOptions() throws Throwable { + AsynchronousSocketChannel assc = AsynchronousSocketChannel.open(); + + Set> supportedOptions = assc.supportedOptions(); + assertEquals(5, supportedOptions.size()); + + assertTrue(supportedOptions.contains(StandardSocketOptions.SO_REUSEADDR)); + assertTrue(supportedOptions.contains(StandardSocketOptions.SO_RCVBUF)); + assertTrue(supportedOptions.contains(StandardSocketOptions.SO_SNDBUF)); + assertTrue(supportedOptions.contains(StandardSocketOptions.SO_KEEPALIVE)); + assertTrue(supportedOptions.contains(StandardSocketOptions.TCP_NODELAY)); + + // supportedOptions should work after close according to spec + assc.close(); + supportedOptions = assc.supportedOptions(); + assertEquals(5, supportedOptions.size()); + } + + + public void test_group() throws Exception { + AsynchronousChannelProvider provider = + AsynchronousChannelProvider.provider(); + AsynchronousChannelGroup group = + provider.openAsynchronousChannelGroup(2, Executors.defaultThreadFactory()); + + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(group); + assertEquals(provider, asc.provider()); + asc.close(); + } + + private static ByteBuffer createTestByteBuffer(int size, boolean isDirect) { + return createTestByteBuffer(size, isDirect, 0); + } + + private static ByteBuffer createTestByteBuffer(int size, boolean isDirect, int contentOffset) { + ByteBuffer bb = isDirect ? ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size); + for (int i = 0; i < size; ++i) { + bb.put(i, (byte)(i + contentOffset)); + } + return bb; + } + + public void test_closeGuardSupport() throws IOException { + try (AsynchronousSocketChannel asc = AsynchronousSocketChannel.open()) { + leakageDetectorRule.assertUnreleasedResourceCount(asc, 1); + } + } + + public void test_closeGuardSupport_group() throws IOException { + AsynchronousChannelProvider provider = + AsynchronousChannelProvider.provider(); + AsynchronousChannelGroup group = + provider.openAsynchronousChannelGroup(2, Executors.defaultThreadFactory()); + + try (AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(group)) { + leakageDetectorRule.assertUnreleasedResourceCount(asc, 1); + } + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java b/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java index 4bbf3a9a0..c867250ac 100644 --- a/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java +++ b/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java @@ -17,12 +17,25 @@ package libcore.java.nio.channels; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousByteChannel; import java.nio.channels.Channels; import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.Pipe; import java.nio.channels.WritableByteChannel; +import java.util.Arrays; +import java.util.concurrent.Future; import junit.framework.TestCase; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import static org.mockito.Mockito.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public final class ChannelsTest extends TestCase { @@ -56,5 +69,60 @@ private Pipe.SourceChannel createNonBlockingChannel(byte[] content) throws IOExc sourceChannel.configureBlocking(false); return sourceChannel; } -} + public void testInputStreamAsynchronousByteChannel() throws Exception { + AsynchronousByteChannel abc = mock(AsynchronousByteChannel.class); + InputStream is = Channels.newInputStream(abc); + Future result = mock(Future.class); + ArgumentCaptor bbCaptor = ArgumentCaptor.forClass(ByteBuffer.class); + final byte[] bytesRead = new byte[10]; + + when(abc.read(bbCaptor.capture())).thenReturn(result); + when(result.get()).thenAnswer( + new Answer() { + public Integer answer(InvocationOnMock invocation) { + ByteBuffer bb = bbCaptor.getValue(); + assertEquals(bytesRead.length, bb.remaining()); + // Write '7' bytes + bb.put(new byte[] {0, 1, 2, 3, 4, 5, 6}); + return 7; + } + }); + + assertEquals(7, is.read(bytesRead)); + // Only 7 bytes of data should be written into the buffer + byte[] bytesExpected = new byte[] { 0, 1, 2, 3, 4, 5, 6, 0, 0, 0 }; + assertTrue(Arrays.equals(bytesExpected, bytesRead)); + + Mockito.verify(abc).read(isA(ByteBuffer.class)); + Mockito.verify(result).get(); + } + + public void testOutputStreamAsynchronousByteChannel() throws Exception { + AsynchronousByteChannel abc = mock(AsynchronousByteChannel.class); + OutputStream os = Channels.newOutputStream(abc); + Future result = mock(Future.class); + ArgumentCaptor bbCaptor = ArgumentCaptor.forClass(ByteBuffer.class); + final byte[] data = "world".getBytes(); + + when(abc.write(bbCaptor.capture())).thenReturn(result); + when(result.get()).thenAnswer( + new Answer() { + public Integer answer(InvocationOnMock invocation) { + ByteBuffer bb = bbCaptor.getValue(); + assertEquals(data.length, bb.remaining()); + byte[] readData = new byte[data.length]; + // Read the whole thing + bb.get(readData); + assertTrue(Arrays.equals(data, readData)); + return data.length; + } + }); + + os.write(data); + + Mockito.verify(abc).write(isA(ByteBuffer.class)); + Mockito.verify(result).get(); + } + +} diff --git a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java new file mode 100644 index 000000000..ae45c0da5 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java @@ -0,0 +1,1250 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.channels; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.MembershipKey; +import java.util.ArrayList; +import java.util.Enumeration; + +import libcore.io.IoBridge; + +import static android.system.OsConstants.POLLIN; + +/** + * Tests associated with multicast behavior of DatagramChannel. + * These tests require IPv6 multicasting enabled network. + */ +public class DatagramChannelMulticastTest extends TestCase { + + private static InetAddress lookup(String s) { + try { + return InetAddress.getByName(s); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + // These IP addresses aren't inherently "good" or "bad"; they're just used like that. + // We use the "good" addresses for our actual group, and the "bad" addresses are for + // a group that we won't actually set up. + private static final InetAddress GOOD_MULTICAST_IPv4 = lookup("239.255.0.1"); + private static final InetAddress BAD_MULTICAST_IPv4 = lookup("239.255.0.2"); + private static final InetAddress GOOD_MULTICAST_IPv6 = lookup("ff05::7:7"); + private static final InetAddress BAD_MULTICAST_IPv6 = lookup("ff05::7:8"); + + // Special addresses. + private static final InetAddress WILDCARD_IPv4 = lookup("0.0.0.0"); + private static final InetAddress WILDCARD_IPv6 = lookup("::"); + + // Arbitrary unicast addresses. Used when the value doesn't actually matter. e.g. for source + // filters. + private static final InetAddress UNICAST_IPv4_1 = lookup("192.168.1.1"); + private static final InetAddress UNICAST_IPv4_2 = lookup("192.168.1.2"); + private static final InetAddress UNICAST_IPv6_1 = lookup("2001:db8::1"); + private static final InetAddress UNICAST_IPv6_2 = lookup("2001:db8::2"); + + private NetworkInterface ipv4NetworkInterface; + private NetworkInterface ipv6NetworkInterface; + private NetworkInterface loopbackInterface; + + private boolean supportsMulticast; + + @Override + protected void setUp() throws Exception { + // The loopback interface isn't actually useful for sending/receiving multicast messages + // but it can be used as a dummy for tests where that does not matter. + loopbackInterface = NetworkInterface.getByInetAddress(InetAddress.getLoopbackAddress()); + assertNotNull(loopbackInterface); + assertTrue(loopbackInterface.isLoopback()); + assertFalse(loopbackInterface.supportsMulticast()); + + Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + + // Determine if the device is marked to support multicast or not. If this propery is not + // set we assume the device has an interface capable of supporting multicast. + supportsMulticast = Boolean.parseBoolean( + System.getProperty("android.cts.device.multicast", "true")); + if (!supportsMulticast) { + return; + } + + while (interfaces.hasMoreElements() + && (ipv4NetworkInterface == null || ipv6NetworkInterface == null)) { + NetworkInterface nextInterface = interfaces.nextElement(); + if (willWorkForMulticast(nextInterface)) { + Enumeration addresses = nextInterface.getInetAddresses(); + while (addresses.hasMoreElements()) { + final InetAddress nextAddress = addresses.nextElement(); + if (nextAddress instanceof Inet6Address && ipv6NetworkInterface == null) { + ipv6NetworkInterface = nextInterface; + } else if (nextAddress instanceof Inet4Address + && ipv4NetworkInterface == null) { + ipv4NetworkInterface = nextInterface; + } + } + } + } + + if (ipv4NetworkInterface == null) { + fail("Test environment must have at least one network interface capable of IPv4" + + " multicast"); + } + if (ipv6NetworkInterface == null) { + fail("Test environment must have at least one network interface capable of IPv6" + + " multicast"); + } + } + + public void test_open() throws IOException { + DatagramChannel dc = DatagramChannel.open(); + + // Unlike MulticastSocket, DatagramChannel has SO_REUSEADDR set to false by default. + assertFalse(dc.getOption(StandardSocketOptions.SO_REUSEADDR)); + + assertNull(dc.getLocalAddress()); + assertTrue(dc.isOpen()); + assertFalse(dc.isConnected()); + } + + public void test_bind_null() throws Exception { + DatagramChannel dc = createReceiverChannel(); + assertNotNull(dc.getLocalAddress()); + assertTrue(dc.isOpen()); + assertFalse(dc.isConnected()); + + dc.close(); + try { + dc.getLocalAddress(); + fail(); + } catch (ClosedChannelException expected) { + } + assertFalse(dc.isOpen()); + assertFalse(dc.isConnected()); + } + + public void test_joinAnySource_afterClose() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + dc.close(); + try { + dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + fail(); + } catch (ClosedChannelException expected) { + } + } + + public void test_joinAnySource_nullGroupAddress() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(null, ipv4NetworkInterface); + fail(); + } catch (NullPointerException expected) { + } + dc.close(); + } + + public void test_joinAnySource_nullNetworkInterface() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(GOOD_MULTICAST_IPv4, null); + fail(); + } catch (NullPointerException expected) { + } + dc.close(); + } + + public void test_joinAnySource_nonMulticastGroupAddress_IPv4() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(UNICAST_IPv4_1, ipv4NetworkInterface); + fail(); + } catch (IllegalArgumentException expected) { + } + dc.close(); + } + + public void test_joinAnySource_nonMulticastGroupAddress_IPv6() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(UNICAST_IPv6_1, ipv6NetworkInterface); + fail(); + } catch (IllegalArgumentException expected) { + } + dc.close(); + } + + public void test_joinAnySource_IPv4() throws Exception { + test_joinAnySource(GOOD_MULTICAST_IPv4, BAD_MULTICAST_IPv4, ipv4NetworkInterface); + } + + public void test_joinAnySource_IPv6() throws Exception { + test_joinAnySource(GOOD_MULTICAST_IPv6, BAD_MULTICAST_IPv6, ipv6NetworkInterface); + } + + private void test_joinAnySource(InetAddress group, InetAddress group2, + NetworkInterface networkInterface) throws Exception { + if (!supportsMulticast) { + return; + } + // Set up a receiver join the group on ipv4NetworkInterface + DatagramChannel receiverChannel = createReceiverChannel(); + InetSocketAddress localAddress = (InetSocketAddress) receiverChannel.getLocalAddress(); + receiverChannel.join(group, networkInterface); + + String msg = "Hello World"; + sendMulticastMessage(group, localAddress.getPort(), msg); + + // now verify that we received the data as expected + ByteBuffer recvBuffer = ByteBuffer.allocate(100); + SocketAddress sourceAddress = receiverChannel.receive(recvBuffer); + assertNotNull(sourceAddress); + assertEquals(msg, new String(recvBuffer.array(), 0, recvBuffer.position())); + + // now verify that we didn't receive the second message + String msg2 = "Hello World - Different Group"; + sendMulticastMessage(group2, localAddress.getPort(), msg2); + recvBuffer.position(0); + SocketAddress sourceAddress2 = receiverChannel.receive(recvBuffer); + assertNull(sourceAddress2); + + receiverChannel.close(); + } + + public void test_joinAnySource_processLimit() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + for (byte i = 1; i <= 25; i++) { + InetAddress groupAddress = Inet4Address.getByName("239.255.0." + i); + try { + dc.join(groupAddress, ipv4NetworkInterface); + } catch (SocketException e) { + // There is a limit, that's ok according to the RI docs. For this test a lower bound of 20 + // is used, which appears to be the default linux limit. + // See /proc/sys/net/ipv4/igmp_max_memberships + assertTrue(i > 20); + break; + } + } + + dc.close(); + } + + public void test_joinAnySource_blockLimit() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey key = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + for (byte i = 1; i <= 15; i++) { + InetAddress sourceAddress = Inet4Address.getByName("10.0.0." + i); + try { + key.block(sourceAddress); + } catch (SocketException e) { + // There is a limit, that's ok according to the RI docs. For this test a lower bound of 10 + // is used, which appears to be the default linux limit. + // See /proc/sys/net/ipv4/igmp_max_msf + assertTrue(i > 10); + break; + } + } + + dc.close(); + } + + /** Confirms that calling join() does not cause an implicit bind() to take place. */ + public void test_joinAnySource_doesNotCauseBind() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = DatagramChannel.open(); + dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + assertNull(dc.getLocalAddress()); + + dc.close(); + } + + public void test_joinAnySource_networkInterfaces() throws Exception { + if (!supportsMulticast) { + return; + } + // Check that we can join on specific interfaces and that we only receive if data is + // received on that interface. This test is only really useful on devices with multiple + // non-loopback interfaces. + + ArrayList realInterfaces = new ArrayList(); + Enumeration theInterfaces = NetworkInterface.getNetworkInterfaces(); + while (theInterfaces.hasMoreElements()) { + NetworkInterface thisInterface = theInterfaces.nextElement(); + if (thisInterface.getInetAddresses().hasMoreElements()) { + realInterfaces.add(thisInterface); + } + } + + for (int i = 0; i < realInterfaces.size(); i++) { + NetworkInterface thisInterface = realInterfaces.get(i); + if (!thisInterface.supportsMulticast()) { + // Skip interfaces that do not support multicast - there's no point in proving + // they cannot send / receive multicast messages. + continue; + } + + // get the first address on the interface + + // start server which is joined to the group and has + // only asked for packets on this interface + Enumeration addresses = thisInterface.getInetAddresses(); + + NetworkInterface sendingInterface = null; + InetAddress group = null; + if (addresses.hasMoreElements()) { + InetAddress firstAddress = addresses.nextElement(); + if (firstAddress instanceof Inet4Address) { + group = GOOD_MULTICAST_IPv4; + sendingInterface = ipv4NetworkInterface; + } else { + // if this interface only seems to support IPV6 addresses + group = GOOD_MULTICAST_IPv6; + sendingInterface = ipv6NetworkInterface; + } + } + + DatagramChannel dc = createReceiverChannel(); + InetSocketAddress localAddress = (InetSocketAddress) dc.getLocalAddress(); + dc.join(group, thisInterface); + + // Now send out a package on sendingInterface. We should only see the packet if we send + // it on the same interface we are listening on (thisInterface). + String msg = "Hello World - Again" + thisInterface.getName(); + sendMulticastMessage(group, localAddress.getPort(), msg, sendingInterface); + + ByteBuffer recvBuffer = ByteBuffer.allocate(100); + SocketAddress sourceAddress = dc.receive(recvBuffer); + if (thisInterface.equals(sendingInterface)) { + assertEquals(msg, new String(recvBuffer.array(), 0, recvBuffer.position())); + } else { + assertNull(sourceAddress); + } + + dc.close(); + } + } + + /** Confirms that the scope of each membership is network interface-level. */ + public void test_join_canMixTypesOnDifferentInterfaces() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = DatagramChannel.open(); + MembershipKey membershipKey1 = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + MembershipKey membershipKey2 = dc.join(GOOD_MULTICAST_IPv4, loopbackInterface, UNICAST_IPv4_1); + assertNotSame(membershipKey1, membershipKey2); + + dc.close(); + } + + + private DatagramChannel createReceiverChannel() throws Exception { + DatagramChannel dc = DatagramChannel.open(); + dc.bind(null /* leave the OS to determine the port, and use the wildcard address */); + configureChannelForReceiving(dc); + return dc; + } + + public void test_joinAnySource_multiple_joins_IPv4() + throws Exception { + test_joinAnySource_multiple_joins(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + } + + public void test_joinAnySource_multiple_joins_IPv6() + throws Exception { + test_joinAnySource_multiple_joins(GOOD_MULTICAST_IPv6, ipv6NetworkInterface); + } + + private void test_joinAnySource_multiple_joins(InetAddress group, + NetworkInterface networkInterface) throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + + MembershipKey membershipKey1 = dc.join(group, networkInterface); + + MembershipKey membershipKey2 = dc.join(group, loopbackInterface); + assertFalse(membershipKey1.equals(membershipKey2)); + + MembershipKey membershipKey1_2 = dc.join(group, networkInterface); + assertEquals(membershipKey1, membershipKey1_2); + + dc.close(); + } + + public void test_joinAnySource_multicastLoopOption_IPv4() throws Exception { + test_joinAnySource_multicastLoopOption(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + } + + public void test_multicastLoopOption_IPv6() throws Exception { + test_joinAnySource_multicastLoopOption(GOOD_MULTICAST_IPv6, ipv6NetworkInterface); + } + + private void test_joinAnySource_multicastLoopOption(InetAddress group, + NetworkInterface networkInterface) throws Exception { + if (!supportsMulticast) { + return; + } + final String message = "Hello, world!"; + + DatagramChannel dc = createReceiverChannel(); + dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true /* enable loop */); + configureChannelForReceiving(dc); + dc.join(group, networkInterface); + + InetSocketAddress localAddress = (InetSocketAddress) dc.getLocalAddress(); + + // send the datagram + byte[] sendData = message.getBytes(); + ByteBuffer sendBuffer = ByteBuffer.wrap(sendData); + dc.send(sendBuffer, new InetSocketAddress(group, localAddress.getPort())); + + // receive the datagram + ByteBuffer recvBuffer = ByteBuffer.allocate(100); + SocketAddress sourceAddress = dc.receive(recvBuffer); + assertNotNull(sourceAddress); + + String recvMessage = new String(recvBuffer.array(), 0, recvBuffer.position()); + assertEquals(message, recvMessage); + + // Turn off loop + dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, false /* enable loopback */); + + // send another datagram + recvBuffer.position(0); + ByteBuffer sendBuffer2 = ByteBuffer.wrap(sendData); + dc.send(sendBuffer2, new InetSocketAddress(group, localAddress.getPort())); + + SocketAddress sourceAddress2 = dc.receive(recvBuffer); + assertNull(sourceAddress2); + + dc.close(); + } + + public void testMembershipKeyAccessors_IPv4() throws Exception { + testMembershipKeyAccessors(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + } + + public void testMembershipKeyAccessors_IPv6() throws Exception { + testMembershipKeyAccessors(GOOD_MULTICAST_IPv6, ipv6NetworkInterface); + } + + private void testMembershipKeyAccessors(InetAddress group, + NetworkInterface networkInterface) throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + + MembershipKey key = dc.join(group, networkInterface); + assertSame(dc, key.channel()); + assertSame(group, key.group()); + assertTrue(key.isValid()); + assertSame(networkInterface, key.networkInterface()); + assertNull(key.sourceAddress()); + dc.close(); + } + + public void test_dropAnySource_twice_IPv4() throws Exception { + test_dropAnySource_twice(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + } + + public void test_dropAnySource_twice_IPv6() throws Exception { + test_dropAnySource_twice(GOOD_MULTICAST_IPv6, ipv6NetworkInterface); + } + + private void test_dropAnySource_twice(InetAddress group, + NetworkInterface networkInterface) throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(group, networkInterface); + + assertTrue(membershipKey.isValid()); + membershipKey.drop(); + assertFalse(membershipKey.isValid()); + + // Try to leave a group we are no longer a member of - should do nothing. + membershipKey.drop(); + + dc.close(); + } + + public void test_close_invalidatesMembershipKey() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + + assertTrue(membershipKey.isValid()); + + dc.close(); + + assertFalse(membershipKey.isValid()); + } + + public void test_block_null() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + try { + membershipKey.block(null); + fail(); + } catch (NullPointerException expected) { + } + + dc.close(); + } + + public void test_block_mixedAddressTypes_IPv4() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + try { + membershipKey.block(UNICAST_IPv6_1); + fail(); + } catch (IllegalArgumentException expected) { + } + + dc.close(); + } + + public void test_block_mixedAddressTypes_IPv6() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv6, ipv6NetworkInterface); + try { + membershipKey.block(UNICAST_IPv4_1); + fail(); + } catch (IllegalArgumentException expected) { + } + + dc.close(); + } + + public void test_block_cannotBlockWithSourceSpecificMembership() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, UNICAST_IPv4_1); + try { + membershipKey.block(UNICAST_IPv4_2); + fail(); + } catch (IllegalStateException expected) { + } + + dc.close(); + } + + public void test_block_multipleBlocksIgnored() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + membershipKey.block(UNICAST_IPv4_1); + + MembershipKey membershipKey2 = membershipKey.block(UNICAST_IPv4_1); + assertSame(membershipKey2, membershipKey); + + dc.close(); + } + + public void test_block_wildcardAddress() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + try { + membershipKey.block(WILDCARD_IPv4); + fail(); + } catch (IllegalArgumentException expected) { + } + + dc.close(); + } + + public void test_unblock_multipleUnblocksFail() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + + try { + membershipKey.unblock(UNICAST_IPv4_1); + fail(); + } catch (IllegalStateException expected) { + } + + assertTrue(membershipKey.isValid()); + + membershipKey.block(UNICAST_IPv4_1); + membershipKey.unblock(UNICAST_IPv4_1); + + try { + membershipKey.unblock(UNICAST_IPv4_1); + fail(); + } catch (IllegalStateException expected) { + } + + dc.close(); + } + + public void test_unblock_null() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + membershipKey.block(UNICAST_IPv4_1); + + try { + membershipKey.unblock(null); + fail(); + } catch (IllegalStateException expected) { + // Either of these exceptions are fine + } catch (NullPointerException expected) { + // Either of these exception are fine + } + + dc.close(); + } + + public void test_unblock_mixedAddressTypes_IPv4() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface); + try { + membershipKey.unblock(UNICAST_IPv6_1); + fail(); + } catch (IllegalStateException expected) { + // Either of these exceptions are fine + } catch (IllegalArgumentException expected) { + // Either of these exceptions are fine + } + + dc.close(); + } + + public void test_unblock_mixedAddressTypes_IPv6() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv6, ipv6NetworkInterface); + try { + membershipKey.unblock(UNICAST_IPv4_1); + fail(); + } catch (IllegalStateException expected) { + // Either of these exceptions are fine + } catch (IllegalArgumentException expected) { + // Either of these exceptions are fine + } + + dc.close(); + } + + /** Checks that block() works when the receiver is bound to the multicast group address */ + public void test_block_filtersAsExpected_groupBind_ipv4() throws Exception { + InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface); + test_block_filtersAsExpected( + ipv4LocalAddress /* senderBindAddress */, + GOOD_MULTICAST_IPv4 /* receiverBindAddress */, + GOOD_MULTICAST_IPv4 /* groupAddress */, + ipv4NetworkInterface); + } + + /** Checks that block() works when the receiver is bound to the multicast group address */ + public void test_block_filtersAsExpected_groupBind_ipv6() throws Exception { + InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface); + test_block_filtersAsExpected( + ipv6LocalAddress /* senderBindAddress */, + GOOD_MULTICAST_IPv6 /* receiverBindAddress */, + GOOD_MULTICAST_IPv6 /* groupAddress */, + ipv6NetworkInterface); + } + + /** Checks that block() works when the receiver is bound to the "any" address */ + public void test_block_filtersAsExpected_anyBind_ipv4() throws Exception { + InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface); + test_block_filtersAsExpected( + ipv4LocalAddress /* senderBindAddress */, + WILDCARD_IPv4 /* receiverBindAddress */, + GOOD_MULTICAST_IPv4 /* groupAddress */, + ipv4NetworkInterface); + } + + /** Checks that block() works when the receiver is bound to the "any" address */ + public void test_block_filtersAsExpected_anyBind_ipv6() throws Exception { + InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface); + test_block_filtersAsExpected( + ipv6LocalAddress /* senderBindAddress */, + WILDCARD_IPv6 /* receiverBindAddress */, + GOOD_MULTICAST_IPv6 /* groupAddress */, + ipv6NetworkInterface); + } + + private void test_block_filtersAsExpected( + InetAddress senderBindAddress, InetAddress receiverBindAddress, + InetAddress groupAddress, NetworkInterface networkInterface) + throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel sendingChannel = DatagramChannel.open(); + // In order to block a sender the sender's address must be known. The sendingChannel is + // explicitly bound to a known, non-loopback address. + sendingChannel.bind(new InetSocketAddress(senderBindAddress, 0)); + InetSocketAddress sendingAddress = (InetSocketAddress) sendingChannel.getLocalAddress(); + + DatagramChannel receivingChannel = DatagramChannel.open(); + configureChannelForReceiving(receivingChannel); + receivingChannel.bind( + new InetSocketAddress(receiverBindAddress, 0) /* local port left to the OS to determine */); + InetSocketAddress localReceivingAddress = + (InetSocketAddress) receivingChannel.getLocalAddress(); + InetSocketAddress groupSocketAddress = + new InetSocketAddress(groupAddress, localReceivingAddress.getPort()); + MembershipKey membershipKey = + receivingChannel.join(groupSocketAddress.getAddress(), networkInterface); + + ByteBuffer receiveBuffer = ByteBuffer.allocate(10); + + // Send a message. It should be received. + String msg1 = "Hello1"; + sendMessage(sendingChannel, msg1, groupSocketAddress); + IoBridge.poll(receivingChannel.socket().getFileDescriptor$(), POLLIN, 1000); + InetSocketAddress sourceAddress1 = (InetSocketAddress) receivingChannel.receive(receiveBuffer); + assertEquals(sourceAddress1, sendingAddress); + assertEquals(msg1, new String(receiveBuffer.array(), 0, receiveBuffer.position())); + + // Now block the sender + membershipKey.block(sendingAddress.getAddress()); + + // Send a message. It should be filtered. + String msg2 = "Hello2"; + sendMessage(sendingChannel, msg2, groupSocketAddress); + try { + IoBridge.poll(receivingChannel.socket().getFileDescriptor$(), POLLIN, 1000); + fail(); + } catch (SocketTimeoutException expected) { } + receiveBuffer.position(0); + InetSocketAddress sourceAddress2 = (InetSocketAddress) receivingChannel.receive(receiveBuffer); + assertNull(sourceAddress2); + + // Now unblock the sender + membershipKey.unblock(sendingAddress.getAddress()); + + // Send a message. It should be received. + String msg3 = "Hello3"; + sendMessage(sendingChannel, msg3, groupSocketAddress); + IoBridge.poll(receivingChannel.socket().getFileDescriptor$(), POLLIN, 1000); + receiveBuffer.position(0); + InetSocketAddress sourceAddress3 = (InetSocketAddress) receivingChannel.receive(receiveBuffer); + assertEquals(sourceAddress3, sendingAddress); + assertEquals(msg3, new String(receiveBuffer.array(), 0, receiveBuffer.position())); + + sendingChannel.close(); + receivingChannel.close(); + } + + public void test_joinSourceSpecific_nullGroupAddress() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(null, ipv4NetworkInterface, UNICAST_IPv4_1); + fail(); + } catch (NullPointerException expected) { + } + dc.close(); + } + + public void test_joinSourceSpecific_afterClose() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + dc.close(); + try { + dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, UNICAST_IPv4_1); + fail(); + } catch (ClosedChannelException expected) { + } + } + + public void test_joinSourceSpecific_nullNetworkInterface() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(GOOD_MULTICAST_IPv4, null, UNICAST_IPv4_1); + fail(); + } catch (NullPointerException expected) { + } + dc.close(); + } + + public void test_joinSourceSpecific_nonMulticastGroupAddress_IPv4() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(UNICAST_IPv4_1, ipv4NetworkInterface, UNICAST_IPv4_1); + fail(); + } catch (IllegalArgumentException expected) { + } + dc.close(); + } + + public void test_joinSourceSpecific_nonMulticastGroupAddress_IPv6() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(UNICAST_IPv6_1, ipv6NetworkInterface, UNICAST_IPv6_1); + fail(); + } catch (IllegalArgumentException expected) { + } + dc.close(); + } + + public void test_joinSourceSpecific_nullSourceAddress_IPv4() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, null); + fail(); + } catch (NullPointerException expected) { + } + dc.close(); + } + + public void test_joinSourceSpecific_nullSourceAddress_IPv6() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(GOOD_MULTICAST_IPv6, ipv6NetworkInterface, null); + fail(); + } catch (NullPointerException expected) { + } + dc.close(); + } + + public void test_joinSourceSpecific_mixedAddressTypes() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, UNICAST_IPv6_1); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + dc.join(GOOD_MULTICAST_IPv6, ipv6NetworkInterface, UNICAST_IPv4_1); + fail(); + } catch (IllegalArgumentException expected) { + } + dc.close(); + } + + public void test_joinSourceSpecific_nonUnicastSourceAddress_IPv4() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, BAD_MULTICAST_IPv4); + fail(); + } catch (IllegalArgumentException expected) { + } + dc.close(); + } + + public void test_joinSourceSpecific_nonUniicastSourceAddress_IPv6() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + try { + dc.join(GOOD_MULTICAST_IPv6, ipv6NetworkInterface, BAD_MULTICAST_IPv6); + fail(); + } catch (IllegalArgumentException expected) { + } + dc.close(); + } + + public void test_joinSourceSpecific_multipleSourceAddressLimit() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + for (byte i = 1; i <= 20; i++) { + InetAddress sourceAddress = Inet4Address.getByAddress(new byte[] { 10, 0, 0, i}); + try { + dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, sourceAddress); + } catch (SocketException e) { + // There is a limit, that's ok according to the RI docs. For this test a lower bound of 10 + // is used, which appears to be the default linux limit. See /proc/sys/net/ipv4/igmp_max_msf + assertTrue(i > 10); + break; + } + } + + dc.close(); + } + + /** + * Checks that a source-specific join() works when the receiver is bound to the multicast group + * address + */ + public void test_joinSourceSpecific_null() throws Exception { + InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface); + test_joinSourceSpecific( + ipv4LocalAddress /* senderBindAddress */, + GOOD_MULTICAST_IPv4 /* receiverBindAddress */, + GOOD_MULTICAST_IPv4 /* groupAddress */, + UNICAST_IPv4_1 /* badSenderAddress */, + ipv4NetworkInterface); + } + + /** + * Checks that a source-specific join() works when the receiver is bound to the multicast group + * address + */ + public void test_joinSourceSpecific_groupBind_ipv4() throws Exception { + InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface); + test_joinSourceSpecific( + ipv4LocalAddress /* senderBindAddress */, + GOOD_MULTICAST_IPv4 /* receiverBindAddress */, + GOOD_MULTICAST_IPv4 /* groupAddress */, + UNICAST_IPv4_1 /* badSenderAddress */, + ipv6NetworkInterface); + } + + /** + * Checks that a source-specific join() works when the receiver is bound to the multicast group + * address + */ + public void test_joinSourceSpecific_groupBind_ipv6() throws Exception { + InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface); + test_joinSourceSpecific( + ipv6LocalAddress /* senderBindAddress */, + GOOD_MULTICAST_IPv6 /* receiverBindAddress */, + GOOD_MULTICAST_IPv6 /* groupAddress */, + UNICAST_IPv6_1 /* badSenderAddress */, + ipv6NetworkInterface); + } + + /** Checks that a source-specific join() works when the receiver is bound to the "any" address */ + public void test_joinSourceSpecific_anyBind_ipv4() throws Exception { + InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface); + test_joinSourceSpecific( + ipv4LocalAddress /* senderBindAddress */, + WILDCARD_IPv4 /* receiverBindAddress */, + GOOD_MULTICAST_IPv4 /* groupAddress */, + UNICAST_IPv4_1 /* badSenderAddress */, + ipv4NetworkInterface); + } + + /** Checks that a source-specific join() works when the receiver is bound to the "any" address */ + public void test_joinSourceSpecific_anyBind_ipv6() throws Exception { + InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface); + test_joinSourceSpecific( + ipv6LocalAddress /* senderBindAddress */, + WILDCARD_IPv6 /* receiverBindAddress */, + GOOD_MULTICAST_IPv6 /* groupAddress */, + UNICAST_IPv6_1 /* badSenderAddress */, + ipv6NetworkInterface); + } + + /** + * Checks that the source-specific membership is correctly source-filtering. + * + * @param senderBindAddress the address to bind the sender socket to + * @param receiverBindAddress the address to bind the receiver socket to + * @param groupAddress the group address to join + * @param badSenderAddress a unicast address to join to perform a negative test + */ + private void test_joinSourceSpecific( + InetAddress senderBindAddress, InetAddress receiverBindAddress, InetAddress groupAddress, + InetAddress badSenderAddress, NetworkInterface networkInterface) + throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel sendingChannel = DatagramChannel.open(); + // In order to be source-specific the sender's address must be known. The sendingChannel is + // explicitly bound to a known, non-loopback address. + sendingChannel.bind(new InetSocketAddress(senderBindAddress, 0)); + InetSocketAddress sendingAddress = (InetSocketAddress) sendingChannel.getLocalAddress(); + + DatagramChannel receivingChannel = DatagramChannel.open(); + receivingChannel.bind( + new InetSocketAddress(receiverBindAddress, 0) /* local port left to the OS to determine */); + configureChannelForReceiving(receivingChannel); + + InetSocketAddress localReceivingAddress = + (InetSocketAddress) receivingChannel.getLocalAddress(); + InetSocketAddress groupSocketAddress = + new InetSocketAddress(groupAddress, localReceivingAddress.getPort()); + MembershipKey membershipKey1 = receivingChannel + .join(groupSocketAddress.getAddress(), networkInterface, senderBindAddress); + + ByteBuffer receiveBuffer = ByteBuffer.allocate(10); + + // Send a message. It should be received. + String msg1 = "Hello1"; + sendMessage(sendingChannel, msg1, groupSocketAddress); + InetSocketAddress sourceAddress1 = (InetSocketAddress) receivingChannel.receive(receiveBuffer); + assertEquals(sourceAddress1, sendingAddress); + assertEquals(msg1, new String(receiveBuffer.array(), 0, receiveBuffer.position())); + + membershipKey1.drop(); + + receivingChannel.join(groupSocketAddress.getAddress(), networkInterface, badSenderAddress); + + // Send a message. It should not be received. + String msg2 = "Hello2"; + sendMessage(sendingChannel, msg2, groupSocketAddress); + InetSocketAddress sourceAddress2 = (InetSocketAddress) receivingChannel.receive(receiveBuffer); + assertNull(sourceAddress2); + + receivingChannel.close(); + sendingChannel.close(); + } + + public void test_dropSourceSpecific_twice_IPv4() throws Exception { + test_dropSourceSpecific_twice( + GOOD_MULTICAST_IPv4 /* groupAddress */, UNICAST_IPv4_1 /* sourceAddress */, + ipv4NetworkInterface); + } + + public void test_dropSourceSpecific_twice_IPv6() throws Exception { + test_dropSourceSpecific_twice( + GOOD_MULTICAST_IPv6 /* groupAddress */, UNICAST_IPv6_1 /* sourceAddress */, + ipv6NetworkInterface); + } + + private void test_dropSourceSpecific_twice(InetAddress groupAddress, InetAddress sourceAddress, + NetworkInterface networkInterface) + throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(groupAddress, networkInterface, sourceAddress); + + assertTrue(membershipKey.isValid()); + membershipKey.drop(); + assertFalse(membershipKey.isValid()); + + // Try to leave a group we are no longer a member of - should do nothing. + membershipKey.drop(); + + dc.close(); + } + + public void test_dropSourceSpecific_sourceKeysAreIndependent_IPv4() throws Exception { + test_dropSourceSpecific_sourceKeysAreIndependent( + GOOD_MULTICAST_IPv4 /* groupAddress */, + UNICAST_IPv4_1 /* sourceAddress1 */, + UNICAST_IPv4_2 /* sourceAddress2 */, + ipv4NetworkInterface); + } + + public void test_dropSourceSpecific_sourceKeysAreIndependent_IPv6() throws Exception { + test_dropSourceSpecific_sourceKeysAreIndependent( + GOOD_MULTICAST_IPv6 /* groupAddress */, + UNICAST_IPv6_1 /* sourceAddress1 */, + UNICAST_IPv6_2 /* sourceAddress2 */, + ipv6NetworkInterface); + } + + private void test_dropSourceSpecific_sourceKeysAreIndependent( + InetAddress groupAddress, InetAddress sourceAddress1, InetAddress sourceAddress2, + NetworkInterface networkInterface) throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey1 = dc.join(groupAddress, networkInterface, sourceAddress1); + MembershipKey membershipKey2 = dc.join(groupAddress, networkInterface, sourceAddress2); + assertFalse(membershipKey1.equals(membershipKey2)); + assertTrue(membershipKey1.isValid()); + assertTrue(membershipKey2.isValid()); + + membershipKey1.drop(); + + assertFalse(membershipKey1.isValid()); + assertTrue(membershipKey2.isValid()); + + dc.close(); + } + + public void test_drop_keyBehaviorAfterDrop() throws Exception { + if (!supportsMulticast) { + return; + } + DatagramChannel dc = createReceiverChannel(); + MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, UNICAST_IPv4_1); + membershipKey.drop(); + assertFalse(membershipKey.isValid()); + + try { + membershipKey.block(UNICAST_IPv4_1); + fail(); + } catch (IllegalStateException expected) { + } + + try { + membershipKey.unblock(UNICAST_IPv4_1); + fail(); + } catch (IllegalStateException expected) { + } + + assertSame(dc, membershipKey.channel()); + assertSame(GOOD_MULTICAST_IPv4, membershipKey.group()); + assertSame(UNICAST_IPv4_1, membershipKey.sourceAddress()); + assertSame(ipv4NetworkInterface, membershipKey.networkInterface()); + } + + private static void configureChannelForReceiving(DatagramChannel receivingChannel) + throws Exception { + + // NOTE: At the time of writing setSoTimeout() has no effect in the RI, making these tests hang + // if the channel is in blocking mode. configureBlocking(false) is used instead and rely on the + // network to the local host being instantaneous. + // receivingChannel.socket().setSoTimeout(200); + // receivingChannel.configureBlocking(true); + receivingChannel.configureBlocking(false); + } + + private static boolean willWorkForMulticast(NetworkInterface iface) throws IOException { + return iface.isUp() + // Typically loopback interfaces do not support multicast, but they are ruled out + // explicitly here anyway. + && !iface.isLoopback() && iface.supportsMulticast() + && iface.getInetAddresses().hasMoreElements(); + } + + private static void sendMulticastMessage(InetAddress group, int port, String msg) + throws IOException { + sendMulticastMessage(group, port, msg, null /* networkInterface */); + } + + private static void sendMulticastMessage( + InetAddress group, int port, String msg, NetworkInterface sendingInterface) + throws IOException { + // Any datagram socket can send to a group. It does not need to have joined the group. + DatagramChannel dc = DatagramChannel.open(); + if (sendingInterface != null) { + // For some reason, if set, this must be set to a real (non-loopback) device for an IPv6 + // group, but can be loopback for an IPv4 group. + dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, sendingInterface); + } + sendMessage(dc, msg, new InetSocketAddress(group, port)); + dc.close(); + } + + private static void sendMessage( + DatagramChannel sendingChannel, String msg, InetSocketAddress targetAddress) + throws IOException { + + ByteBuffer sendBuffer = ByteBuffer.wrap(msg.getBytes()); + sendingChannel.send(sendBuffer, targetAddress); + } + + private static InetAddress getLocalIpv4Address(NetworkInterface networkInterface) { + for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) { + if (interfaceAddress.getAddress() instanceof Inet4Address) { + return interfaceAddress.getAddress(); + } + } + throw new AssertionFailedError("Unable to find local IPv4 address for " + networkInterface); + } + + private static InetAddress getLocalIpv6Address(NetworkInterface networkInterface) { + for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) { + if (interfaceAddress.getAddress() instanceof Inet6Address) { + return interfaceAddress.getAddress(); + } + } + throw new AssertionFailedError("Unable to find local IPv6 address for " + networkInterface); + } +} + diff --git a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java index f3bd7ff80..bc8c933f1 100644 --- a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java +++ b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java @@ -16,6 +16,8 @@ package libcore.java.nio.channels; +import org.junit.Rule; + import java.io.IOException; import java.net.BindException; import java.net.DatagramSocket; @@ -34,27 +36,34 @@ import java.nio.channels.UnsupportedAddressTypeException; import java.nio.channels.spi.SelectorProvider; import java.util.Enumeration; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; + +public class DatagramChannelTest extends TestCaseWithRules { + @Rule + public ResourceLeakageDetector.LeakageDetectorRule guardRule = + ResourceLeakageDetector.getRule(); -public class DatagramChannelTest extends junit.framework.TestCase { public void test_read_intoReadOnlyByteArrays() throws Exception { ByteBuffer readOnly = ByteBuffer.allocate(1).asReadOnlyBuffer(); - DatagramSocket ds = new DatagramSocket(0); - DatagramChannel dc = DatagramChannel.open(); - dc.connect(ds.getLocalSocketAddress()); - try { - dc.read(readOnly); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - dc.read(new ByteBuffer[] { readOnly }); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - dc.read(new ByteBuffer[] { readOnly }, 0, 1); - fail(); - } catch (IllegalArgumentException expected) { + try (DatagramSocket ds = new DatagramSocket(0); + DatagramChannel dc = DatagramChannel.open()) { + dc.connect(ds.getLocalSocketAddress()); + try { + dc.read(readOnly); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + dc.read(new ByteBuffer[] { readOnly }); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + dc.read(new ByteBuffer[] { readOnly }, 0, 1); + fail(); + } catch (IllegalArgumentException expected) { + } } } @@ -148,14 +157,13 @@ public void test_bind_IPv6() throws Exception { } private void test_bind(InetAddress bindAddress) throws IOException { - DatagramChannel dc = DatagramChannel.open(); - dc.socket().bind(new InetSocketAddress(bindAddress, 0)); - - InetSocketAddress actualAddress = (InetSocketAddress) dc.socket().getLocalSocketAddress(); - assertEquals(bindAddress, actualAddress.getAddress()); - assertTrue(actualAddress.getPort() > 0); + try (DatagramChannel dc = DatagramChannel.open()) { + dc.socket().bind(new InetSocketAddress(bindAddress, 0)); - dc.close(); + InetSocketAddress actualAddress = (InetSocketAddress) dc.socket().getLocalSocketAddress(); + assertEquals(bindAddress, actualAddress.getAddress()); + assertTrue(actualAddress.getPort() > 0); + } } public void test_setOption() throws Exception { @@ -179,9 +187,10 @@ public void test_setOption() throws Exception { // http://b/26292854 public void test_getFileDescriptor() throws Exception { - DatagramSocket socket = DatagramChannel.open().socket(); - socket.getReuseAddress(); - assertNotNull(socket.getFileDescriptor$()); + try (DatagramSocket socket = DatagramChannel.open().socket()) { + socket.getReuseAddress(); + assertNotNull(socket.getFileDescriptor$()); + } } public void test_bind() throws IOException { @@ -200,8 +209,8 @@ public void test_bind() throws IOException { socketAddress = new InetSocketAddress(Inet4Address.LOOPBACK, ((InetSocketAddress)(channel.getLocalAddress())).getPort()); - try { - DatagramChannel.open().bind(socketAddress); + try (DatagramChannel dc = DatagramChannel.open()){ + dc.bind(socketAddress); fail(); } catch (BindException expected) {} @@ -209,50 +218,59 @@ public void test_bind() throws IOException { socketAddress = new InetSocketAddress(Inet4Address.LOOPBACK, 0); try { channel.bind(socketAddress); + fail(); } catch (ClosedChannelException expected) {} } public void test_getRemoteAddress() throws IOException { InetSocketAddress socketAddress = new InetSocketAddress(Inet4Address.LOOPBACK, 0); - DatagramChannel clientChannel = DatagramChannel.open(); - DatagramChannel serverChannel = DatagramChannel.open(); - serverChannel.bind(socketAddress); + try (DatagramChannel clientChannel = DatagramChannel.open(); + DatagramChannel serverChannel = DatagramChannel.open()) { + serverChannel.bind(socketAddress); - assertNull(clientChannel.getRemoteAddress()); + assertNull(clientChannel.getRemoteAddress()); - clientChannel.connect(serverChannel.getLocalAddress()); - assertEquals(socketAddress.getAddress(), - ((InetSocketAddress)(clientChannel.getRemoteAddress())).getAddress()); - assertEquals(((InetSocketAddress)(serverChannel.getLocalAddress())).getPort(), - ((InetSocketAddress)(clientChannel.getRemoteAddress())).getPort()); + clientChannel.connect(serverChannel.getLocalAddress()); + assertEquals(socketAddress.getAddress(), + ((InetSocketAddress) (clientChannel.getRemoteAddress())).getAddress()); + assertEquals(((InetSocketAddress) (serverChannel.getLocalAddress())).getPort(), + ((InetSocketAddress) (clientChannel.getRemoteAddress())).getPort()); + } } public void test_open$java_net_ProtocolFamily() throws IOException { - DatagramChannel channel = DatagramChannel.open(StandardProtocolFamily.INET); - - channel.bind(new InetSocketAddress(Inet4Address.LOOPBACK, 0)); - assertEquals(SelectorProvider.provider(), channel.provider()); + try (DatagramChannel channel = DatagramChannel.open(StandardProtocolFamily.INET)) { + channel.bind(new InetSocketAddress(Inet4Address.LOOPBACK, 0)); + assertEquals(SelectorProvider.provider(), channel.provider()); + } - try { - // Should not support IPv6 Address - // InetSocketAddress(int) returns IPv6 ANY address - DatagramChannel.open(StandardProtocolFamily.INET).bind(new InetSocketAddress(0)); + // Should not support IPv6 Address + // InetSocketAddress(int) returns IPv6 ANY address + try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) { + dc.bind(new InetSocketAddress(0)); fail(); } catch (UnsupportedAddressTypeException expected) {} - DatagramChannel.open(StandardProtocolFamily.INET6).bind(new InetSocketAddress(0)); - - try { - DatagramChannel.open(MockProtocolFamily.MOCK); + try (DatagramChannel dc = DatagramChannel.open(MockProtocolFamily.MOCK)) { fail(); } catch (UnsupportedOperationException expected) {} - try { - DatagramChannel.open(null); + try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET6)) { + assertSame(dc, dc.bind(new InetSocketAddress(0))); + } + + // NullPointerException + try (DatagramChannel dc = DatagramChannel.open(null)) { fail(); } catch (NullPointerException expected) {} } + public void test_closeGuardSupport() throws IOException { + try(DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) { + guardRule.assertUnreleasedResourceCount(dc, 1); + } + } + private static InetAddress getNonLoopbackNetworkInterfaceAddress(boolean ipv4) throws IOException { Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { diff --git a/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java index 8ecf72d15..c3e46e7f8 100644 --- a/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java +++ b/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java @@ -16,6 +16,11 @@ package libcore.java.nio.channels; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.WRITE; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -24,9 +29,24 @@ import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.file.FileSystem; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.spi.FileSystemProvider; +import java.util.HashSet; +import java.util.Set; import libcore.io.IoUtils; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.LeakageDetectorRule; +import org.junit.Rule; + +public class FileChannelTest extends TestCaseWithRules { + + @Rule + public LeakageDetectorRule guardRule = ResourceLeakageDetector.getRule(); -public class FileChannelTest extends junit.framework.TestCase { public void testReadOnlyByteArrays() throws Exception { ByteBuffer readOnly = ByteBuffer.allocate(1).asReadOnlyBuffer(); File tmp = File.createTempFile("FileChannelTest", "tmp"); @@ -240,6 +260,23 @@ public void test_close_fromFileDescriptor() throws Exception { fosFromFd.close(); } + public void test_closeGuardSupport_open_without_append() throws IOException { + File tmpFile = File.createTempFile("file", "txt"); + try (FileInputStream fis = new FileInputStream(tmpFile)) { + try (FileChannel fc = fis.getChannel()) { + guardRule.assertUnreleasedResourceCount(fc, 1); + } + } + } + + public void test_closeGuardSupport_open_with_append() throws IOException { + File tmpFile = File.createTempFile("file", "txt"); + try (FileOutputStream fos = new FileOutputStream(tmpFile)) { + try (FileChannel fc = fos.getChannel()) { + guardRule.assertUnreleasedResourceCount(fc, 1); + } + } + } private static FileChannel createFileContainingBytes(byte[] bytes) throws IOException { File tmp = File.createTempFile("FileChannelTest", "tmp"); @@ -255,4 +292,55 @@ private static FileChannel createFileContainingBytes(byte[] bytes) throws IOExce return fc; } + + /** + * The test verifies that FileChannel#open(Path, Set, FileAttribute ...) returns the + * same object returned by #newFileChannel(Path, Set, FileAttribute ...) method + * in given Paths's FileSystemProvider. + */ + public void test_open_Path_Set_FileAttributes() throws IOException { + Path mockPath = mock(Path.class); + FileSystem mockFileSystem = mock(FileSystem.class); + FileSystemProvider mockFileSystemProvider = mock(FileSystemProvider.class); + FileChannel mockFileChannel = mock(FileChannel.class); + + FileAttribute mockFileAttribute1 = mock(FileAttribute.class); + FileAttribute mockFileAttribute2 = mock(FileAttribute.class); + + Set standardOpenOptions = new HashSet<>(); + standardOpenOptions.add(READ); + standardOpenOptions.add(WRITE); + + when(mockPath.getFileSystem()).thenReturn(mockFileSystem); + when(mockFileSystem.provider()).thenReturn(mockFileSystemProvider); + when(mockFileSystemProvider.newFileChannel(mockPath, standardOpenOptions, + mockFileAttribute1, mockFileAttribute2)).thenReturn(mockFileChannel); + + assertEquals(mockFileChannel, FileChannel.open(mockPath, standardOpenOptions, + mockFileAttribute1, mockFileAttribute2)); + } + + /** + * The test verifies that FileChannel#open(Path, OpenOption ...) returns the + * same object returned by #newFileChannel(Path, OpenOption ...) method + * in given Paths's FileSystemProvider. + */ + public void test_open_Path_OpenOptions() throws IOException { + + Path mockPath = mock(Path.class); + FileSystem mockFileSystem = mock(FileSystem.class); + FileSystemProvider mockFileSystemProvider = mock(FileSystemProvider.class); + FileChannel mockFileChannel = mock(FileChannel.class); + + Set standardOpenOptions = new HashSet<>(); + standardOpenOptions.add(READ); + standardOpenOptions.add(WRITE); + + when(mockPath.getFileSystem()).thenReturn(mockFileSystem); + when(mockFileSystem.provider()).thenReturn(mockFileSystemProvider); + when(mockFileSystemProvider.newFileChannel(mockPath, standardOpenOptions)) + .thenReturn(mockFileChannel); + + assertEquals(mockFileChannel, FileChannel.open(mockPath, READ, WRITE)); + } } diff --git a/luni/src/test/java/libcore/java/nio/channels/FutureLikeCompletionHandler.java b/luni/src/test/java/libcore/java/nio/channels/FutureLikeCompletionHandler.java new file mode 100644 index 000000000..fc24a8f06 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/FutureLikeCompletionHandler.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.nio.channels; + +import java.nio.channels.CompletionHandler; + +/** A CompletionHandler that behaves like a Future and enables compact, single-threaded tests. */ +public class FutureLikeCompletionHandler implements CompletionHandler { + Throwable e; + boolean done; + V result; + Object attachment; + + public void completed(V result, Object attachment) { + synchronized (this) { + if (done) { + e = new IllegalStateException("CompletionHandler used twice"); + } + this.result = result; + this.done = true; + this.attachment = attachment; + this.notifyAll(); + } + } + + public void failed(Throwable exc, Object attachment) { + synchronized (this) { + if (done) { + e = new IllegalStateException("CompletionHandler used twice"); + } + this.e = exc; + this.done = true; + this.attachment = attachment; + this.notifyAll(); + } + } + + V get(long timeoutMiliseconds) throws Throwable { + synchronized (this) { + while (!done) { + wait(timeoutMiliseconds); + } + if (e != null) { + throw e; + } + return result; + } + } + + public Object getAttachment() { + synchronized (this) { + return attachment; + } + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/IllegalChannelGroupExceptionTest.java b/luni/src/test/java/libcore/java/nio/channels/IllegalChannelGroupExceptionTest.java new file mode 100644 index 000000000..3e8592791 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/IllegalChannelGroupExceptionTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.channels; + +import junit.framework.TestCase; + +import java.nio.channels.IllegalChannelGroupException; + +public class IllegalChannelGroupExceptionTest extends TestCase{ + + /** + * java.nio.channels.IllegalChannelGroupException#IllegalChannelGroupException() + */ + public void test_empty() { + IllegalChannelGroupException e = new IllegalChannelGroupException(); + assertTrue(e instanceof IllegalArgumentException); + assertNull(e.getMessage()); + assertNull(e.getLocalizedMessage()); + assertNull(e.getCause()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/InterruptedByTimeoutExceptionTest.java b/luni/src/test/java/libcore/java/nio/channels/InterruptedByTimeoutExceptionTest.java new file mode 100644 index 000000000..38b1c45ea --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/InterruptedByTimeoutExceptionTest.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.channels; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.channels.InterruptedByTimeoutException; + +public class InterruptedByTimeoutExceptionTest extends TestCase { + + /** + * java.nio.channels.InterruptedByTimeoutException#InterruptedByTimeoutException() + */ + public void test_empty() { + InterruptedByTimeoutException e = new InterruptedByTimeoutException(); + assertTrue(e instanceof IOException); + assertNull(e.getMessage()); + assertNull(e.getLocalizedMessage()); + assertNull(e.getCause()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java b/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java new file mode 100644 index 000000000..7f385a0c3 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.channels; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.StandardProtocolFamily; +import java.net.StandardSocketOptions; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.channels.MembershipKey; + +public class MembershipKeyTest extends TestCase { + + private MembershipKey key; + private final int PORT = 5000; + private final String TEST_MESSAGE = "hello"; + private DatagramChannel client; + private InetAddress sourceAddress = Inet4Address.LOOPBACK; + private final static InetAddress MULTICAST_ADDRESS = getMulticastAddress(); + private final static NetworkInterface NETWORK_INTERFACE = getNetworkInterface(); + + private void init(boolean withSource) throws Exception { + client = DatagramChannel.open(StandardProtocolFamily.INET) + .bind(new InetSocketAddress(Inet4Address.ANY, PORT)); + client.configureBlocking(false); + + if (withSource) { + key = client.join(MULTICAST_ADDRESS, NETWORK_INTERFACE, sourceAddress); + } else { + key = client.join(MULTICAST_ADDRESS, NETWORK_INTERFACE); + } + } + + @Override + public void tearDown() throws IOException { + client.close(); + key = null; + } + + public void test_isValid_OnChannelCloseWithJoinWithoutSource() throws Exception { + init(false); + check_isValid(); + } + + public void test_isValid_OnChannelCloseWithJoinWithSource() throws Exception { + init(true); + check_isValid(); + } + + private void check_isValid() throws IOException { + assertTrue(key.isValid()); + client.close(); + assertFalse(key.isValid()); + } + + public void test_isValid_OnDropJoinWithoutSource() throws Exception { + init(false); + check_isValid_OnDrop(); + } + + public void test_isValid_OnDropJoinWithSource() throws Exception { + init(true); + check_isValid_OnDrop(); + } + + private void check_isValid_OnDrop() { + assertTrue(key.isValid()); + key.drop(); + assertFalse(key.isValid()); + } + + public void test_dropWithJoinWithoutSource() throws Exception { + init(false); + check_drop(); + } + + public void test_dropWithJoinWithSource() throws Exception { + init(true); + check_drop(); + } + + private void check_drop() throws IOException { + key.drop(); + try(DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) { + assertEquals(TEST_MESSAGE.length(), dc + .bind(new InetSocketAddress(Inet4Address.LOOPBACK, 0)) + .send(ByteBuffer.wrap(TEST_MESSAGE.getBytes()), + new InetSocketAddress(MULTICAST_ADDRESS, PORT))); + } + + ByteBuffer buffer = ByteBuffer.allocate(1048); + client.receive(buffer); + buffer.flip(); + assertEquals(0, buffer.limit()); + } + + public void test_networkInterface() throws Exception { + init(false); + assertEquals(NETWORK_INTERFACE, key.networkInterface()); + client.close(); + assertEquals(NETWORK_INTERFACE, key.networkInterface()); + } + + public void test_sourceAddressWithJoinWithSource() throws Exception { + init(true); + assertEquals(sourceAddress, key.sourceAddress()); + } + + public void test_sourceAddressWithJoinWithoutSource() throws Exception { + init(false); + assertNull(key.sourceAddress()); + } + + public void test_groupWithJoinWithSource() throws Exception { + init(true); + assertEquals(MULTICAST_ADDRESS, key.group()); + } + + public void test_groupWithoutJoinWIthSource() throws Exception { + init(false); + assertEquals(MULTICAST_ADDRESS, key.group()); + } + + public void test_channelWithJoinWithSource() throws Exception { + init(true); + assertEquals(client, key.channel()); + key.drop(); + assertEquals(client, key.channel()); + } + + public void test_channelWithJoinWithoutSource() throws Exception { + init(false); + assertEquals(client, key.channel()); + key.drop(); + assertEquals(client, key.channel()); + } + + public void test_blockWithJoinWithSource() throws Exception { + init(true); + try { + key.block(sourceAddress); + fail(); + } catch (IllegalStateException expected) {} + } + + public void test_blockWithJoinWithoutSource() throws Exception { + init(false); + key.block(sourceAddress); + + try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) { + assertEquals(TEST_MESSAGE.length(), dc + .bind(new InetSocketAddress(Inet4Address.LOOPBACK, 0)) + .send(ByteBuffer.wrap(TEST_MESSAGE.getBytes()), + new InetSocketAddress(MULTICAST_ADDRESS, PORT))); + } + + ByteBuffer buffer = ByteBuffer.allocate(1048); + client.receive(buffer); + buffer.flip(); + assertEquals(0, buffer.limit()); + } + + public void test_block_Exception () throws Exception { + init(false); + + // Blocking a multicast channel + try { + key.block(Inet4Address.getByName("224.0.0.10")); + fail(); + } catch (IllegalArgumentException expected) {} + + // Different address type than the group + try { + key.block(Inet6Address.LOOPBACK); + fail(); + } catch (IllegalArgumentException expected) {} + + key.drop(); + try { + key.block(sourceAddress); + fail(); + } catch (IllegalStateException expected) {} + } + + public void test_unblockWithJoinWithSource() throws Exception { + init(true); + try { + key.unblock(Inet4Address.getByName("127.0.0.2")); + fail(); + } catch (IllegalStateException expected) {} + } + + public void test_unblockWithJoinWithoutSource() throws Exception { + init(false); + + key.block(sourceAddress); + key.unblock(sourceAddress); + + try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) { + assertEquals(TEST_MESSAGE.length(), dc + .bind(new InetSocketAddress(Inet4Address.LOOPBACK, 0)) + .setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true /* enable loop */) + .send(ByteBuffer.wrap(TEST_MESSAGE.getBytes()), + new InetSocketAddress(MULTICAST_ADDRESS, PORT))); + } + + ByteBuffer buffer = ByteBuffer.allocate(1048); + client.receive(buffer); + buffer.flip(); + int limits = buffer.limit(); + byte bytes[] = new byte[limits]; + buffer.get(bytes, 0, limits); + String receivedMessage = new String(bytes); + assertEquals(TEST_MESSAGE, receivedMessage); + } + + public void test_unblock_Exception() throws Exception { + init(false); + try { + key.unblock(sourceAddress); + fail(); + } catch (IllegalStateException expected) {} + + key.drop(); + + try { + key.unblock(sourceAddress); + fail(); + } catch (IllegalStateException expected) {} + } + + private static InetAddress getMulticastAddress() { + try { + return InetAddress.getByName("239.255.0.1"); + } catch (UnknownHostException exception) { + throw new RuntimeException(exception); + } + } + + private static NetworkInterface getNetworkInterface() { + try { + return NetworkInterface.getByName("lo"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/ReadPendingExceptionTest.java b/luni/src/test/java/libcore/java/nio/channels/ReadPendingExceptionTest.java new file mode 100644 index 000000000..f05e4cc62 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/ReadPendingExceptionTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.channels; + +import junit.framework.TestCase; + +import java.nio.channels.ReadPendingException; + +public class ReadPendingExceptionTest extends TestCase { + + /** + * java.nio.channels.ReadPendingException#ReadPendingException() + */ + public void test_empty() { + ReadPendingException e = new ReadPendingException(); + assertTrue(e instanceof IllegalStateException); + assertNull(e.getMessage()); + assertNull(e.getLocalizedMessage()); + assertNull(e.getCause()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/ShutdownChannelGroupExceptionTest.java b/luni/src/test/java/libcore/java/nio/channels/ShutdownChannelGroupExceptionTest.java new file mode 100644 index 000000000..0d771ccb6 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/ShutdownChannelGroupExceptionTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.channels; + +import junit.framework.TestCase; + +import java.nio.channels.ShutdownChannelGroupException; + +public class ShutdownChannelGroupExceptionTest extends TestCase { + + /** + * java.nio.channels.ShutdownChannelGroupException#ShutdownChannelGroupException() + */ + public void test_empty() { + ShutdownChannelGroupException e = new ShutdownChannelGroupException(); + assertTrue(e instanceof IllegalStateException); + assertNull(e.getMessage()); + assertNull(e.getLocalizedMessage()); + assertNull(e.getCause()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java index 72609ee1c..a8f191265 100644 --- a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java +++ b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java @@ -16,6 +16,8 @@ package libcore.java.nio.channels; +import org.junit.Rule; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -36,28 +38,35 @@ import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.LeakageDetectorRule; + +public class SocketChannelTest extends TestCaseWithRules { -public class SocketChannelTest extends junit.framework.TestCase { + @Rule + public LeakageDetectorRule guardRule = ResourceLeakageDetector.getRule(); public void test_read_intoReadOnlyByteArrays() throws Exception { - ByteBuffer readOnly = ByteBuffer.allocate(1).asReadOnlyBuffer(); - ServerSocket ss = new ServerSocket(0); - ss.setReuseAddress(true); - SocketChannel sc = SocketChannel.open(ss.getLocalSocketAddress()); - try { - sc.read(readOnly); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - sc.read(new ByteBuffer[] { readOnly }); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - sc.read(new ByteBuffer[] { readOnly }, 0, 1); - fail(); - } catch (IllegalArgumentException expected) { + try (ServerSocket ss = new ServerSocket(0); + SocketChannel sc = SocketChannel.open(ss.getLocalSocketAddress())) { + ByteBuffer readOnly = ByteBuffer.allocate(1).asReadOnlyBuffer(); + ss.setReuseAddress(true); + try { + sc.read(readOnly); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + sc.read(new ByteBuffer[] { readOnly }); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + sc.read(new ByteBuffer[] { readOnly }, 0, 1); + fail(); + } catch (IllegalArgumentException expected) { + } } } @@ -269,13 +278,14 @@ public void test_connect_nonBlocking() throws Exception { } public void test_Socket_impl_notNull() throws Exception { - SocketChannel sc = SocketChannel.open(); - Socket socket = sc.socket(); - Field f_impl = Socket.class.getDeclaredField("impl"); - f_impl.setAccessible(true); - Object implFieldValue = f_impl.get(socket); - assertNotNull(implFieldValue); - assertTrue(implFieldValue instanceof SocketImpl); + try (SocketChannel sc = SocketChannel.open(); + Socket socket = sc.socket()) { + Field f_impl = Socket.class.getDeclaredField("impl"); + f_impl.setAccessible(true); + Object implFieldValue = f_impl.get(socket); + assertNotNull(implFieldValue); + assertTrue(implFieldValue instanceof SocketImpl); + } } public void test_setOption() throws Exception { @@ -311,8 +321,8 @@ public void test_bind() throws IOException { socketAddress = new InetSocketAddress(Inet4Address.LOOPBACK, ((InetSocketAddress) (sc.getLocalAddress())).getPort()); - try { - SocketChannel.open().bind(socketAddress); + try (SocketChannel sc1 = SocketChannel.open()){ + sc1.bind(socketAddress); fail(); } catch (BindException expected) { } @@ -321,77 +331,87 @@ public void test_bind() throws IOException { socketAddress = new InetSocketAddress(Inet4Address.LOOPBACK, 0); try { sc.bind(socketAddress); + fail(); } catch (ClosedChannelException expected) { } } public void test_getRemoteAddress() throws IOException { - SocketChannel sc = SocketChannel.open(); - ServerSocket ss = new ServerSocket(0); - - assertNull(sc.getRemoteAddress()); + try (SocketChannel sc = SocketChannel.open(); + ServerSocket ss = new ServerSocket(0)) { + assertNull(sc.getRemoteAddress()); - sc.connect(ss.getLocalSocketAddress()); - assertEquals(sc.getRemoteAddress(), ss.getLocalSocketAddress()); + sc.connect(ss.getLocalSocketAddress()); + assertEquals(sc.getRemoteAddress(), ss.getLocalSocketAddress()); + } } public void test_shutdownInput() throws IOException { - SocketChannel channel1 = SocketChannel.open(); - ServerSocket server1 = new ServerSocket(0); - InetSocketAddress localAddr1 = new InetSocketAddress("127.0.0.1", server1.getLocalPort()); - - // initialize write content - byte[] writeContent = new byte[10]; - for (int i = 0; i < writeContent.length; i++) { - writeContent[i] = (byte) i; + try (SocketChannel channel1 = SocketChannel.open(); + ServerSocket server1 = new ServerSocket(0)) { + InetSocketAddress localAddr1 = new InetSocketAddress("127.0.0.1", + server1.getLocalPort()); + + // initialize write content + byte[] writeContent = new byte[10]; + for (int i = 0; i < writeContent.length; i++) { + writeContent[i] = (byte) i; + } + + // establish connection + channel1.connect(localAddr1); + Socket acceptedSocket = server1.accept(); + // use OutputStream.write to write bytes data. + OutputStream out = acceptedSocket.getOutputStream(); + out.write(writeContent); + // use close to guarantee all data is sent + acceptedSocket.close(); + + channel1.configureBlocking(false); + ByteBuffer readContent = ByteBuffer.allocate(10 + 1); + channel1.shutdownInput(); + assertEquals(-1, channel1.read(readContent)); } - - // establish connection - channel1.connect(localAddr1); - Socket acceptedSocket = server1.accept(); - // use OutputStream.write to write bytes data. - OutputStream out = acceptedSocket.getOutputStream(); - out.write(writeContent); - // use close to guarantee all data is sent - acceptedSocket.close(); - - channel1.configureBlocking(false); - ByteBuffer readContent = ByteBuffer.allocate(10 + 1); - channel1.shutdownInput(); - assertEquals(-1, channel1.read(readContent)); } public void test_shutdownOutput() throws IOException { - SocketChannel channel1 = SocketChannel.open(); - ServerSocket server1 = new ServerSocket(0); - InetSocketAddress localAddr1 = new InetSocketAddress("127.0.0.1", server1.getLocalPort()); - - // initialize write content - ByteBuffer writeContent = ByteBuffer.allocate(10); - for (int i = 0; i < 10; i++) { - writeContent.put((byte) i); - } - writeContent.flip(); - - try { + try (SocketChannel channel1 = SocketChannel.open(); + ServerSocket server1 = new ServerSocket(0)) { + InetSocketAddress localAddr1 = new InetSocketAddress( + "127.0.0.1", server1.getLocalPort()); + + // initialize write content + ByteBuffer writeContent = ByteBuffer.allocate(10); + for (int i = 0; i < 10; i++) { + writeContent.put((byte) i); + } + writeContent.flip(); + + try { + channel1.shutdownOutput(); + fail(); + } catch (NotYetConnectedException expected) { + } + + // establish connection + channel1.connect(localAddr1); channel1.shutdownOutput(); - fail(); - } catch (NotYetConnectedException expected) {} - - // establish connection - channel1.connect(localAddr1); - channel1.shutdownOutput(); - try { - channel1.write(writeContent); - fail(); - } catch (ClosedChannelException expected) {} - - channel1.close(); - - try { - channel1.shutdownOutput(); - fail(); - } catch(ClosedChannelException expected) {} + try { + channel1.write(writeContent); + fail(); + } catch (ClosedChannelException expected) { + } + + // Closing the channel early to verify that is CloseChannelException thrown by + // #shutdownOutput. + channel1.close(); + + try { + channel1.shutdownOutput(); + fail(); + } catch (ClosedChannelException expected) { + } + } } } diff --git a/luni/src/test/java/libcore/java/nio/channels/WritePendingExceptionTest.java b/luni/src/test/java/libcore/java/nio/channels/WritePendingExceptionTest.java new file mode 100644 index 000000000..463cf8c80 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/WritePendingExceptionTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.channels; + +import junit.framework.TestCase; + +import java.nio.channels.WritePendingException; + +public class WritePendingExceptionTest extends TestCase { + + /** + * java.nio.channels.WritePendingException#WritePendingException() + */ + public void test_empty() { + WritePendingException e = new WritePendingException(); + assertTrue(e instanceof IllegalStateException); + assertNull(e.getMessage()); + assertNull(e.getLocalizedMessage()); + assertNull(e.getCause()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/channels/spi/AsynchronousChannelProviderTest.java b/luni/src/test/java/libcore/java/nio/channels/spi/AsynchronousChannelProviderTest.java new file mode 100644 index 000000000..16d088190 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/channels/spi/AsynchronousChannelProviderTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.channels.spi; + +import junit.framework.TestCase; + +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +public class AsynchronousChannelProviderTest extends TestCase { + + public void test_open_methods() throws Exception { + AsynchronousChannelProvider provider = AsynchronousChannelProvider.provider(); + + assertNotNull(provider); + assertSame(AsynchronousChannelProvider.provider(), provider); + + assertNotNull(provider.openAsynchronousChannelGroup(1, new TestThreadFactory())); + + assertNotNull(provider.openAsynchronousChannelGroup(Executors.newSingleThreadExecutor(), + 1)); + + assertNotNull(provider.openAsynchronousServerSocketChannel( + AsynchronousChannelGroup.withFixedThreadPool(1, new TestThreadFactory()))); + + assertNotNull(provider.openAsynchronousSocketChannel( + AsynchronousChannelGroup.withFixedThreadPool(1, new TestThreadFactory()))); + + assertNotNull(provider.openAsynchronousChannelGroup(1, new TestThreadFactory())); + } + + private static class TestThreadFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(false); + return t; + } + } +} diff --git a/luni/src/test/java/libcore/java/nio/charset/OldCharset_AbstractTest.java b/luni/src/test/java/libcore/java/nio/charset/OldCharset_AbstractTest.java index d4cf83edc..4b061ed94 100644 --- a/luni/src/test/java/libcore/java/nio/charset/OldCharset_AbstractTest.java +++ b/luni/src/test/java/libcore/java/nio/charset/OldCharset_AbstractTest.java @@ -177,14 +177,22 @@ public void test_CodecDynamic () throws CharacterCodingException { encoder.onUnmappableCharacter(CodingErrorAction.REPORT); decoder.onMalformedInput(CodingErrorAction.REPORT); CharBuffer inputCB = CharBuffer.allocate(65536); - for (int code = 32; code <= 65533; ++code) { - // icu4c seems to accept any surrogate as a sign that "more is coming", - // even for charsets like US-ASCII. http://b/10310751 - if (code >= 0xd800 && code <= 0xdfff) { + // Only test most of the Unicode BMP. + // Supplementary code points would require use of encoder.canEncode(CharSequence). + for (char code = 0x20; code <= 0xfffd; code++) { + // Skip surrogates to avoid writing broken UTF-16. + // Ignore charsets that do convert surrogate code units. + if (code == 0xd800) { + code = 0xdfff; continue; } - if (encoder.canEncode((char) code)) { - inputCB.put((char) code); + // Ignore the private use area. + if (code == 0xe000) { + code = 0xf8ff; + continue; + } + if (encoder.canEncode(code)) { + inputCB.put(code); } } inputCB.rewind(); @@ -211,7 +219,7 @@ static void assertEqualCBs (String msg, CharBuffer expectedCB, CharBuffer actual actual = actualCB.get(); if (actual != expected) { String detail = String.format( - "Mismatch at index %d: %d instead of expected %d.\n", + "Mismatch at index %d: U+%04X instead of expected U+%04X.\n", i, (int) actual, (int) expected); match = false; fail(msg + ": " + detail); @@ -231,8 +239,9 @@ static void assertEqualChars(char[] expected, CharBuffer actualCB) { for (int i = 0; i < actualCB.length(); ++i) { char actual = actualCB.get(); if (actual != expected[i]) { - String detail = String.format("Mismatch at index %d: %d instead of expected %d.\n", - i, (int) actual, (int) expected[i]); + String detail = String.format( + "Mismatch at index %d: U+%04X instead of expected U+%04X.\n", + i, (int) actual, (int) expected[i]); fail(detail); } } @@ -251,7 +260,7 @@ static void assertEqualBytes (String msg, byte[] expected, ByteBuffer actualBB) actual = actualBB.get(); if (actual != expected[i]) { String detail = String.format( - "Mismatch at index %d: %d instead of expected %d.\n", + "Mismatch at index %d: %02X instead of expected %02X.\n", i, actual & 0xff, expected[i] & 0xff); match = false; fail(msg + ": " + detail); diff --git a/luni/src/test/java/libcore/java/nio/charset/OldCharset_MultiByte_EUC_JP.java b/luni/src/test/java/libcore/java/nio/charset/OldCharset_MultiByte_EUC_JP.java index 4849ad3df..73ed1bf70 100644 --- a/luni/src/test/java/libcore/java/nio/charset/OldCharset_MultiByte_EUC_JP.java +++ b/luni/src/test/java/libcore/java/nio/charset/OldCharset_MultiByte_EUC_JP.java @@ -15,11 +15,6 @@ */ package libcore.java.nio.charset; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.CodingErrorAction; - public class OldCharset_MultiByte_EUC_JP extends OldCharset_AbstractTest { @Override protected void setUp() throws Exception { charsetName = "EUC-JP"; @@ -30,22 +25,4 @@ public class OldCharset_MultiByte_EUC_JP extends OldCharset_AbstractTest { 'T', 'o', 'k', 'y', 'o', ' ', '1', '2', '3'); super.setUp(); } - - @Override public void test_CodecDynamic() throws CharacterCodingException { - encoder.onUnmappableCharacter(CodingErrorAction.REPORT); - decoder.onMalformedInput(CodingErrorAction.REPORT); - CharBuffer inputCB = CharBuffer.allocate(65536); - for (char codePoint = 0; codePoint <= 0xfffe; ++codePoint) { - if (encoder.canEncode(codePoint)) { - inputCB.put(codePoint); - } - } - inputCB.rewind(); - ByteBuffer intermediateBB = encoder.encode(inputCB); - inputCB.rewind(); - intermediateBB.rewind(); - CharBuffer outputCB = decoder.decode(intermediateBB); - outputCB.rewind(); - assertEqualCBs("decode(encode(A)) must be identical with A!", inputCB, outputCB); - } } diff --git a/luni/src/test/java/libcore/java/nio/file/AccessDeniedExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/AccessDeniedExceptionTest.java new file mode 100644 index 000000000..baa11103e --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/AccessDeniedExceptionTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.AccessDeniedException; +import java.nio.file.FileSystemException; +import libcore.util.SerializationTester; + +public class AccessDeniedExceptionTest extends TestCase { + + public void test_constructor$String() { + AccessDeniedException exception = new AccessDeniedException("file"); + assertEquals("file", exception.getFile()); + assertNull(exception.getOtherFile()); + assertNull(exception.getReason()); + + assertTrue(exception instanceof FileSystemException); + } + + public void test_constructor$String$String$String() { + AccessDeniedException exception = new AccessDeniedException("file", "otherFile", "reason"); + assertEquals("file", exception.getFile()); + assertEquals("otherFile", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } + + public void test_serialization() throws IOException, ClassNotFoundException { + String hex = "aced0005737200236a6176612e6e696f2e66696c652e41636365737344656e6965644578" + + "63657074696f6e44993d6bf81c2721020000787200216a6176612e6e696f2e66696c652e46696c65" + + "53797374656d457863657074696f6ed598f27876d360fc0200024c000466696c657400124c6a6176" + + "612f6c616e672f537472696e673b4c00056f7468657271007e0002787200136a6176612e696f2e49" + + "4f457863657074696f6e6c8073646525f0ab020000787200136a6176612e6c616e672e4578636570" + + "74696f6ed0fd1f3e1a3b1cc4020000787200136a6176612e6c616e672e5468726f7761626c65d5c6" + + "35273977b8cb0300044c000563617573657400154c6a6176612f6c616e672f5468726f7761626c65" + + "3b4c000d64657461696c4d65737361676571007e00025b000a737461636b547261636574001e5b4c" + + "6a6176612f6c616e672f537461636b5472616365456c656d656e743b4c0014737570707265737365" + + "64457863657074696f6e737400104c6a6176612f7574696c2f4c6973743b787071007e0009740006" + + "726561736f6e7572001e5b4c6a6176612e6c616e672e537461636b5472616365456c656d656e743b" + + "02462a3c3cfd22390200007870000000097372001b6a6176612e6c616e672e537461636b54726163" + + "65456c656d656e746109c59a2636dd8502000449000a6c696e654e756d6265724c000e6465636c61" + + "72696e67436c61737371007e00024c000866696c654e616d6571007e00024c000a6d6574686f644e" + + "616d6571007e000278700000002674002f6c6962636f72652e6a6176612e6e696f2e66696c652e41" + + "636365737344656e696564457863657074696f6e5465737474001e41636365737344656e69656445" + + "7863657074696f6e546573742e6a617661740025746573745f636f6e7374727563746f7224537472" + + "696e6724537472696e6724537472696e677371007e000dfffffffe7400186a6176612e6c616e672e" + + "7265666c6563742e4d6574686f6474000b4d6574686f642e6a617661740006696e766f6b65737100" + + "7e000d000000f9740028766f6761722e7461726765742e6a756e69742e4a756e69743324566f6761" + + "724a556e69745465737474000b4a756e6974332e6a61766174000372756e7371007e000d00000063" + + "740020766f6761722e7461726765742e6a756e69742e4a556e697452756e6e657224317400104a55" + + "6e697452756e6e65722e6a61766174000463616c6c7371007e000d0000005c740020766f6761722e" + + "7461726765742e6a756e69742e4a556e697452756e6e657224317400104a556e697452756e6e6572" + + "2e6a61766174000463616c6c7371007e000d000000ed74001f6a6176612e7574696c2e636f6e6375" + + "7272656e742e4675747572655461736b74000f4675747572655461736b2e6a61766174000372756e" + + "7371007e000d0000046d7400276a6176612e7574696c2e636f6e63757272656e742e546872656164" + + "506f6f6c4578656375746f72740017546872656164506f6f6c4578656375746f722e6a6176617400" + + "0972756e576f726b65727371007e000d0000025f74002e6a6176612e7574696c2e636f6e63757272" + + "656e742e546872656164506f6f6c4578656375746f7224576f726b6572740017546872656164506f" + + "6f6c4578656375746f722e6a61766174000372756e7371007e000d000002f97400106a6176612e6c" + + "616e672e54687265616474000b5468726561642e6a61766174000372756e7372001f6a6176612e75" + + "74696c2e436f6c6c656374696f6e7324456d7074794c6973747ab817b43ca79ede02000078707874" + + "000466696c657400096f7468657246696c65"; + AccessDeniedException exception = (AccessDeniedException) + SerializationTester.deserializeHex(hex); + + String hex1 = SerializationTester.serializeHex(exception).toString(); + assertEquals(hex, hex1); + assertEquals("file", exception.getFile()); + assertEquals("otherFile", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/AtomicMoveNotSupportedExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/AtomicMoveNotSupportedExceptionTest.java new file mode 100644 index 000000000..c330ad88c --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/AtomicMoveNotSupportedExceptionTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.FileSystemException; +import libcore.util.SerializationTester; + +public class AtomicMoveNotSupportedExceptionTest extends TestCase { + + public void test_constructor$String$String$String() { + AtomicMoveNotSupportedException exception = new AtomicMoveNotSupportedException("source", + "target", "reason"); + assertEquals("source", exception.getFile()); + assertEquals("target", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + assertTrue(exception instanceof FileSystemException); + } + + public void test_serialization() throws IOException, ClassNotFoundException { + String hex = "aced00057372002d6a6176612e6e696f2e66696c652e41746f6d69634d6f76654e6f745375707" + + "06f72746564457863657074696f6e4afa75ccc59748db020000787200216a6176612e6e696f2e666" + + "96c652e46696c6553797374656d457863657074696f6ed598f27876d360fc0200024c000466696c6" + + "57400124c6a6176612f6c616e672f537472696e673b4c00056f7468657271007e0002787200136a6" + + "176612e696f2e494f457863657074696f6e6c8073646525f0ab020000787200136a6176612e6c616" + + "e672e457863657074696f6ed0fd1f3e1a3b1cc4020000787200136a6176612e6c616e672e5468726" + + "f7761626c65d5c635273977b8cb0300044c000563617573657400154c6a6176612f6c616e672f546" + + "8726f7761626c653b4c000d64657461696c4d65737361676571007e00025b000a737461636b54726" + + "1636574001e5b4c6a6176612f6c616e672f537461636b5472616365456c656d656e743b4c0014737" + + "57070726573736564457863657074696f6e737400104c6a6176612f7574696c2f4c6973743b78707" + + "1007e0009740006726561736f6e7572001e5b4c6a6176612e6c616e672e537461636b54726163654" + + "56c656d656e743b02462a3c3cfd22390200007870000000097372001b6a6176612e6c616e672e537" + + "461636b5472616365456c656d656e746109c59a2636dd8502000449000a6c696e654e756d6265724" + + "c000e6465636c6172696e67436c61737371007e00024c000866696c654e616d6571007e00024c000" + + "a6d6574686f644e616d6571007e00027870000000247400396c6962636f72652e6a6176612e6e696" + + "f2e66696c652e41746f6d69634d6f76654e6f74537570706f72746564457863657074696f6e54657" + + "37474002841746f6d69634d6f76654e6f74537570706f72746564457863657074696f6e546573742" + + "e6a617661740012746573745f73657269616c697a6174696f6e7371007e000dfffffffe7400186a6" + + "176612e6c616e672e7265666c6563742e4d6574686f6474000b4d6574686f642e6a6176617400066" + + "96e766f6b657371007e000d000000f9740028766f6761722e7461726765742e6a756e69742e4a756" + + "e69743324566f6761724a556e69745465737474000b4a756e6974332e6a61766174000372756e737" + + "1007e000d00000063740020766f6761722e7461726765742e6a756e69742e4a556e697452756e6e6" + + "57224317400104a556e697452756e6e65722e6a61766174000463616c6c7371007e000d0000005c7" + + "40020766f6761722e7461726765742e6a756e69742e4a556e697452756e6e657224317400104a556" + + "e697452756e6e65722e6a61766174000463616c6c7371007e000d000000ed74001f6a6176612e757" + + "4696c2e636f6e63757272656e742e4675747572655461736b74000f4675747572655461736b2e6a6" + + "1766174000372756e7371007e000d0000046d7400276a6176612e7574696c2e636f6e63757272656" + + "e742e546872656164506f6f6c4578656375746f72740017546872656164506f6f6c4578656375746" + + "f722e6a61766174000972756e576f726b65727371007e000d0000025f74002e6a6176612e7574696" + + "c2e636f6e63757272656e742e546872656164506f6f6c4578656375746f7224576f726b657274001" + + "7546872656164506f6f6c4578656375746f722e6a61766174000372756e7371007e000d000002f97" + + "400106a6176612e6c616e672e54687265616474000b5468726561642e6a61766174000372756e737" + + "2001f6a6176612e7574696c2e436f6c6c656374696f6e7324456d7074794c6973747ab817b43ca79" + + "ede020000787078740006736f75726365740006746172676574"; + + AtomicMoveNotSupportedException exception = (AtomicMoveNotSupportedException) + SerializationTester.deserializeHex(hex); + + String hex1 = SerializationTester.serializeHex(exception).toString(); + assertEquals(hex, hex1); + assertEquals("source", exception.getFile()); + assertEquals("target", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java b/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java new file mode 100644 index 000000000..763efbf7c --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.DosFileAttributeView; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileOwnerAttributeView; +import java.nio.file.attribute.FileStoreAttributeView; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.UserDefinedFileAttributeView; + +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNull; +import static junit.framework.TestCase.assertTrue; +import static libcore.java.nio.file.FilesSetup.execCmdAndWaitForTermination; +import static libcore.java.nio.file.FilesSetup.readFromInputStream; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class DefaultFileStoreTest { + + @Rule + public FilesSetup filesSetup = new FilesSetup(); + + @Test + public void test_name() throws IOException, InterruptedException { + Path path = filesSetup.getPathInTestDir("dir"); + Files.createDirectory(path); + Process p = execCmdAndWaitForTermination("df", path.toAbsolutePath().toString()); + String shellOutput = readFromInputStream(p.getInputStream()).split("\n")[1]; + String storeTypeFromShell = shellOutput.split("\\s")[0]; + assertEquals(storeTypeFromShell, Files.getFileStore(path).name()); + } + + @Test + public void test_type() throws IOException, InterruptedException { + Path path = filesSetup.getPathInTestDir("dir"); + Files.createDirectory(path); + String fileStore = Files.getFileStore(path).name(); + Process p = execCmdAndWaitForTermination("mount"); + String mountOutput[] = readFromInputStream(p.getInputStream()).split("\n"); + for (String mountInfo : mountOutput) { + if (mountInfo.contains(fileStore) + && mountInfo.contains(Files.getFileStore(path).type())) { + return; + } + } + fail(); + } + + @Test + public void test_isReadOnly() throws IOException { + Path path = Paths.get("/system"); + assertTrue(Files.getFileStore(path).isReadOnly()); + + path = Paths.get("/data"); + assertFalse(Files.getFileStore(path).isReadOnly()); + } + + @Test + public void test_getTotalSpace() throws IOException { + Path path = Paths.get("/data"); + assertTrue(Files.getFileStore(path).getTotalSpace() > 0); + } + + @Test + public void test_getUsableSpace() throws IOException { + Path path = Paths.get("/data"); + long usableSpace = Files.getFileStore(path).getUsableSpace(); + long totalSpace = Files.getFileStore(path).getTotalSpace(); + assertTrue(usableSpace <= totalSpace && usableSpace > 0); + } + + @Test + public void test_getUnallocatedSpace() throws IOException { + Path path = Paths.get("/data"); + long unallocatedSpace = Files.getFileStore(path).getUnallocatedSpace(); + assertTrue(unallocatedSpace >= 0); + } + + @Test + public void test_supportsFileAttributeView$Class() throws IOException { + Path path = filesSetup.getPathInTestDir("dir"); + Files.createDirectories(path); + assertTrue(Files.getFileStore(path).supportsFileAttributeView( + BasicFileAttributeView.class)); + assertTrue(Files.getFileStore(path).supportsFileAttributeView( + FileOwnerAttributeView.class)); + assertTrue(Files.getFileStore(path).supportsFileAttributeView( + PosixFileAttributeView.class)); + assertFalse(Files.getFileStore(path).supportsFileAttributeView(DosFileAttributeView.class)); + assertFalse(Files.getFileStore(path). + supportsFileAttributeView(UserDefinedFileAttributeView.class)); + assertFalse(Files.getFileStore(path). + supportsFileAttributeView(NonStandardFileAttributeView.class)); + } + + @Test + public void test_supportsFileAttributeView$String() throws IOException { + Path path = filesSetup.getPathInTestDir("dir"); + Files.createDirectories(path); + assertTrue(Files.getFileStore(path).supportsFileAttributeView("basic")); + assertTrue(Files.getFileStore(path).supportsFileAttributeView("unix")); + assertTrue(Files.getFileStore(path).supportsFileAttributeView("posix")); + assertTrue(Files.getFileStore(path).supportsFileAttributeView("owner")); + assertFalse(Files.getFileStore(path).supportsFileAttributeView("user")); + assertFalse(Files.getFileStore(path).supportsFileAttributeView("dos")); + assertFalse(Files.getFileStore(path).supportsFileAttributeView("nonStandardView")); + } + + @Test + public void test_getFileStoreAttributeView() throws IOException { + Path path = filesSetup.getPathInTestDir("dir"); + Files.createDirectories(path); + assertNull(Files.getFileStore(path).getFileStoreAttributeView( + FileStoreAttributeView.class)); + try { + Files.getFileStore(path).getFileStoreAttributeView(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_getAttribute() throws IOException { + Path p = filesSetup.getPathInTestDir("dir"); + Files.createDirectories(p); + FileStore store = Files.getFileStore(p); + assertEquals(store.getTotalSpace(), store.getAttribute("totalSpace")); + assertEquals(store.getUnallocatedSpace(), store.getAttribute("unallocatedSpace")); + assertEquals(store.getUsableSpace(), store.getAttribute("usableSpace")); + try { + store.getAttribute("test"); + fail(); + } catch (UnsupportedOperationException expected) {} + } + + private static class NonStandardFileAttributeView implements FileAttributeView { + @Override + public String name() { + return null; + } + } +} \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProvider2Test.java b/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProvider2Test.java new file mode 100644 index 000000000..dbbfeae44 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProvider2Test.java @@ -0,0 +1,683 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.NonReadableChannelException; +import java.nio.file.CopyOption; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.NotLinkException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.nio.file.spi.FileSystemProvider; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.nio.file.StandardOpenOption.APPEND; +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; +import static java.nio.file.StandardOpenOption.WRITE; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertTrue; +import static libcore.java.nio.file.FilesSetup.DATA_FILE; +import static libcore.java.nio.file.FilesSetup.NonStandardOption; +import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA; +import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA_2; +import static libcore.java.nio.file.FilesSetup.readFromFile; +import static libcore.java.nio.file.FilesSetup.writeToFile; +import static libcore.java.nio.file.LinuxFileSystemTestData.getPath_URI_InputOutputTestData; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; + +@RunWith(JUnitParamsRunner.class) +public class DefaultFileSystemProvider2Test { + + @Rule + public FilesSetup filesSetup = new FilesSetup(); + + private FileSystemProvider provider; + + @Before + public void setUp() throws Exception { + provider = filesSetup.getDataFilePath().getFileSystem().provider(); + } + + @Test + public void test_move() throws IOException { + provider.move(filesSetup.getDataFilePath(), filesSetup.getTestPath()); + assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath())); + assertFalse(Files.exists(filesSetup.getDataFilePath())); + + filesSetup.reset(); + Files.createFile(filesSetup.getTestPath()); + // When target file exists. + try { + provider.move(filesSetup.getDataFilePath(), filesSetup.getTestPath()); + fail(); + } catch (FileAlreadyExistsException expected) {} + + // Move to existing target file with REPLACE_EXISTING copy option. + filesSetup.reset(); + Files.createFile(filesSetup.getTestPath()); + writeToFile(filesSetup.getDataFilePath(), TEST_FILE_DATA_2); + provider.move(filesSetup.getDataFilePath(), filesSetup.getTestPath(), REPLACE_EXISTING); + assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getTestPath())); + + // Copy from a non existent file. + filesSetup.reset(); + try { + provider.move(filesSetup.getTestPath(), filesSetup.getDataFilePath(), REPLACE_EXISTING); + fail(); + } catch (NoSuchFileException expected) {} + } + + @Test + public void test_move_CopyOption() throws IOException { + FileTime fileTime = FileTime.fromMillis(System.currentTimeMillis() - 10000); + Files.setAttribute(filesSetup.getDataFilePath(), "basic:lastModifiedTime", fileTime); + provider.move(filesSetup.getDataFilePath(), filesSetup.getTestPath()); + assertEquals(fileTime.to(TimeUnit.SECONDS), + ((FileTime) Files.getAttribute(filesSetup.getTestPath(), + "basic:lastModifiedTime")).to(TimeUnit.SECONDS)); + assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath())); + + // ATOMIC_MOVE + filesSetup.reset(); + provider.move(filesSetup.getDataFilePath(), filesSetup.getTestPath(), ATOMIC_MOVE); + assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath())); + + filesSetup.reset(); + try { + provider.move(filesSetup.getDataFilePath(), filesSetup.getTestPath(), + NonStandardOption.OPTION1); + fail(); + } catch (UnsupportedOperationException expected) {} + } + + @Test + public void test_move_NPE() throws IOException { + try { + provider.move(null, filesSetup.getTestPath()); + fail(); + } catch(NullPointerException expected) {} + + try { + provider.move(filesSetup.getDataFilePath(), null); + fail(); + } catch(NullPointerException expected) {} + + try { + provider.move(filesSetup.getDataFilePath(), filesSetup.getTestPath(), + (CopyOption[]) null); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_move_directory() throws IOException { + Path dirPath = filesSetup.getPathInTestDir("dir1"); + final Path nestedDirPath = filesSetup.getPathInTestDir("dir1/dir"); + final Path dirPath2 = filesSetup.getPathInTestDir("dir2"); + + Files.createDirectory(dirPath); + Files.createDirectory(nestedDirPath); + Files.copy(filesSetup.getDataFilePath(), + filesSetup.getPathInTestDir("dir1/" + DATA_FILE)); + provider.move(dirPath, dirPath2); + + Map pathMap = new HashMap<>(); + try (DirectoryStream directoryStream = Files.newDirectoryStream(dirPath2)) { + directoryStream.forEach(file -> pathMap.put(file, true)); + } + + // The files are not copied. The command is equivalent of creating a new directory. + assertEquals(2, pathMap.size()); + assertEquals(TEST_FILE_DATA, + readFromFile(filesSetup.getPathInTestDir("dir2/" + DATA_FILE))); + assertFalse(Files.exists(dirPath)); + + filesSetup.reset(); + } + + @Test + public void test_move_directory_DirectoryNotEmptyException() throws IOException { + Path dirPath = filesSetup.getPathInTestDir("dir1"); + Path dirPath4 = filesSetup.getPathInTestDir("dir4"); + Files.createDirectory(dirPath); + Files.createDirectory(dirPath4); + Files.createFile(Paths.get(dirPath.toString(), DATA_FILE)); + Files.createFile(Paths.get(dirPath4.toString(), DATA_FILE)); + try { + Files.copy(dirPath, dirPath4, REPLACE_EXISTING); + fail(); + } catch (DirectoryNotEmptyException expected) {} + } + + @Test + public void test_readSymbolicLink() throws IOException { + provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(), + /* Path of the target of the symbolic link */ + filesSetup.getDataFilePath().toAbsolutePath()); + assertEquals(filesSetup.getDataFilePath().toAbsolutePath(), + Files.readSymbolicLink(filesSetup.getTestPath())); + + // Sym link to itself + filesSetup.reset(); + provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(), + /* Path of the target of the symbolic link */ + filesSetup.getTestPath().toAbsolutePath()); + assertEquals(filesSetup.getTestPath().toAbsolutePath(), + Files.readSymbolicLink(filesSetup.getTestPath())); + + filesSetup.reset(); + try { + provider.readSymbolicLink(filesSetup.getDataFilePath()); + fail(); + } catch (NotLinkException expected) { + } + } + + @Test + public void test_readSymbolicLink_NPE() throws IOException { + try { + provider.readSymbolicLink(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_isSameFile() throws IOException { + // When both the files exists. + assertTrue(provider.isSameFile(filesSetup.getDataFilePath(), filesSetup.getDataFilePath())); + + // When the files doesn't exist. + assertTrue(provider.isSameFile(filesSetup.getTestPath(), filesSetup.getTestPath())); + + // With two different files. + try { + assertFalse( + provider.isSameFile(filesSetup.getDataFilePath(), filesSetup.getTestPath())); + fail(); + } catch (NoSuchFileException expected) {} + } + + @Test + public void test_isSameFile_NPE() throws IOException { + try { + provider.isSameFile(null, filesSetup.getDataFilePath()); + fail(); + } catch (NullPointerException expected) {} + + try { + provider.isSameFile(filesSetup.getDataFilePath(), null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_getFileStore() throws IOException { + FileStore fileStore = provider.getFileStore(filesSetup.getDataFilePath()); + assertNotNull(fileStore); + } + + @Test + public void test_getFileStore_NPE() throws IOException { + try { + provider.getFileStore(null); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_isHidden() throws IOException { + assertFalse(provider.isHidden(filesSetup.getDataFilePath())); + Files.setAttribute(filesSetup.getDataFilePath(), "dos:hidden", true); + + // Files can't be hid. + assertFalse(provider.isHidden(filesSetup.getDataFilePath())); + } + + @Test + public void test_isHidden_NPE() throws IOException { + try { + provider.isHidden(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_probeContentType_NPE() throws IOException { + try { + Files.probeContentType(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_getFileAttributeView() throws IOException { + BasicFileAttributeView fileAttributeView = provider + .getFileAttributeView(filesSetup.getDataFilePath(), + BasicFileAttributeView.class); + + assertTrue(fileAttributeView.readAttributes().isRegularFile()); + assertFalse(fileAttributeView.readAttributes().isDirectory()); + } + + @Test + public void test_getFileAttributeView_NPE() throws IOException { + try { + provider.getFileAttributeView(null, BasicFileAttributeView.class); + fail(); + } catch (NullPointerException expected) {} + + try { + provider.getFileAttributeView(filesSetup.getDataFilePath(), null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_readAttributes() throws IOException { + FileTime fileTime = FileTime.fromMillis(System.currentTimeMillis() - 10000); + Files.setAttribute(filesSetup.getDataFilePath(), "basic:lastModifiedTime", fileTime); + BasicFileAttributes basicFileAttributes = provider + .readAttributes(filesSetup.getDataFilePath(), + BasicFileAttributes.class); + FileTime lastModifiedTime = basicFileAttributes.lastModifiedTime(); + assertEquals(fileTime.to(TimeUnit.SECONDS), lastModifiedTime.to(TimeUnit.SECONDS)); + + // When file is NON_EXISTENT. + try { + provider.readAttributes(filesSetup.getTestPath(), BasicFileAttributes.class); + fail(); + } catch (NoSuchFileException expected) {} + } + + @Test + public void test_readAttributes_NPE() throws IOException { + try { + provider.readAttributes(filesSetup.getDataFilePath(), + (Class) null); + fail(); + } catch(NullPointerException expected) {} + + try { + provider.readAttributes(null, BasicFileAttributes.class); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_setAttribute() throws IOException { + // Other tests are covered in test_readAttributes. + // When file is NON_EXISTENT. + try { + FileTime fileTime = FileTime.fromMillis(System.currentTimeMillis()); + provider.setAttribute(filesSetup.getTestPath(), "basic:lastModifiedTime", fileTime); + fail(); + } catch (NoSuchFileException expected) {} + + // ClassCastException + try { + provider.setAttribute(filesSetup.getDataFilePath(), "basic:lastModifiedTime", 10); + fail(); + } catch (ClassCastException expected) {} + + // IllegalArgumentException + try { + provider.setAttribute(filesSetup.getDataFilePath(), "xyz", 10); + fail(); + } catch (IllegalArgumentException expected) {} + + try { + provider.setAttribute(null, "xyz", 10); + fail(); + } catch (NullPointerException expected) {} + + try { + provider.setAttribute(filesSetup.getDataFilePath(), null, 10); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_newFileChannel() throws IOException { + Set openOptions = new HashSet<>(); + + // When file doesn't exist/ + try { + // With CREATE & WRITE in OpenOptions. + openOptions.add(CREATE); + openOptions.add(WRITE); + provider.newFileChannel(filesSetup.getTestPath(), openOptions); + assertTrue(Files.exists(filesSetup.getTestPath())); + Files.delete(filesSetup.getTestPath()); + } finally { + filesSetup.reset(); + openOptions.clear(); + } + + try { + // With CREATE & APPEND in OpenOption. + assertFalse(Files.exists(filesSetup.getTestPath())); + openOptions.add(CREATE); + openOptions.add(APPEND); + provider.newFileChannel(filesSetup.getTestPath(), openOptions); + assertTrue(Files.exists(filesSetup.getTestPath())); + Files.delete(filesSetup.getTestPath()); + } finally { + filesSetup.reset(); + openOptions.clear(); + } + + // When file exists. + try { + FileChannel fc = provider.newFileChannel(filesSetup.getDataFilePath(), openOptions); + assertEquals(filesSetup.TEST_FILE_DATA, readFromFileChannel(fc)); + } finally { + filesSetup.reset(); + openOptions.clear(); + } + + try { + // When file exists and READ in OpenOptions. + openOptions.add(READ); + FileChannel fc = provider.newFileChannel(filesSetup.getDataFilePath(), openOptions); + assertEquals(filesSetup.TEST_FILE_DATA, readFromFileChannel(fc)); + } finally { + filesSetup.reset(); + openOptions.clear(); + } + + // Reading from a file opened with WRITE. + try { + openOptions.add(WRITE); + FileChannel fc = provider.newFileChannel(filesSetup.getDataFilePath(), openOptions); + assertEquals(filesSetup.TEST_FILE_DATA, readFromFileChannel(fc)); + fail(); + } catch (NonReadableChannelException expected) { + } finally { + filesSetup.reset(); + openOptions.clear(); + } + + // Writing to an exiting file. + try { + openOptions.add(WRITE); + FileChannel fc = provider.newFileChannel(filesSetup.getDataFilePath(), openOptions); + writeToFileChannel(fc, filesSetup.TEST_FILE_DATA_2); + fc.close(); + assertEquals(overlayString1OnString2(TEST_FILE_DATA_2, TEST_FILE_DATA), + readFromFile(filesSetup.getDataFilePath())); + } finally { + filesSetup.reset(); + openOptions.clear(); + } + + // APPEND to an existing file. + try { + openOptions.add(WRITE); + openOptions.add(TRUNCATE_EXISTING); + FileChannel fc = provider.newFileChannel(filesSetup.getDataFilePath(), openOptions); + writeToFileChannel(fc, filesSetup.TEST_FILE_DATA_2); + fc.close(); + assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getDataFilePath())); + } finally { + filesSetup.reset(); + openOptions.clear(); + } + + // TRUNCATE an existing file. + try { + openOptions.add(WRITE); + openOptions.add(APPEND); + FileChannel fc = provider.newFileChannel(filesSetup.getDataFilePath(), openOptions); + writeToFileChannel(fc, filesSetup.TEST_FILE_DATA_2); + fc.close(); + assertEquals(TEST_FILE_DATA + TEST_FILE_DATA_2, readFromFile(filesSetup.getDataFilePath())); + } finally { + filesSetup.reset(); + openOptions.clear(); + } + } + + @Test + @Parameters(method = "parameters_test_newFileChannel_NoSuchFileException") + public void test_newFileChannel_NoSuchFileException(Set openOptions) + throws IOException { + try { + provider.newFileChannel(filesSetup.getTestPath(), openOptions); + fail(); + } catch (NoSuchFileException expected) {} + } + + @SuppressWarnings("unused") + private Object[] parameters_test_newFileChannel_NoSuchFileException() { + return new Object[] { + new Object[] { EnumSet.noneOf(StandardOpenOption.class) }, + new Object[] { EnumSet.of(READ) }, + new Object[] { EnumSet.of(WRITE) }, + new Object[] { EnumSet.of(TRUNCATE_EXISTING) }, + new Object[] { EnumSet.of(APPEND) }, + new Object[] { EnumSet.of(CREATE, READ) }, + new Object[] { EnumSet.of(CREATE, TRUNCATE_EXISTING) }, + new Object[] { EnumSet.of(CREATE, READ) }, + }; + } + + @Test + public void test_newFileChannel_withFileAttributes() throws IOException { + Set openOptions = new HashSet<>(); + FileTime fileTime = FileTime.fromMillis(System.currentTimeMillis()); + Files.setAttribute(filesSetup.getDataFilePath(), "basic:lastModifiedTime", fileTime); + FileAttribute unsupportedAttr = new MockFileAttribute<>( + "basic:lastModifiedTime", fileTime); + + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> supportedAttr = + PosixFilePermissions.asFileAttribute(perm); + + try { + // When file doesn't exists and with OpenOption CREATE & WRITE. + openOptions.clear(); + openOptions.add(CREATE); + openOptions.add(WRITE); + provider.newFileChannel(filesSetup.getTestPath(), openOptions, unsupportedAttr); + fail(); + } catch (UnsupportedOperationException expected) { + } finally { + filesSetup.reset(); + openOptions.clear(); + } + + try { + // With OpenOption CREATE & WRITE. + openOptions.clear(); + openOptions.add(CREATE); + openOptions.add(WRITE); + provider.newFileChannel(filesSetup.getTestPath(), openOptions, supportedAttr); + assertEquals(supportedAttr.value(), Files.getAttribute(filesSetup.getTestPath(), + supportedAttr.name())); + } finally { + filesSetup.reset(); + openOptions.clear(); + } + + // When file exists. + try { + provider.newFileChannel(filesSetup.getDataFilePath(), openOptions, + unsupportedAttr); + fail(); + } catch (UnsupportedOperationException expected) { + } finally { + filesSetup.reset(); + openOptions.clear(); + } + + // When file exists. No change in permissions. + try { + Set originalPermissions= (Set) + Files.getAttribute(filesSetup.getDataFilePath(), supportedAttr.name()); + FileChannel fc = provider.newFileChannel(filesSetup.getDataFilePath(), openOptions, + supportedAttr); + assertEquals(originalPermissions, Files.getAttribute(filesSetup.getDataFilePath(), + supportedAttr.name())); + } finally { + filesSetup.reset(); + openOptions.clear(); + } + } + + + @Test + public void test_newFileChannel_NPE() throws IOException { + try { + provider.newByteChannel(null, new HashSet<>(), new MockFileAttribute<>()); + fail(); + } catch (NullPointerException expected) {} + + try { + provider.newByteChannel(filesSetup.getTestPath(), null, new MockFileAttribute<>()); + fail(); + } catch (NullPointerException expected) {} + + try { + provider.newByteChannel(filesSetup.getTestPath(), new HashSet<>(), null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_getPath() throws Exception { + List inputOutputTestCases = getPath_URI_InputOutputTestData(); + for (LinuxFileSystemTestData.TestData inputOutputTestCase : inputOutputTestCases) { + assertEquals(inputOutputTestCase.output, + provider.getPath(new URI(inputOutputTestCase.input)).toString()); + } + + // When URI is null. + try { + provider.getPath(null); + fail(); + } catch (NullPointerException expected) {} + + // When Schema is not supported. + try { + provider.getPath(new URI("scheme://d")); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test + public void test_getScheme() { + assertEquals("file", provider.getScheme()); + } + + @Test + public void test_installedProviders() { + assertNotNull(provider.installedProviders()); + } + + @Test + public void test_newFileSystem$URI$Map() throws Exception { + Path testPath = Paths.get("/"); + assertNotNull(provider.getFileSystem(testPath.toUri())); + + try { + provider.getFileSystem(null); + fail(); + } catch (NullPointerException expected) {} + + // Test the case when URI has illegal scheme. + URI stubURI = new URI("scheme://path"); + try { + provider.getFileSystem(stubURI); + fail(); + } catch (IllegalArgumentException expected) {} + } + + String readFromFileChannel(FileChannel fc) throws IOException { + ByteBuffer bb = ByteBuffer.allocate(20); + fc.read(bb); + return new String(bb.array(), "UTF-8").trim(); + } + + void writeToFileChannel(FileChannel fc, String data) throws IOException { + fc.write(ByteBuffer.wrap(data.getBytes())); + } + + String overlayString1OnString2(String s1, String s2) { + return s1 + s2.substring(s1.length()); + } + + static class MockFileAttribute implements FileAttribute { + + String name; + T value; + + MockFileAttribute() { + } + + MockFileAttribute(String name, T value) { + this.name = name; + this.value = value; + } + + @Override + public String name() { + return name; + } + + @Override + public T value() { + return value; + } + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java b/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java new file mode 100644 index 000000000..01b32bec2 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java @@ -0,0 +1,827 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.NonReadableChannelException; +import java.nio.channels.NonWritableChannelException; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.CopyOption; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.NotDirectoryException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SecureDirectoryStream; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.nio.file.spi.FileSystemProvider; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; +import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.nio.file.StandardOpenOption.APPEND; +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.CREATE_NEW; +import static java.nio.file.StandardOpenOption.DELETE_ON_CLOSE; +import static java.nio.file.StandardOpenOption.DSYNC; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.SPARSE; +import static java.nio.file.StandardOpenOption.SYNC; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; +import static java.nio.file.StandardOpenOption.WRITE; +import static libcore.java.nio.file.FilesSetup.DATA_FILE; +import static libcore.java.nio.file.FilesSetup.NonStandardOption; +import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA; +import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA_2; +import static libcore.java.nio.file.FilesSetup.readFromFile; +import static libcore.java.nio.file.FilesSetup.readFromInputStream; +import static libcore.java.nio.file.FilesSetup.writeToFile; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class DefaultFileSystemProviderTest { + + @Rule + public FilesSetup filesSetup = new FilesSetup(); + + private FileSystemProvider provider; + + @Before + public void setUp() throws Exception { + provider = filesSetup.getDataFilePath().getFileSystem().provider(); + } + + @Test + public void test_newInputStream() throws IOException { + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), READ)) { + assertEquals(TEST_FILE_DATA, readFromInputStream(is)); + } + } + + @Test + public void test_newInputStream_openOption() throws IOException { + // Write and Append are not supported. + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), WRITE)) { + fail(); + } catch (UnsupportedOperationException expected) { + } + + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), APPEND)) { + fail(); + } catch (UnsupportedOperationException expected) { + } + + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), + NonStandardOption.OPTION1)){ + fail(); + } catch (UnsupportedOperationException expected) { + } + + // Supported options. + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), DELETE_ON_CLOSE, + CREATE_NEW, TRUNCATE_EXISTING, SPARSE, SYNC, DSYNC)) { + assertEquals(TEST_FILE_DATA, readFromInputStream(is)); + } + } + + @Test + public void test_newInputStream_twice() throws IOException { + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), READ); + // Open the same file again. + InputStream is2 = provider.newInputStream(filesSetup.getDataFilePath(), READ)) { + + assertEquals(TEST_FILE_DATA, readFromInputStream(is)); + assertEquals(TEST_FILE_DATA, readFromInputStream(is2)); + } + } + + @Test + public void test_newInputStream_NPE() throws IOException { + try (InputStream is = provider.newInputStream(null)){ + fail(); + } catch (NullPointerException expected) {} + + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), + (OpenOption[]) null)) { + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_newOutputStream() throws IOException { + try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath())) { + os.write(TEST_FILE_DATA.getBytes()); + } + + try (InputStream is = provider.newInputStream(filesSetup.getTestPath())) { + assertEquals(TEST_FILE_DATA, readFromInputStream(is)); + } + } + + @Test + public void test_newOutputStream_openOption_READ() throws IOException { + try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), READ)) { + fail(); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void test_newOutputStream_openOption_APPEND() throws IOException { + // When file exists and it contains data. + try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), APPEND)) { + os.write(TEST_FILE_DATA.getBytes()); + } + + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) { + assertEquals(TEST_FILE_DATA + TEST_FILE_DATA, readFromInputStream(is)); + } + + // When file doesn't exist. + try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), APPEND)) { + fail(); + } catch (NoSuchFileException expected) { + assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString())); + } + } + + @Test + public void test_newOutputStream_openOption_TRUNCATE() throws IOException { + // When file exists. + try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), + TRUNCATE_EXISTING)) { + os.write(TEST_FILE_DATA_2.getBytes()); + } + + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) { + assertEquals(TEST_FILE_DATA_2, readFromInputStream(is)); + } + + // When file doesn't exist. + try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), + TRUNCATE_EXISTING)) { + fail(); + } catch (NoSuchFileException expected) { + assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString())); + } + } + + @Test + public void test_newOutputStream_openOption_WRITE() throws IOException { + // When file exists. + try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), WRITE)) { + os.write(TEST_FILE_DATA_2.getBytes()); + } + + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) { + String expectedFileData = TEST_FILE_DATA_2 + + TEST_FILE_DATA.substring(TEST_FILE_DATA_2.length()); + assertEquals(expectedFileData, readFromInputStream(is)); + } + + // When file doesn't exist. + try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), WRITE)) { + fail(); + } catch (NoSuchFileException expected) { + assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString())); + } + } + + @Test + public void test_newOutputStream_openOption_CREATE() throws IOException { + // When file exists. + try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), CREATE)) { + os.write(TEST_FILE_DATA_2.getBytes()); + } + + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) { + String expectedFileData = TEST_FILE_DATA_2 + + TEST_FILE_DATA.substring(TEST_FILE_DATA_2.length()); + assertEquals(expectedFileData, readFromInputStream(is)); + } + + // When file doesn't exist. + try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), CREATE)) { + os.write(TEST_FILE_DATA.getBytes()); + } + + try (InputStream is = provider.newInputStream(filesSetup.getTestPath())) { + assertEquals(TEST_FILE_DATA, readFromInputStream(is)); + } + } + + @Test + public void test_newOutputStream_openOption_CREATE_NEW() throws IOException { + // When file exists. + try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), CREATE_NEW)) { + fail(); + } catch (FileAlreadyExistsException expected) { + } + + // When file doesn't exist. + try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), CREATE_NEW)) { + os.write(TEST_FILE_DATA.getBytes()); + } + + try (InputStream is = provider.newInputStream(filesSetup.getTestPath())) { + assertEquals(TEST_FILE_DATA, readFromInputStream(is)); + } + } + + @Test + public void test_newOutputStream_openOption_SYNC() throws IOException { + // The data should be written to the file + try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), CREATE, SYNC); + InputStream is = provider.newInputStream(filesSetup.getTestPath(), SYNC)) { + os.write(TEST_FILE_DATA.getBytes()); + assertEquals(TEST_FILE_DATA, readFromInputStream(is)); + } + } + + @Test + public void test_newOutputStream_NPE() throws IOException { + try (OutputStream os = provider.newOutputStream(null)) { + fail(); + } catch (NullPointerException expected) {} + + try (OutputStream os = provider + .newOutputStream(filesSetup.getTestPath(), (OpenOption[]) null)) { + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_newByteChannel() throws IOException { + Set set = new HashSet(); + + // When file doesn't exist + try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getTestPath(), set)) { + fail(); + } catch (NoSuchFileException expected) { + assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString())); + } + + // When file exists. + + // File opens in READ mode by default. The channel is non writable by default. + try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) { + sbc.write(ByteBuffer.allocate(10)); + fail(); + } catch (NonWritableChannelException expected) { + } + + // Read a file. + try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) { + ByteBuffer readBuffer = ByteBuffer.allocate(10); + int bytesReadCount = sbc.read(readBuffer); + + String readData = new String(Arrays.copyOf(readBuffer.array(), bytesReadCount), + "UTF-8"); + assertEquals(TEST_FILE_DATA, readData); + } + } + + /** + * Behaviour of newByteChannel when called with OpenOption#WRITE. + * @throws IOException + */ + @Test + public void test_newByteChannel_openOption_WRITE() throws IOException { + Set set = new HashSet(); + set.add(WRITE); + + // When file doesn't exist + try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getTestPath(), set)) { + fail(); + } catch (NoSuchFileException expected) { + assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString())); + } + + + // When file exists. + try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) { + sbc.read(ByteBuffer.allocate(10)); + fail(); + } catch (NonReadableChannelException expected) { + } + + // Write in file. + try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) { + sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes())); + } + + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) { + String expectedFileData = TEST_FILE_DATA_2 + + TEST_FILE_DATA.substring(TEST_FILE_DATA_2.length()); + assertEquals(expectedFileData, readFromInputStream(is)); + } + } + + /** + * Check behaviour when newByteChannel is called with WRITE, READ and SYNC. + * @throws IOException + */ + @Test + public void test_newByteChannel_openOption_WRITE_READ() throws IOException { + Set set = new HashSet(); + set.add(WRITE); + set.add(READ); + set.add(SYNC); + + try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) { + ByteBuffer readBuffer = ByteBuffer.allocate(10); + int bytesReadCount = sbc.read(readBuffer); + + String readData = new String(Arrays.copyOf(readBuffer.array(), bytesReadCount), + "UTF-8"); + assertEquals(TEST_FILE_DATA, readData); + + // Pointer will move to the end of the file after read operation. The write should + // append the data at the end of the file. + sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes())); + } + + try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) { + String expectedFileData = TEST_FILE_DATA + TEST_FILE_DATA_2; + assertEquals(expectedFileData, readFromInputStream(is)); + } + } + + @Test + public void test_newByteChannel_NPE() throws IOException { + Set set = new HashSet(); + try (SeekableByteChannel sbc = provider.newByteChannel(null, set)) { + fail(); + } catch(NullPointerException expected) {} + + try (SeekableByteChannel sbc = provider + .newByteChannel(filesSetup.getDataFilePath(), null)) { + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_createDirectory() throws IOException { + // Check if createDirectory is actually creating a directory. + Path newDirectory = filesSetup.getPathInTestDir("newDir"); + assertFalse(Files.exists(newDirectory)); + assertFalse(Files.isDirectory(newDirectory)); + + provider.createDirectory(newDirectory); + + assertTrue(Files.exists(newDirectory)); + assertTrue(Files.isDirectory(newDirectory)); + + // Expecting exception when directory already exists. + try { + provider.createDirectory(newDirectory); + fail(); + } catch (FileAlreadyExistsException expected) { + } + + // File with unicode name. + Path unicodeFilePath = filesSetup.getPathInTestDir("टेस्ट डायरेक्टरी"); + provider.createDirectory(unicodeFilePath); + assertTrue(Files.exists(unicodeFilePath)); + } + + @Test + public void test_createDirectory$String$FileAttr() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + provider.createDirectory(filesSetup.getTestPath(), attr); + assertEquals(attr.value(), Files.getAttribute(filesSetup.getTestPath(), attr.name())); + + // Creating a new file and passing multiple attribute of the same name. + perm = PosixFilePermissions.fromString("rw-------"); + FileAttribute> attr1 = PosixFilePermissions.asFileAttribute(perm); + Path dirPath2 = filesSetup.getPathInTestDir("new_file"); + provider.createDirectory(dirPath2, attr, attr1); + // Value should be equal to the last attribute passed. + assertEquals(attr1.value(), Files.getAttribute(dirPath2, attr.name())); + } + + @Test + public void test_createDirectory$String$FileAttr_NPE() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + try { + provider.createDirectory(null, attr); + fail(); + } catch(NullPointerException expected) {} + + try { + provider.createDirectory(filesSetup.getTestPath(), (FileAttribute[]) null); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_createDirectory_NPE() throws IOException { + try { + provider.createDirectory(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_createSymbolicLink() throws IOException { + provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(), + /* Path of the target of the symbolic link */ + filesSetup.getDataFilePath().toAbsolutePath()); + assertTrue(Files.isSymbolicLink(filesSetup.getTestPath())); + + // When file exists at the sym link location. + try { + provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(), + /* Path of the target of the symbolic link */ + filesSetup.getDataFilePath().toAbsolutePath()); + fail(); + } catch (FileAlreadyExistsException expected) {} finally { + Files.deleteIfExists(filesSetup.getTestPath()); + } + + // Sym link to itself + provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(), + /* Path of the target of the symbolic link */ + filesSetup.getTestPath().toAbsolutePath()); + assertTrue(Files.isSymbolicLink(filesSetup.getTestPath().toAbsolutePath())); + } + + @Test + public void test_createSymbolicLink_NPE() throws IOException { + try { + provider.createSymbolicLink(null, filesSetup.getDataFilePath().toAbsolutePath()); + fail(); + } catch (NullPointerException expected) {} + + try { + provider.createSymbolicLink(filesSetup.getTestPath(), null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_createSymbolicLink$Path$Attr() throws IOException { + try { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions + .asFileAttribute(perm); + provider.createSymbolicLink(filesSetup.getTestPath(), + filesSetup.getDataFilePath().toAbsolutePath(), attr); + fail(); + } catch (UnsupportedOperationException expected) {} + } + + @Test + public void test_createSymbolicLink$Path$Attr_NPE() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions + .asFileAttribute(perm); + + try { + provider.createSymbolicLink(null, filesSetup.getDataFilePath().toAbsolutePath(), attr); + fail(); + } catch (NullPointerException expected) {} + + try { + provider.createSymbolicLink(filesSetup.getTestPath(), null, attr); + fail(); + + } catch (NullPointerException expected) {} + + try { + provider.createSymbolicLink(filesSetup.getTestPath(), filesSetup.getDataFilePath(), + (FileAttribute[]) null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_delete() throws IOException { + // Delete existing file. + provider.delete(filesSetup.getDataFilePath()); + assertFalse(Files.exists(filesSetup.getDataFilePath())); + + // Delete non existing files. + try { + provider.delete(filesSetup.getTestPath()); + fail(); + } catch (NoSuchFileException expected) { + assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString())); + } + + // Delete a directory. + Path dirPath = filesSetup.getPathInTestDir("dir"); + Files.createDirectory(dirPath); + provider.delete(dirPath); + assertFalse(Files.exists(dirPath)); + + + // Delete a non empty directory. + Files.createDirectory(dirPath); + Files.createFile(filesSetup.getPathInTestDir("dir/file")); + try { + provider.delete(dirPath); + fail(); + } catch (DirectoryNotEmptyException expected) {} + } + + @Test + public void test_delete_NPE() throws IOException { + try { + provider.delete(null); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_deleteIfExist() throws IOException { + // Delete existing file. + assertTrue(Files.deleteIfExists(filesSetup.getDataFilePath())); + assertFalse(Files.exists(filesSetup.getDataFilePath())); + + // Delete non existing files. + assertFalse(Files.deleteIfExists(filesSetup.getTestPath())); + + // Delete a directory. + Path dirPath = filesSetup.getPathInTestDir("dir"); + Files.createDirectory(dirPath); + assertTrue(Files.deleteIfExists(dirPath)); + assertFalse(Files.exists(dirPath)); + + // Delete a non empty directory. + Files.createDirectory(dirPath); + Files.createFile(filesSetup.getPathInTestDir("dir/file")); + try { + provider.deleteIfExists(dirPath); + fail(); + } catch (DirectoryNotEmptyException expected) {} + } + + @Test + public void test_deleteIfExist_NPE() throws IOException { + try { + provider.deleteIfExists(null); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_copy() throws IOException { + provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath()); + assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath())); + // The original file should also exists. + assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getDataFilePath())); + + // When target file exists. + try { + provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath()); + fail(); + } catch (FileAlreadyExistsException expected) {} + + // Copy to existing target file with REPLACE_EXISTING copy option. + writeToFile(filesSetup.getDataFilePath(), TEST_FILE_DATA_2); + provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), REPLACE_EXISTING); + assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getTestPath())); + + + // Copy to the same file. Should not fail. + filesSetup.reset(); + provider.copy(filesSetup.getDataFilePath(), filesSetup.getDataFilePath()); + assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getDataFilePath())); + + // With target is a symbolic link file. + try { + filesSetup.reset(); + Path symlink = filesSetup.getPathInTestDir("symlink"); + Path newFile = filesSetup.getPathInTestDir("newDir"); + Files.createFile(newFile); + assertTrue(Files.exists(newFile)); + Files.createSymbolicLink(symlink, filesSetup.getDataFilePath()); + provider.copy(filesSetup.getDataFilePath(), symlink); + fail(); + } catch (FileAlreadyExistsException expected) {} + + filesSetup.reset(); + try { + provider.copy(filesSetup.getTestPath(), filesSetup.getDataFilePath(), REPLACE_EXISTING); + fail(); + } catch (NoSuchFileException expected) { + assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString())); + } + } + + @Test + public void test_copy_NPE() throws IOException { + try { + provider.copy((Path) null, filesSetup.getTestPath()); + fail(); + } catch(NullPointerException expected) {} + + try { + provider.copy(filesSetup.getDataFilePath(), (Path) null); + fail(); + } catch(NullPointerException expected) {} + + try { + provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), + (CopyOption[]) null); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_copy_CopyOption() throws IOException { + // COPY_ATTRIBUTES + FileTime fileTime = FileTime.fromMillis(System.currentTimeMillis() - 10000); + Files.setAttribute(filesSetup.getDataFilePath(), "basic:lastModifiedTime", fileTime); + provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), COPY_ATTRIBUTES); + assertEquals(fileTime.to(TimeUnit.SECONDS), + ((FileTime) Files.getAttribute(filesSetup.getTestPath(), + "basic:lastModifiedTime")).to(TimeUnit.SECONDS)); + assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath())); + + // ATOMIC_MOVE + Files.deleteIfExists(filesSetup.getTestPath()); + try { + provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), ATOMIC_MOVE); + fail(); + } catch (UnsupportedOperationException expected) {} + + Files.deleteIfExists(filesSetup.getTestPath()); + try { + provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), + NonStandardOption.OPTION1); + fail(); + } catch (UnsupportedOperationException expected) {} + } + + @Test + public void test_copy_directory() throws IOException { + final Path dirPath = filesSetup.getPathInTestDir("dir1"); + final Path dirPath2 = filesSetup.getPathInTestDir("dir2"); + // Nested directory. + final Path dirPath3 = filesSetup.getPathInTestDir("dir1/dir"); + + // Create dir1 and dir1/dir, and copying dir1/dir to dir2. Copy will create dir2, however, + // it will not copy the content of the source directory. + Files.createDirectory(dirPath); + Files.createDirectory(dirPath3); + provider.copy(filesSetup.getDataFilePath(), + filesSetup.getPathInTestDir("dir1/" + DATA_FILE)); + provider.copy(dirPath, dirPath2); + assertTrue(Files.exists(dirPath2)); + + Map pathMap = new HashMap<>(); + try (DirectoryStream directoryStream = Files.newDirectoryStream(dirPath2)) { + directoryStream.forEach(file -> pathMap.put(file, true)); + } + + // The files are not copied. The command is equivalent of creating a new directory. + assertEquals(0, pathMap.size()); + + + // When the target directory is not empty. + Path dirPath4 = filesSetup.getPathInTestDir("dir4"); + Files.createDirectories(dirPath4); + Path file = Paths.get("file"); + Files.createFile(Paths.get(dirPath.toString(), file.toString())); + Files.createFile(Paths.get(dirPath4.toString(), file.toString())); + + try { + provider.copy(dirPath, dirPath4, REPLACE_EXISTING); + fail(); + } catch (DirectoryNotEmptyException expected) {} + } + + @Test + public void test_newDirectoryStream$Path$Filter() throws IOException { + + // Initial setup of directory. + Path path_root = filesSetup.getPathInTestDir("dir"); + Path path_dir1 = filesSetup.getPathInTestDir("dir/dir1"); + Path path_dir2 = filesSetup.getPathInTestDir("dir/dir2"); + Path path_dir3 = filesSetup.getPathInTestDir("dir/dir3"); + + Path path_f1 = filesSetup.getPathInTestDir("dir/f1"); + Path path_f2 = filesSetup.getPathInTestDir("dir/f2"); + Path path_f3 = filesSetup.getPathInTestDir("dir/f3"); + + Files.createDirectory(path_root); + Files.createDirectory(path_dir1); + Files.createDirectory(path_dir2); + Files.createDirectory(path_dir3); + Files.createFile(path_f1); + Files.createFile(path_f2); + Files.createFile(path_f3); + + HashSet pathsSet = new HashSet<>(); + HashSet expectedPathsSet = new HashSet<>(); + + expectedPathsSet.add(path_dir1); + expectedPathsSet.add(path_dir2); + expectedPathsSet.add(path_dir3); + + // Filter all the directories. + try (DirectoryStream directoryStream = provider.newDirectoryStream(path_root, + file -> Files.isDirectory(file))) { + assertTrue(directoryStream instanceof SecureDirectoryStream); + directoryStream.forEach(path -> pathsSet.add(path)); + + assertEquals(expectedPathsSet, pathsSet); + } + } + + /** + * Tests exceptions for the newDirectoryStream(Path, DirectoryStream.Filter) method + * - NoSuchFileException & NoDirectoryException. + * @throws IOException + */ + @Test + public void test_newDirectoryStream$Filter_Exception() throws IOException { + // Non existent directory. + Path path_dir1 = filesSetup.getPathInTestDir("newDir1"); + DirectoryStream.Filter fileFilter = new DirectoryStream.Filter() { + @Override + public boolean accept(Path entry) throws IOException { + return Files.isDirectory(entry); + } + }; + + try (DirectoryStream directoryStream = provider.newDirectoryStream(path_dir1, + fileFilter)) { + fail(); + } catch (NoSuchFileException expected) { + assertTrue(expected.getMessage().contains(path_dir1.toString())); + } + + // File instead of directory. + Path path_file1 = filesSetup.getPathInTestDir("newFile1"); + Files.createFile(path_file1); + try (DirectoryStream directoryStream = provider.newDirectoryStream(path_file1, + fileFilter)) { + fail(); + } catch (NotDirectoryException expected) { + } + } + + @Test + public void test_newDirectoryStream$Filter_NPE() throws IOException { + DirectoryStream.Filter fileFilter = new DirectoryStream.Filter() { + @Override + public boolean accept(Path entry) throws IOException { + return Files.isDirectory(entry); + } + }; + try (DirectoryStream directoryStream = provider.newDirectoryStream(null, + fileFilter)) { + fail(); + } catch (NullPointerException expected) { + } + + // Non existent directory. + Path path_dir1 = filesSetup.getPathInTestDir("newDir1"); + try (DirectoryStream directoryStream = provider.newDirectoryStream(path_dir1, + (DirectoryStream.Filter) null)) { + fail(); + } catch (NullPointerException expected) { + } + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/DefaultSecureDirectoryStreamTest.java b/luni/src/test/java/libcore/java/nio/file/DefaultSecureDirectoryStreamTest.java new file mode 100644 index 000000000..2c9c2cdef --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/DefaultSecureDirectoryStreamTest.java @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.NonWritableChannelException; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.ClosedDirectoryStreamException; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystemException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.NoSuchFileException; +import java.nio.file.NotDirectoryException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SecureDirectoryStream; +import java.nio.file.attribute.BasicFileAttributeView; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.LeakageDetectorRule; + +import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA; +import static libcore.java.nio.file.FilesSetup.writeToFile; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class DefaultSecureDirectoryStreamTest { + + Path path_root; + Path path_dir1; + Path path_dir2; + Path path_dir3; + Path path_f1; + Path path_f2; + Path path_f3; + Path path_dir4; + + @Rule + public FilesSetup filesSetup = new FilesSetup(); + @Rule + public LeakageDetectorRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule(); + + @Before + public void setup() throws Exception { + + // Initial setup of directory. + path_root = filesSetup.getPathInTestDir("dir"); + path_dir1 = filesSetup.getPathInTestDir("dir/dir1"); + path_dir2 = filesSetup.getPathInTestDir("dir/dir2"); + path_dir3 = filesSetup.getPathInTestDir("dir/dir3"); + path_dir4 = filesSetup.getPathInTestDir("dir/dir1/dir4"); + + path_f1 = filesSetup.getPathInTestDir("dir/f1"); + path_f2 = filesSetup.getPathInTestDir("dir/f2"); + path_f3 = filesSetup.getPathInTestDir("dir/f3"); + + Files.createDirectory(path_root); + Files.createDirectory(path_dir1); + Files.createDirectory(path_dir2); + Files.createDirectory(path_dir3); + Files.createDirectory(path_dir4); + Files.createFile(path_f1); + Files.createFile(path_f2); + Files.createFile(path_f3); + } + + @Test + public void testIterator() throws IOException { + HashSet pathsSet = new HashSet<>(); + HashSet expectedPathsSet = new HashSet<>(); + + expectedPathsSet.add(path_dir1); + expectedPathsSet.add(path_dir2); + expectedPathsSet.add(path_dir3); + + // Filter all the directories. + try (DirectoryStream directoryStream = Files.newDirectoryStream(path_root, + file -> Files.isDirectory(file))) { + Iterator directoryStreamIterator = directoryStream.iterator(); + directoryStreamIterator.forEachRemaining(path -> pathsSet.add(path)); + assertEquals(expectedPathsSet, pathsSet); + } + } + + @Test + public void testIterator_calledTwice() throws IOException { + try (DirectoryStream directoryStream = Files.newDirectoryStream(path_root, + file -> Files.isDirectory(file))) { + directoryStream.iterator(); + try { + directoryStream.iterator(); + fail(); + } catch (IllegalStateException expected) {} + } + } + + @Test + public void testIterator_afterClose() throws IOException { + DirectoryStream directoryStream = Files.newDirectoryStream(path_root, + file -> Files.isDirectory(file)); + directoryStream.close(); + try { + directoryStream.iterator(); + fail(); + } catch (IllegalStateException expected) {} + } + + @Test + public void test_newDirectoryStream() throws IOException { + HashSet pathsSet = new HashSet<>(); + HashSet expectedPathsSet = new HashSet<>(); + + expectedPathsSet.add(path_dir4); + + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root); + DirectoryStream ds_path_dir1 = ds_path_root.newDirectoryStream(path_root. + relativize(path_dir1))) { + + ds_path_dir1.forEach(path -> pathsSet.add(path)); + assertEquals(expectedPathsSet, pathsSet); + } + } + + @Test + public void test_newDirectoryStream_symbolicLink() throws IOException { + Path symlinkPath = Paths.get(path_dir1.toString(), "symlink"); + Files.createSymbolicLink(symlinkPath, path_dir3); + assertTrue(Files.isSymbolicLink(symlinkPath)); + + try (SecureDirectoryStream ds_path_dir1 = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + try (DirectoryStream ds_path_dir2 = ds_path_dir1.newDirectoryStream(path_root. + relativize(symlinkPath), LinkOption.NOFOLLOW_LINKS)) { + fail(); + } catch (FileSystemException expected) {} + } + } + + @Test + public void test_newDirectoryStream_Exception() throws IOException { + + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + // When file is not a directory. + try (DirectoryStream ds_path_dir1 = ds_path_root.newDirectoryStream(path_root. + relativize(path_f1))) { + fail(); + } catch (NotDirectoryException expected) {} + + + // NPE + try (DirectoryStream ds_path_dir1 = ds_path_root.newDirectoryStream(null)) { + fail(); + } catch (NullPointerException expected) {} + + // NPE + try (DirectoryStream ds_path_dir1 = ds_path_root.newDirectoryStream(path_root. + relativize(path_f1), null)) { + fail(); + } catch (NullPointerException expected) {} + + // When stream is closed. + ds_path_root.close(); + try (DirectoryStream ds_path_dir1 = ds_path_root.newDirectoryStream(path_root. + relativize(path_dir1))) { + fail(); + } catch (ClosedDirectoryStreamException expected) {} + } + } + + @Test + public void test_newByteChannel() throws IOException { + Set set = new HashSet(); + + // When file doesn't exist. + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + + try (SeekableByteChannel sbc = ds_path_root.newByteChannel(filesSetup.getTestPath(), + set)) { + fail(); + } catch (NoSuchFileException expected) { + assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString())); + } + + // When file exists. + // File opens in READ mode by default. The channel is non writable by default. + try (SeekableByteChannel sbc = ds_path_root.newByteChannel( + path_root.relativize(path_f1), set)) { + sbc.write(ByteBuffer.allocate(10)); + fail(); + } catch (NonWritableChannelException expected) { + } + + // Read a file. + writeToFile(path_f1, TEST_FILE_DATA); + try (SeekableByteChannel sbc = ds_path_root.newByteChannel( + path_root.relativize(path_f1), set)) { + ByteBuffer readBuffer = ByteBuffer.allocate(10); + int bytesReadCount = sbc.read(readBuffer); + + String readData = new String(Arrays.copyOf(readBuffer.array(), bytesReadCount), + "UTF-8"); + assertEquals(TEST_FILE_DATA, readData); + } + + // when directory stream is closed. + ds_path_root.close(); + try (SeekableByteChannel sbc = ds_path_root.newByteChannel( + path_root.relativize(path_f1), set)) { + fail(); + } catch (ClosedDirectoryStreamException expected) {} + } + } + + @Test + public void test_newByteChannel_NPE() throws IOException { + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + Set set = new HashSet(); + try (SeekableByteChannel sbc = ds_path_root.newByteChannel(null, set)) { + fail(); + } catch (NullPointerException expected) { + } + + try (SeekableByteChannel sbc = ds_path_root.newByteChannel(filesSetup.getDataFilePath(), + null)) { + fail(); + } catch (NullPointerException expected) { + } + } + } + + @Test + public void test_deleteFile() throws IOException { + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + ds_path_root.deleteFile(path_root.relativize(path_f1)); + assertFalse(Files.exists(path_f1)); + + // --- Exceptions --- + // When the file is a directory. + try { + ds_path_root.deleteFile(path_root.relativize(path_dir1)); + fail(); + } catch (FileSystemException expected) {} + + // When file doesn't exists. + try { + ds_path_root.deleteFile(filesSetup.getTestPath()); + fail(); + } catch (NoSuchFileException expected) {} + + // NullPointerException + try { + ds_path_root.deleteFile(null); + fail(); + } catch (NullPointerException expected) {} + + // When the directory stream is closed. + ds_path_root.close(); + try { + ds_path_root.deleteFile(path_root.relativize(path_f2)); + fail(); + } catch (ClosedDirectoryStreamException expected) {} + + } + } + + @Test + public void test_deleteDirectory() throws IOException { + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + ds_path_root.deleteDirectory(path_root.relativize(path_dir2)); + assertFalse(Files.exists(path_dir2)); + + // When file is not a directory. + try { + ds_path_root.deleteDirectory(path_root.relativize(path_f1)); + fail(); + } catch (FileSystemException expected) {} + + // When path doesn't exists. + try { + ds_path_root.deleteDirectory(filesSetup.getTestPath()); + fail(); + } catch (NoSuchFileException expected) {} + + // When the directory is not empty. + try { + ds_path_root.deleteDirectory(path_root.relativize(path_dir1)); + fail(); + } catch (DirectoryNotEmptyException expected) {} + + // --- Exceptions --- + // NullPointerException + try { + ds_path_root.deleteDirectory(null); + fail(); + } catch (NullPointerException expected) {} + + // When the directory stream is closed. + ds_path_root.close(); + try { + ds_path_root.deleteDirectory(path_root.relativize(path_f2)); + fail(); + } catch (ClosedDirectoryStreamException expected) {} + } + } + + @Test + public void test_move() throws IOException { + SecureDirectoryStream ds_path_dir1 = (SecureDirectoryStream) + Files.newDirectoryStream(path_dir1); + + // moving a file. + ds_path_dir1.move(path_f1, ds_path_dir1, Paths.get("f1")); + assertTrue(Files.exists(Paths.get(path_dir1.toString(), "f1"))); + assertFalse(Files.exists(path_f1)); + + // moving a directory. + ds_path_dir1.move(path_dir2, ds_path_dir1, Paths.get(path_dir4.toString(), "path_dir2")); + assertTrue(Files.exists(Paths.get(path_dir4.toString(), "path_dir2"))); + assertFalse(Files.exists(path_dir2)); + + // when directory already exists of the same name. + ds_path_dir1.move(path_dir3, ds_path_dir1, Paths.get("path_dir2")); + assertTrue(Files.exists(Paths.get(path_dir1.toString(), "path_dir2"))); + assertFalse(Files.exists(path_dir3)); + + // moving a non empty directory. + ds_path_dir1.move(path_dir1, ds_path_dir1, Paths.get(path_root.getParent().toString(), + "path_dir1")); + assertTrue(Files.exists(Paths.get(path_root.getParent().toString(), "path_dir1"))); + assertFalse(Files.exists(path_dir1)); + + // --- Exceptions --- + // NullPointerException. + try { + ds_path_dir1.move(null, ds_path_dir1, + Paths.get(path_root.getParent().toString(), "path_dir1")); + fail(); + } catch (NullPointerException expected) {} + + try { + ds_path_dir1.move(path_dir1, null, + Paths.get(path_root.getParent().toString(), "path_dir1")); + fail(); + } catch (NullPointerException expected) {} + + try { + ds_path_dir1.move(path_dir1, ds_path_dir1, + Paths.get(path_root.getParent().toString(), null)); + fail(); + } catch (NullPointerException expected) {} + + try { + // when targetDir stream is closed. + ds_path_dir1.close(); + ds_path_dir1.move(path_root, ds_path_dir1, path_f3); + fail(); + } catch (ClosedDirectoryStreamException expected) {} + } + + @Test + public void test_getFileAttributeView() throws IOException { + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + BasicFileAttributeView fileAttributeView = ds_path_root + .getFileAttributeView(BasicFileAttributeView.class); + + assertFalse(fileAttributeView.readAttributes().isRegularFile()); + assertTrue(fileAttributeView.readAttributes().isDirectory()); + assertFalse(fileAttributeView.readAttributes().isSymbolicLink()); + + // --- Exceptions --- + // NullPointerException + try { + ds_path_root.getFileAttributeView(null); + } catch (NullPointerException expected) {} + + // When directory stream is closed. + ds_path_root.close(); + fileAttributeView = ds_path_root.getFileAttributeView(BasicFileAttributeView.class); + try { + fileAttributeView.readAttributes(); + fail(); + } catch (ClosedDirectoryStreamException expected) {} + } + } + + @Test + public void test_getFileAttributeView_Path() throws IOException { + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + BasicFileAttributeView fileAttributeView = ds_path_root.getFileAttributeView( + path_root.relativize(path_dir1), BasicFileAttributeView.class); + + assertFalse(fileAttributeView.readAttributes().isRegularFile()); + assertTrue(fileAttributeView.readAttributes().isDirectory()); + assertFalse(fileAttributeView.readAttributes().isSymbolicLink()); + + fileAttributeView = ds_path_root.getFileAttributeView(path_root.relativize(path_f1), + BasicFileAttributeView.class); + + assertTrue(fileAttributeView.readAttributes().isRegularFile()); + assertFalse(fileAttributeView.readAttributes().isDirectory()); + assertFalse(fileAttributeView.readAttributes().isSymbolicLink()); + + // When file is a symbolic link. + Path symlinkPath = Paths.get(path_root.toString(), "symlink"); + Files.createSymbolicLink(symlinkPath, path_dir1); + assertTrue(Files.isSymbolicLink(symlinkPath)); + // When file is a symbolic link and method is invoked with LinkOptions.NOFOLLOW_LINKS. + fileAttributeView = ds_path_root.getFileAttributeView(path_root.relativize(symlinkPath), + BasicFileAttributeView.class); + assertTrue(fileAttributeView.readAttributes().isDirectory()); + + // --- Exceptions --- + try { + ds_path_root.getFileAttributeView(null, BasicFileAttributeView.class); + fail(); + } catch (NullPointerException expected) {} + + try { + ds_path_root.getFileAttributeView(path_root.relativize(path_f1), null); + fail(); + } catch (NullPointerException expected) {} + + // When directory stream is closed. + ds_path_root.close(); + fileAttributeView = ds_path_root.getFileAttributeView(path_root.relativize(path_f1), + BasicFileAttributeView.class); + try { + fileAttributeView.readAttributes(); + fail(); + } catch (ClosedDirectoryStreamException expected) {} + } + } + + @Test + public void test_getFileAttributeView_Path_LinkOptions() throws IOException { + Path symlinkPath = Paths.get(path_root.toString(), "symlink"); + Files.createSymbolicLink(symlinkPath, path_dir1); + assertTrue(Files.isSymbolicLink(symlinkPath)); + // When file is a symbolic link and method is invoked with LinkOptions.NOFOLLOW_LINKS. + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + BasicFileAttributeView fileAttributeView = ds_path_root.getFileAttributeView( + path_root.relativize(symlinkPath), BasicFileAttributeView.class, + LinkOption.NOFOLLOW_LINKS); + assertTrue(fileAttributeView.readAttributes().isSymbolicLink()); + } + + // When file is not a symbolic link. + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + BasicFileAttributeView fileAttributeView = ds_path_root + .getFileAttributeView(path_root.relativize(path_f1), + BasicFileAttributeView.class, LinkOption.NOFOLLOW_LINKS); + assertTrue(fileAttributeView.readAttributes().isRegularFile()); + assertFalse(fileAttributeView.readAttributes().isDirectory()); + assertFalse(fileAttributeView.readAttributes().isSymbolicLink()); + } + + // --- Exceptions --- + // NullPointerException + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + ds_path_root.getFileAttributeView(path_root.relativize(path_f1), + BasicFileAttributeView.class, null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void testUnixSecureDirectoryStreamHasFinalizer() throws IOException { + try (SecureDirectoryStream ds_path_root = (SecureDirectoryStream) + Files.newDirectoryStream(path_root)) { + resourceLeakageDetectorRule.assertUnreleasedResourceCount(ds_path_root, 1); + } + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/DirectoryIteratorExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/DirectoryIteratorExceptionTest.java new file mode 100644 index 000000000..8fb25710f --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/DirectoryIteratorExceptionTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.DirectoryIteratorException; + +public class DirectoryIteratorExceptionTest extends TestCase { + + public void test_constructor() { + IOException ioException = new IOException(); + DirectoryIteratorException exception = new DirectoryIteratorException(ioException); + + assertSame(ioException, exception.getCause()); + + try { + new DirectoryIteratorException(null); + fail(); + } catch (NullPointerException expected) {} + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/DirectoryNotEmptyExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/DirectoryNotEmptyExceptionTest.java new file mode 100644 index 000000000..370475d92 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/DirectoryNotEmptyExceptionTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.FileSystemException; +import libcore.util.SerializationTester; + +public class DirectoryNotEmptyExceptionTest extends TestCase { + + public void test_constructor$String() { + DirectoryNotEmptyException exception = new DirectoryNotEmptyException("file"); + assertEquals("file", exception.getFile()); + assertNull(exception.getOtherFile()); + assertNull(exception.getReason()); + + assertTrue(exception instanceof FileSystemException); + } + + public void test_serialization() throws IOException, ClassNotFoundException { + String hex = "aced0005737200286a6176612e6e696f2e66696c652e4469726563746f72794e6f74456d70747" + + "9457863657074696f6e2a6b773c0727657b020000787200216a6176612e6e696f2e66696c652e466" + + "96c6553797374656d457863657074696f6ed598f27876d360fc0200024c000466696c657400124c6" + + "a6176612f6c616e672f537472696e673b4c00056f7468657271007e0002787200136a6176612e696" + + "f2e494f457863657074696f6e6c8073646525f0ab020000787200136a6176612e6c616e672e45786" + + "3657074696f6ed0fd1f3e1a3b1cc4020000787200136a6176612e6c616e672e5468726f7761626c6" + + "5d5c635273977b8cb0300044c000563617573657400154c6a6176612f6c616e672f5468726f77616" + + "26c653b4c000d64657461696c4d65737361676571007e00025b000a737461636b547261636574001" + + "e5b4c6a6176612f6c616e672f537461636b5472616365456c656d656e743b4c00147375707072657" + + "3736564457863657074696f6e737400104c6a6176612f7574696c2f4c6973743b787071007e00097" + + "07572001e5b4c6a6176612e6c616e672e537461636b5472616365456c656d656e743b02462a3c3cf" + + "d22390200007870000000097372001b6a6176612e6c616e672e537461636b5472616365456c656d6" + + "56e746109c59a2636dd8502000449000a6c696e654e756d6265724c000e6465636c6172696e67436" + + "c61737371007e00024c000866696c654e616d6571007e00024c000a6d6574686f644e616d6571007" + + "e00027870000000267400346c6962636f72652e6a6176612e6e696f2e66696c652e4469726563746" + + "f72794e6f74456d707479457863657074696f6e546573747400234469726563746f72794e6f74456" + + "d707479457863657074696f6e546573742e6a617661740012746573745f73657269616c697a61746" + + "96f6e7371007e000cfffffffe7400186a6176612e6c616e672e7265666c6563742e4d6574686f647" + + "4000b4d6574686f642e6a617661740006696e766f6b657371007e000c000000f9740028766f67617" + + "22e7461726765742e6a756e69742e4a756e69743324566f6761724a556e69745465737474000b4a7" + + "56e6974332e6a61766174000372756e7371007e000c00000063740020766f6761722e74617267657" + + "42e6a756e69742e4a556e697452756e6e657224317400104a556e697452756e6e65722e6a6176617" + + "4000463616c6c7371007e000c0000005c740020766f6761722e7461726765742e6a756e69742e4a5" + + "56e697452756e6e657224317400104a556e697452756e6e65722e6a61766174000463616c6c73710" + + "07e000c000000ed74001f6a6176612e7574696c2e636f6e63757272656e742e46757475726554617" + + "36b74000f4675747572655461736b2e6a61766174000372756e7371007e000c0000046d7400276a6" + + "176612e7574696c2e636f6e63757272656e742e546872656164506f6f6c4578656375746f7274001" + + "7546872656164506f6f6c4578656375746f722e6a61766174000972756e576f726b65727371007e0" + + "00c0000025f74002e6a6176612e7574696c2e636f6e63757272656e742e546872656164506f6f6c4" + + "578656375746f7224576f726b6572740017546872656164506f6f6c4578656375746f722e6a61766" + + "174000372756e7371007e000c000002f97400106a6176612e6c616e672e54687265616474000b546" + + "8726561642e6a61766174000372756e7372001f6a6176612e7574696c2e436f6c6c656374696f6e7" + + "324456d7074794c6973747ab817b43ca79ede02000078707874000466696c6570"; + DirectoryNotEmptyException exception = (DirectoryNotEmptyException) SerializationTester + .deserializeHex(hex); + + String hex1 = SerializationTester.serializeHex(exception).toString(); + assertEquals(hex, hex1); + assertEquals("file", exception.getFile()); + assertNull(exception.getOtherFile()); + assertNull(exception.getReason()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/FileAlreadyExistsExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/FileAlreadyExistsExceptionTest.java new file mode 100644 index 000000000..f8b03fd2f --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/FileAlreadyExistsExceptionTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystemException; +import libcore.util.SerializationTester; + +public class FileAlreadyExistsExceptionTest extends TestCase { + public void test_constructor$String() { + FileAlreadyExistsException exception = new FileAlreadyExistsException("file"); + assertEquals("file", exception.getFile()); + assertNull(exception.getOtherFile()); + assertNull(exception.getReason()); + + assertTrue(exception instanceof FileSystemException); + } + + public void test_constructor$String$String$String() { + FileAlreadyExistsException exception = new FileAlreadyExistsException("file", "otherFile", + "reason"); + assertEquals("file", exception.getFile()); + assertEquals("otherFile", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } + + public void test_serialization() throws IOException, ClassNotFoundException { + String hex = "aced0005737200286a6176612e6e696f2e66696c652e46696c65416c726561647945786973747" + + "3457863657074696f6e692ff0526155cb4d020000787200216a6176612e6e696f2e66696c652e466" + + "96c6553797374656d457863657074696f6ed598f27876d360fc0200024c000466696c657400124c6" + + "a6176612f6c616e672f537472696e673b4c00056f7468657271007e0002787200136a6176612e696" + + "f2e494f457863657074696f6e6c8073646525f0ab020000787200136a6176612e6c616e672e45786" + + "3657074696f6ed0fd1f3e1a3b1cc4020000787200136a6176612e6c616e672e5468726f7761626c6" + + "5d5c635273977b8cb0300044c000563617573657400154c6a6176612f6c616e672f5468726f77616" + + "26c653b4c000d64657461696c4d65737361676571007e00025b000a737461636b547261636574001" + + "e5b4c6a6176612f6c616e672f537461636b5472616365456c656d656e743b4c00147375707072657" + + "3736564457863657074696f6e737400104c6a6176612f7574696c2f4c6973743b787071007e00097" + + "40006726561736f6e7572001e5b4c6a6176612e6c616e672e537461636b5472616365456c656d656" + + "e743b02462a3c3cfd22390200007870000000097372001b6a6176612e6c616e672e537461636b547" + + "2616365456c656d656e746109c59a2636dd8502000449000a6c696e654e756d6265724c000e64656" + + "36c6172696e67436c61737371007e00024c000866696c654e616d6571007e00024c000a6d6574686" + + "f644e616d6571007e000278700000002d7400346c6962636f72652e6a6176612e6e696f2e66696c6" + + "52e46696c65416c7265616479457869737473457863657074696f6e5465737474002346696c65416" + + "c7265616479457869737473457863657074696f6e546573742e6a617661740012746573745f73657" + + "269616c697a6174696f6e7371007e000dfffffffe7400186a6176612e6c616e672e7265666c65637" + + "42e4d6574686f6474000b4d6574686f642e6a617661740006696e766f6b657371007e000d000000f" + + "9740028766f6761722e7461726765742e6a756e69742e4a756e69743324566f6761724a556e69745" + + "465737474000b4a756e6974332e6a61766174000372756e7371007e000d00000063740020766f676" + + "1722e7461726765742e6a756e69742e4a556e697452756e6e657224317400104a556e697452756e6" + + "e65722e6a61766174000463616c6c7371007e000d0000005c740020766f6761722e7461726765742" + + "e6a756e69742e4a556e697452756e6e657224317400104a556e697452756e6e65722e6a617661740" + + "00463616c6c7371007e000d000000ed74001f6a6176612e7574696c2e636f6e63757272656e742e4" + + "675747572655461736b74000f4675747572655461736b2e6a61766174000372756e7371007e000d0" + + "000046d7400276a6176612e7574696c2e636f6e63757272656e742e546872656164506f6f6c45786" + + "56375746f72740017546872656164506f6f6c4578656375746f722e6a61766174000972756e576f7" + + "26b65727371007e000d0000025f74002e6a6176612e7574696c2e636f6e63757272656e742e54687" + + "2656164506f6f6c4578656375746f7224576f726b6572740017546872656164506f6f6c457865637" + + "5746f722e6a61766174000372756e7371007e000d000002f97400106a6176612e6c616e672e54687" + + "265616474000b5468726561642e6a61766174000372756e7372001f6a6176612e7574696c2e436f6" + + "c6c656374696f6e7324456d7074794c6973747ab817b43ca79ede02000078707874000466696c657" + + "400096f7468657246696c65"; + + FileAlreadyExistsException exception = (FileAlreadyExistsException) SerializationTester + .deserializeHex(hex); + + String hex1 = SerializationTester.serializeHex(exception).toString(); + assertEquals(hex, hex1); + assertEquals("file", exception.getFile()); + assertEquals("otherFile", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/FileSystemAlreadyExistsExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/FileSystemAlreadyExistsExceptionTest.java new file mode 100644 index 000000000..4130ddc7c --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/FileSystemAlreadyExistsExceptionTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.nio.file.FileSystemAlreadyExistsException; + +public class FileSystemAlreadyExistsExceptionTest extends TestCase { + + public void test_constructor$String() { + String message = "message"; + FileSystemAlreadyExistsException exception = new FileSystemAlreadyExistsException(message); + assertEquals(message, exception.getMessage()); + + message = null; + exception = new FileSystemAlreadyExistsException(message); + assertEquals(message, exception.getMessage()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/FileSystemExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/FileSystemExceptionTest.java new file mode 100644 index 000000000..647623022 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/FileSystemExceptionTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.FileSystemException; +import libcore.util.SerializationTester; + +public class FileSystemExceptionTest extends TestCase { + + public void test_constructor$String() { + FileSystemException exception = new FileSystemException("file"); + assertEquals("file", exception.getFile()); + assertNull(exception.getOtherFile()); + assertNull(exception.getReason()); + assertTrue(exception instanceof IOException); + } + + public void test_constructor$String$String$String() { + FileSystemException exception = new FileSystemException("file", "otherFile", "reason"); + assertEquals("file", exception.getFile()); + assertEquals("otherFile", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } + + public void test_serialization() throws IOException, ClassNotFoundException { + String hex = "aced0005737200216a6176612e6e696f2e66696c652e46696c6553797374656d4578636570746" + + "96f6ed598f27876d360fc0200024c000466696c657400124c6a6176612f6c616e672f537472696e6" + + "73b4c00056f7468657271007e0001787200136a6176612e696f2e494f457863657074696f6e6c807" + + "3646525f0ab020000787200136a6176612e6c616e672e457863657074696f6ed0fd1f3e1a3b1cc40" + + "20000787200136a6176612e6c616e672e5468726f7761626c65d5c635273977b8cb0300044c00056" + + "3617573657400154c6a6176612f6c616e672f5468726f7761626c653b4c000d64657461696c4d657" + + "37361676571007e00015b000a737461636b547261636574001e5b4c6a6176612f6c616e672f53746" + + "1636b5472616365456c656d656e743b4c001473757070726573736564457863657074696f6e73740" + + "0104c6a6176612f7574696c2f4c6973743b787071007e0008740006726561736f6e7572001e5b4c6" + + "a6176612e6c616e672e537461636b5472616365456c656d656e743b02462a3c3cfd2239020000787" + + "0000000097372001b6a6176612e6c616e672e537461636b5472616365456c656d656e746109c59a2" + + "636dd8502000449000a6c696e654e756d6265724c000e6465636c6172696e67436c61737371007e0" + + "0014c000866696c654e616d6571007e00014c000a6d6574686f644e616d6571007e0001787000000" + + "02374002d6c6962636f72652e6a6176612e6e696f2e66696c652e46696c6553797374656d4578636" + + "57074696f6e5465737474001c46696c6553797374656d457863657074696f6e546573742e6a61766" + + "1740025746573745f636f6e7374727563746f7224537472696e6724537472696e6724537472696e6" + + "77371007e000cfffffffe7400186a6176612e6c616e672e7265666c6563742e4d6574686f6474000" + + "b4d6574686f642e6a617661740006696e766f6b657371007e000c000000f9740028766f6761722e7" + + "461726765742e6a756e69742e4a756e69743324566f6761724a556e69745465737474000b4a756e6" + + "974332e6a61766174000372756e7371007e000c00000063740020766f6761722e7461726765742e6" + + "a756e69742e4a556e697452756e6e657224317400104a556e697452756e6e65722e6a61766174000" + + "463616c6c7371007e000c0000005c740020766f6761722e7461726765742e6a756e69742e4a556e6" + + "97452756e6e657224317400104a556e697452756e6e65722e6a61766174000463616c6c7371007e0" + + "00c000000ed74001f6a6176612e7574696c2e636f6e63757272656e742e4675747572655461736b7" + + "4000f4675747572655461736b2e6a61766174000372756e7371007e000c0000046d7400276a61766" + + "12e7574696c2e636f6e63757272656e742e546872656164506f6f6c4578656375746f72740017546" + + "872656164506f6f6c4578656375746f722e6a61766174000972756e576f726b65727371007e000c0" + + "000025f74002e6a6176612e7574696c2e636f6e63757272656e742e546872656164506f6f6c45786" + + "56375746f7224576f726b6572740017546872656164506f6f6c4578656375746f722e6a617661740" + + "00372756e7371007e000c000002f97400106a6176612e6c616e672e54687265616474000b5468726" + + "561642e6a61766174000372756e7372001f6a6176612e7574696c2e436f6c6c656374696f6e73244" + + "56d7074794c6973747ab817b43ca79ede02000078707874000466696c657400096f7468657246696" + + "c65"; + + FileSystemException exception = (FileSystemException) SerializationTester + .deserializeHex(hex); + + String hex1 = SerializationTester.serializeHex(exception).toString(); + assertEquals(hex, hex1); + assertEquals("file", exception.getFile()); + assertEquals("otherFile", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } + + public void test_getMessage() { + FileSystemException exception = new FileSystemException("file", "otherFile", "reason"); + assertEquals("file -> otherFile: reason", exception.getMessage()); + + exception = new FileSystemException("file", "otherFile", null); + assertEquals("file -> otherFile", exception.getMessage()); + + exception = new FileSystemException(null, "otherFile", "reason"); + assertEquals(" -> otherFile: reason", exception.getMessage()); + + exception = new FileSystemException("file", null, "reason"); + assertEquals("file: reason", exception.getMessage()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/FileSystemLoopExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/FileSystemLoopExceptionTest.java new file mode 100644 index 000000000..2eaaed2e6 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/FileSystemLoopExceptionTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.FileSystemException; +import java.nio.file.FileSystemLoopException; +import libcore.util.SerializationTester; + +public class FileSystemLoopExceptionTest extends TestCase { + + public void test_constructor$String() { + FileSystemLoopException exception = new FileSystemLoopException("file"); + assertEquals("file", exception.getFile()); + assertTrue(exception instanceof FileSystemException); + } + + public void test_serialization () throws IOException, ClassNotFoundException { + String hex = "aced0005737200256a6176612e6e696f2e66696c652e46696c6553797374656d4c6f6f7045786" + + "3657074696f6e4335eed96f492f51020000787200216a6176612e6e696f2e66696c652e46696c655" + + "3797374656d457863657074696f6ed598f27876d360fc0200024c000466696c657400124c6a61766" + + "12f6c616e672f537472696e673b4c00056f7468657271007e0002787200136a6176612e696f2e494" + + "f457863657074696f6e6c8073646525f0ab020000787200136a6176612e6c616e672e45786365707" + + "4696f6ed0fd1f3e1a3b1cc4020000787200136a6176612e6c616e672e5468726f7761626c65d5c63" + + "5273977b8cb0300044c000563617573657400154c6a6176612f6c616e672f5468726f7761626c653" + + "b4c000d64657461696c4d65737361676571007e00025b000a737461636b547261636574001e5b4c6" + + "a6176612f6c616e672f537461636b5472616365456c656d656e743b4c00147375707072657373656" + + "4457863657074696f6e737400104c6a6176612f7574696c2f4c6973743b787071007e00097075720" + + "01e5b4c6a6176612e6c616e672e537461636b5472616365456c656d656e743b02462a3c3cfd22390" + + "200007870000000097372001b6a6176612e6c616e672e537461636b5472616365456c656d656e746" + + "109c59a2636dd8502000449000a6c696e654e756d6265724c000e6465636c6172696e67436c61737" + + "371007e00024c000866696c654e616d6571007e00024c000a6d6574686f644e616d6571007e00027" + + "870000000237400316c6962636f72652e6a6176612e6e696f2e66696c652e46696c6553797374656" + + "d4c6f6f70457863657074696f6e5465737474002046696c6553797374656d4c6f6f7045786365707" + + "4696f6e546573742e6a617661740012746573745f73657269616c697a6174696f6e7371007e000cf" + + "ffffffe7400186a6176612e6c616e672e7265666c6563742e4d6574686f6474000b4d6574686f642" + + "e6a617661740006696e766f6b657371007e000c000000f9740028766f6761722e7461726765742e6" + + "a756e69742e4a756e69743324566f6761724a556e69745465737474000b4a756e6974332e6a61766" + + "174000372756e7371007e000c00000063740020766f6761722e7461726765742e6a756e69742e4a5" + + "56e697452756e6e657224317400104a556e697452756e6e65722e6a61766174000463616c6c73710" + + "07e000c0000005c740020766f6761722e7461726765742e6a756e69742e4a556e697452756e6e657" + + "224317400104a556e697452756e6e65722e6a61766174000463616c6c7371007e000c000000ed740" + + "01f6a6176612e7574696c2e636f6e63757272656e742e4675747572655461736b74000f467574757" + + "2655461736b2e6a61766174000372756e7371007e000c0000046d7400276a6176612e7574696c2e6" + + "36f6e63757272656e742e546872656164506f6f6c4578656375746f72740017546872656164506f6" + + "f6c4578656375746f722e6a61766174000972756e576f726b65727371007e000c0000025f74002e6" + + "a6176612e7574696c2e636f6e63757272656e742e546872656164506f6f6c4578656375746f72245" + + "76f726b6572740017546872656164506f6f6c4578656375746f722e6a61766174000372756e73710" + + "07e000c000002f97400106a6176612e6c616e672e54687265616474000b5468726561642e6a61766" + + "174000372756e7372001f6a6176612e7574696c2e436f6c6c656374696f6e7324456d7074794c697" + + "3747ab817b43ca79ede02000078707874000466696c6570"; + FileSystemLoopException exception = (FileSystemLoopException) SerializationTester + .deserializeHex(hex); + + String hex1 = SerializationTester.serializeHex(exception).toString(); + assertEquals(hex, hex1); + assertEquals("file", exception.getFile()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/FileSystemNotFoundExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/FileSystemNotFoundExceptionTest.java new file mode 100644 index 000000000..2a0fe1ef0 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/FileSystemNotFoundExceptionTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.nio.file.FileSystemNotFoundException; + +public class FileSystemNotFoundExceptionTest extends TestCase { + + public void test_constructor_empty() { + FileSystemNotFoundException exception = new FileSystemNotFoundException(); + assertEquals(null, exception.getMessage()); + } + + public void test_constructor$String() { + String message = "message"; + FileSystemNotFoundException exception = new FileSystemNotFoundException(message); + assertEquals(message, exception.getMessage()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/FileSystemsTest.java b/luni/src/test/java/libcore/java/nio/file/FileSystemsTest.java new file mode 100644 index 000000000..9377d4ecd --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/FileSystemsTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemAlreadyExistsException; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.ProviderNotFoundException; +import java.util.HashMap; +import java.util.Map; + +import dalvik.system.PathClassLoader; +import junitparams.JUnitParamsRunner; +import sun.misc.IOUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +@RunWith(JUnitParamsRunner.class) +public class FileSystemsTest { + + @Rule + public FilesSetup filesSetup = new FilesSetup(); + + @Test + public void test_getDefault() { + FileSystem fs = FileSystems.getDefault(); + assertNotNull(fs.provider()); + } + + @Test + public void test_getFileSystem() { + Path testPath = Paths.get("/"); + FileSystem fs = FileSystems.getFileSystem(testPath.toUri()); + assertNotNull(fs.provider()); + + try { + FileSystems.getFileSystem(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_newFileSystem$URI$Map() throws IOException { + Path testPath = Paths.get("/"); + Map stubEnv = new HashMap<>(); + try { + FileSystems.newFileSystem(testPath.toUri(), stubEnv); + fail(); + } catch (FileSystemAlreadyExistsException expected) {} + + try { + FileSystems.newFileSystem(null, stubEnv); + fail(); + } catch (NullPointerException expected) {} + + try { + FileSystems.newFileSystem(testPath, null); + fail(); + } catch (ProviderNotFoundException expected) {} + } + + @Test + public void test_newFileSystem$URI$Map$ClassLoader() throws Exception { + Path testPath = Paths.get("/"); + Map stubEnv = new HashMap<>(); + try { + FileSystems.newFileSystem(testPath.toUri(), stubEnv, getClass().getClassLoader()); + fail(); + } catch (FileSystemAlreadyExistsException expected) {} + + try { + FileSystems.newFileSystem(null, stubEnv, + Thread.currentThread().getContextClassLoader()); + fail(); + } catch (NullPointerException expected) {} + + try { + FileSystems.newFileSystem(testPath.toUri(), null, + Thread.currentThread().getContextClassLoader()); + fail(); + } catch (FileSystemAlreadyExistsException expected) {} + + try { + FileSystems.newFileSystem(testPath.toUri(), stubEnv, null); + fail(); + } catch (FileSystemAlreadyExistsException expected) {} + } + + @Test + public void test_newFileSystem$URI$Map$ClassLoader_customClassLoader() throws Exception { + Map stubEnv = new HashMap<>(); + // Verify that the Thread's classloader cannot load mypackage.MockFileSystem. + try { + Thread.currentThread().getContextClassLoader().loadClass("mypackage.MockFileSystem"); + fail(); + } catch (ClassNotFoundException expected) {} + + ClassLoader fileSystemsClassLoader = createClassLoaderForTestFileSystems(); + + // The file system configured in filesystemstest.jar is for scheme "stubScheme:// + URI stubURI = new URI("stubScheme://sometext"); + FileSystem fs = FileSystems.newFileSystem(stubURI, stubEnv, fileSystemsClassLoader); + assertEquals("mypackage.MockFileSystem", fs.getClass().getName()); + assertSame(stubURI, fs.getClass().getDeclaredMethod("getURI").invoke(fs)); + assertSame(stubEnv, fs.getClass().getDeclaredMethod("getEnv").invoke(fs)); + } + + @Test + public void test_newFileSystem$Path$ClassLoader() throws Exception { + Path testPath = Paths.get("/"); + try { + FileSystems.newFileSystem(testPath, Thread.currentThread().getContextClassLoader()); + fail(); + } catch (ProviderNotFoundException expected) {} + + try { + FileSystems.newFileSystem(null, Thread.currentThread().getContextClassLoader()); + fail(); + } catch (NullPointerException expected) {} + + try { + FileSystems.newFileSystem(testPath, null); + fail(); + } catch (ProviderNotFoundException expected) {} + } + + @Test + public void test_newFileSystem$Path$ClassLoader_customClassLoader() throws Exception { + // Verify that the Thread's classloader cannot load mypackage.MockFileSystem. + try { + Thread.currentThread().getContextClassLoader().loadClass( + "mypackage.MockFileSystem"); + fail(); + } catch (ClassNotFoundException expected) {} + + ClassLoader fileSystemsClassLoader = createClassLoaderForTestFileSystems(); + FileSystem fs = FileSystems.newFileSystem(filesSetup.getDataFilePath(), + fileSystemsClassLoader); + + assertEquals("mypackage.MockFileSystem", fs.getClass().getName()); + + Path pathValue = (Path)fs.getClass().getDeclaredMethod("getPath").invoke(fs); + assertEquals(filesSetup.getDataFilePath(), pathValue); + } + + /** + * The method creates a custom classloader for the mock FileSystem and FileSystemProvider + * classes. The custom classloader is created by providing filesystemtest.jar which contains + * MockFileSystemProvider and MockFileSystem classes. + * @throws Exception + */ + ClassLoader createClassLoaderForTestFileSystems() throws Exception { + File jarFile = new File(filesSetup.getTestDir().toString(), "filesystemstset.jar"); + InputStream jis = getClass().getResource("/filesystemstest.jar").openStream(); + OutputStream jos = new FileOutputStream(jarFile); + jos.write(IOUtils.readFully(jis, -1, true)); + + return new PathClassLoader(jarFile.getAbsolutePath(), getClass().getClassLoader()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/Files2Test.java b/luni/src/test/java/libcore/java/nio/file/Files2Test.java new file mode 100644 index 000000000..394643a30 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/Files2Test.java @@ -0,0 +1,1889 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.nio.channels.NonReadableChannelException; +import java.nio.channels.NonWritableChannelException; +import java.nio.channels.SeekableByteChannel; +import java.nio.charset.MalformedInputException; +import java.nio.charset.StandardCharsets; +import java.nio.file.AccessDeniedException; +import java.nio.file.CopyOption; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemLoopException; +import java.nio.file.FileVisitOption; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.NoSuchFileException; +import java.nio.file.NotDirectoryException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.nio.file.spi.FileSystemProvider; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +import static java.nio.file.FileVisitResult.CONTINUE; +import static java.nio.file.FileVisitResult.TERMINATE; +import static java.nio.file.StandardOpenOption.APPEND; +import static java.nio.file.StandardOpenOption.CREATE_NEW; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.SYNC; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; +import static java.nio.file.StandardOpenOption.WRITE; +import static junit.framework.TestCase.assertTrue; +import static libcore.java.nio.file.FilesSetup.DATA_FILE; +import static libcore.java.nio.file.FilesSetup.NON_EXISTENT_FILE; +import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA; +import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA_2; +import static libcore.java.nio.file.FilesSetup.UTF_16_DATA; +import static libcore.java.nio.file.FilesSetup.execCmdAndWaitForTermination; +import static libcore.java.nio.file.FilesSetup.readFromFile; +import static libcore.java.nio.file.FilesSetup.readFromInputStream; +import static libcore.java.nio.file.FilesSetup.writeToFile; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class Files2Test { + @Rule + public FilesSetup filesSetup = new FilesSetup(); + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + @Mock + private Path mockPath; + @Mock + private Path mockPath2; + @Mock + private FileSystem mockFileSystem; + @Mock + private FileSystemProvider mockFileSystemProvider; + + @Before + public void setUp() throws Exception { + when(mockPath.getFileSystem()).thenReturn(mockFileSystem); + when(mockPath2.getFileSystem()).thenReturn(mockFileSystem); + when(mockFileSystem.provider()).thenReturn(mockFileSystemProvider); + } + + @Test + public void test_move() throws IOException { + CopyOption mockCopyOption = mock(CopyOption.class); + assertEquals(mockPath2, Files.move(mockPath, mockPath2, mockCopyOption)); + verify(mockFileSystemProvider).move(mockPath, mockPath2, mockCopyOption); + } + + @Test + public void test_readSymbolicLink() throws IOException { + when(mockFileSystemProvider.readSymbolicLink(mockPath)).thenReturn(mockPath2); + assertEquals(mockPath2, Files.readSymbolicLink(mockPath)); + verify(mockFileSystemProvider).readSymbolicLink(mockPath); + } + + @Test + public void test_isSameFile() throws IOException { + when(mockFileSystemProvider.isSameFile(mockPath, mockPath2)).thenReturn(true); + when(mockFileSystemProvider.isSameFile(mockPath2, mockPath)).thenReturn(false); + assertTrue(Files.isSameFile(mockPath, mockPath2)); + assertFalse(Files.isSameFile(mockPath2, mockPath)); + } + + @Test + public void test_getFileStore() throws IOException { + FileStore mockFileStore = mock(FileStore.class); + when(mockFileSystemProvider.getFileStore(mockPath)).thenReturn(mockFileStore); + assertEquals(mockFileStore, Files.getFileStore(mockPath)); + } + + @Test + public void test_isHidden() throws IOException { + when(mockFileSystemProvider.isHidden(mockPath)).thenReturn(true); + when(mockFileSystemProvider.isHidden(mockPath2)).thenReturn(false); + assertTrue(Files.isHidden(mockPath)); + assertFalse(Files.isHidden(mockPath2)); + } + + @Test + public void test_probeContentType() throws IOException { + assertEquals("text/plain", + Files.probeContentType(filesSetup.getPathInTestDir("file.txt"))); + assertEquals("text/x-java", + Files.probeContentType(filesSetup.getPathInTestDir("file.java"))); + } + + @Test + public void test_getFileAttributeView() throws IOException { + FileAttributeView mockFileAttributeView = mock(FileAttributeView.class); + when(mockFileSystemProvider.getFileAttributeView(mockPath, FileAttributeView.class, + LinkOption.NOFOLLOW_LINKS)).thenReturn(mockFileAttributeView); + assertEquals(mockFileAttributeView, Files.getFileAttributeView(mockPath, + FileAttributeView.class, LinkOption.NOFOLLOW_LINKS)); + } + + @Test + public void test_readAttributes() throws IOException { + BasicFileAttributes mockBasicFileAttributes = mock(BasicFileAttributes.class); + when(mockFileSystemProvider.readAttributes(mockPath, BasicFileAttributes.class, + LinkOption.NOFOLLOW_LINKS)).thenReturn(mockBasicFileAttributes); + assertEquals(mockBasicFileAttributes, Files.readAttributes(mockPath, + BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS)); + + } + + @Test + public void test_setAttribute() throws IOException { + assertEquals(mockPath, Files.setAttribute(mockPath, "string", 10, + LinkOption.NOFOLLOW_LINKS)); + verify(mockFileSystemProvider).setAttribute(mockPath, "string", 10, + LinkOption.NOFOLLOW_LINKS); + } + + @Test + public void test_getAttribute() throws IOException { + // Other tests are covered in test_readAttributes. + // When file is NON_EXISTENT. + try { + Files.getAttribute(filesSetup.getTestPath(), "basic:lastModifiedTime"); + fail(); + } catch (NoSuchFileException expected) {} + } + + @Test + public void test_getAttribute_Exception() throws IOException { + // IllegalArgumentException + try { + Files.getAttribute(filesSetup.getDataFilePath(), "xyz"); + fail(); + } catch (IllegalArgumentException expected) {} + + try { + Files.getAttribute(null, "xyz"); + fail(); + } catch(NullPointerException expected) {} + + try { + Files.getAttribute(filesSetup.getDataFilePath(), null); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_getPosixFilePermissions() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + Files.createFile(filesSetup.getTestPath(), attr); + assertEquals(attr.value(), Files.getPosixFilePermissions(filesSetup.getTestPath())); + } + + @Test + public void test_getPosixFilePermissions_NPE() throws IOException { + try { + Files.getPosixFilePermissions(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_setPosixFilePermissions() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + Files.setPosixFilePermissions(filesSetup.getDataFilePath(), perm); + assertEquals(attr.value(), Files.getPosixFilePermissions(filesSetup.getDataFilePath())); + } + + @Test + public void test_setPosixFilePermissions_NPE() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + try { + Files.setPosixFilePermissions(null, perm); + fail(); + } catch(NullPointerException expected) {} + + try { + Files.setPosixFilePermissions(filesSetup.getDataFilePath(), null); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_getOwner() throws IOException, InterruptedException { + String[] statCmd = { "stat", "-c", "%U", filesSetup.getTestDir() + "/" + DATA_FILE }; + Process statProcess = execCmdAndWaitForTermination(statCmd); + String owner = readFromInputStream(statProcess.getInputStream()).trim(); + assertEquals(owner, Files.getOwner(filesSetup.getDataFilePath()).getName()); + } + + @Test + public void test_getOwner_NPE() throws IOException, InterruptedException { + try { + Files.getOwner(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_isSymbolicLink() throws IOException, InterruptedException { + assertFalse(Files.isSymbolicLink(filesSetup.getTestPath())); + assertFalse(Files.isSymbolicLink(filesSetup.getDataFilePath())); + + // Creating a symbolic link. + String[] symLinkCmd = { "ln", "-s", DATA_FILE, + filesSetup.getTestDir() + "/" + NON_EXISTENT_FILE }; + execCmdAndWaitForTermination(symLinkCmd); + assertTrue(Files.isSymbolicLink(filesSetup.getTestPath())); + } + + @Test + public void test_isSymbolicLink_NPE() throws IOException, InterruptedException { + try { + Files.isSymbolicLink(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_isDirectory() throws IOException, InterruptedException { + assertFalse(Files.isDirectory(filesSetup.getDataFilePath())); + // When file doesn't exist. + assertFalse(Files.isDirectory(filesSetup.getTestPath())); + + // Creating a directory. + String dirName = "newDir"; + Path dirPath = filesSetup.getPathInTestDir(dirName); + String mkdir[] = { "mkdir", filesSetup.getTestDir() + "/" + dirName }; + execCmdAndWaitForTermination(mkdir); + assertTrue(Files.isDirectory(dirPath)); + } + + @Test + public void test_isDirectory_NPE() throws IOException { + try { + Files.isDirectory(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_isRegularFile() throws IOException, InterruptedException { + assertTrue(Files.isRegularFile(filesSetup.getDataFilePath())); + // When file doesn't exist. + assertFalse(Files.isRegularFile(filesSetup.getTestPath())); + + // Check directories. + Path dirPath = filesSetup.getPathInTestDir("dir"); + Files.createDirectory(dirPath); + assertFalse(Files.isRegularFile(dirPath)); + + // Check symbolic link. + // When linked to itself. + Files.createSymbolicLink(filesSetup.getTestPath(), + filesSetup.getTestPath().toAbsolutePath()); + assertFalse(Files.isRegularFile(filesSetup.getTestPath())); + + // When linked to some other file. + filesSetup.reset(); + Files.createSymbolicLink(filesSetup.getTestPath(), + filesSetup.getDataFilePath().toAbsolutePath()); + assertTrue(Files.isRegularFile(filesSetup.getTestPath())); + + // When asked to not follow the link. + assertFalse(Files.isRegularFile(filesSetup.getTestPath(), LinkOption.NOFOLLOW_LINKS)); + + // Device file. + Path deviceFilePath = Paths.get("/dev/null"); + assertTrue(Files.exists(deviceFilePath)); + assertFalse(Files.isRegularFile(deviceFilePath)); + } + + @Test + public void test_isRegularFile_NPE() throws IOException { + try { + Files.isReadable(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_getLastModifiedTime() throws IOException, InterruptedException { + String touchCmd[] = { "touch", "-d", "2015-10-09T00:00:00Z", + filesSetup.getTestDir() + "/" + DATA_FILE }; + execCmdAndWaitForTermination(touchCmd); + assertEquals("2015-10-09T00:00:00Z", + Files.getLastModifiedTime(filesSetup.getDataFilePath()).toString()); + + // Non existent file. + try { + Files.getLastModifiedTime(filesSetup.getTestPath()).toString(); + fail(); + } catch (NoSuchFileException expected) {} + } + + @Test + public void test_getLastModifiedTime_NPE() throws IOException { + try { + Files.getLastModifiedTime(null, LinkOption.NOFOLLOW_LINKS); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.getLastModifiedTime(filesSetup.getDataFilePath(), (LinkOption[]) null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_setLastModifiedTime() throws IOException, InterruptedException { + long timeInMillisToBeSet = System.currentTimeMillis() - 10000; + Files.setLastModifiedTime(filesSetup.getDataFilePath(), + FileTime.fromMillis(timeInMillisToBeSet)); + assertEquals(timeInMillisToBeSet/1000, + Files.getLastModifiedTime(filesSetup.getDataFilePath()).to(TimeUnit.SECONDS)); + + // Non existent file. + try { + Files.setLastModifiedTime(filesSetup.getTestPath(), + FileTime.fromMillis(timeInMillisToBeSet)); + fail(); + } catch (NoSuchFileException expected) {} + } + + @Test + public void test_setLastModifiedTime_NPE() throws IOException, InterruptedException { + try { + Files.setLastModifiedTime(null, FileTime.fromMillis(System.currentTimeMillis())); + fail(); + } catch (NullPointerException expected) {} + + // No NullPointerException. + Files.setLastModifiedTime(filesSetup.getDataFilePath(), null); + } + + @Test + public void test_size() throws IOException, InterruptedException { + int testSizeInBytes = 5000; + String ddCmd[] = { "dd", "if=/dev/zero", "of=" + filesSetup.getTestDir() + "/" + DATA_FILE, + "bs=" + + testSizeInBytes, "count=1"}; + execCmdAndWaitForTermination(ddCmd); + + assertEquals(testSizeInBytes, Files.size(filesSetup.getDataFilePath())); + + try { + Files.size(filesSetup.getTestPath()); + fail(); + } catch (NoSuchFileException expected) {} + } + + @Test + public void test_size_NPE() throws IOException, InterruptedException { + try { + Files.size(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_exists() throws IOException { + // When file exists. + assertTrue(Files.exists(filesSetup.getDataFilePath())); + + // When file doesn't exist. + assertFalse(Files.exists(filesSetup.getTestPath())); + + // SymLink + Files.createSymbolicLink(filesSetup.getTestPath(), + filesSetup.getDataFilePath().toAbsolutePath()); + assertTrue(Files.exists(filesSetup.getTestPath())); + + // When link shouldn't be followed + assertTrue(Files.exists(filesSetup.getTestPath(), LinkOption.NOFOLLOW_LINKS)); + + // When the target file doesn't exist. + Files.delete(filesSetup.getDataFilePath()); + assertTrue(Files.exists(filesSetup.getTestPath(), LinkOption.NOFOLLOW_LINKS)); + assertFalse(Files.exists(filesSetup.getTestPath())); + + // Symlink to itself + filesSetup.reset(); + Files.createSymbolicLink(filesSetup.getTestPath(), + filesSetup.getTestPath().toAbsolutePath()); + assertFalse(Files.exists(filesSetup.getTestPath())); + assertTrue(Files.exists(filesSetup.getTestPath(), LinkOption.NOFOLLOW_LINKS)); + } + + @Test + public void test_exists_NPE() throws IOException { + try { + Files.exists(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_notExists() throws IOException { + // When file exists. + assertFalse(Files.notExists(filesSetup.getDataFilePath())); + + // When file doesn't exist. + assertTrue(Files.notExists(filesSetup.getTestPath())); + + // SymLink + Files.createSymbolicLink(filesSetup.getTestPath(), + filesSetup.getDataFilePath().toAbsolutePath()); + assertFalse(Files.notExists(filesSetup.getTestPath())); + + // When link shouldn't be followed + assertFalse(Files.notExists(filesSetup.getTestPath(), LinkOption.NOFOLLOW_LINKS)); + + // When the target file doesn't exist. + Files.delete(filesSetup.getDataFilePath()); + assertFalse(Files.notExists(filesSetup.getTestPath(), LinkOption.NOFOLLOW_LINKS)); + assertTrue(Files.notExists(filesSetup.getTestPath())); + + // Symlink to itself + filesSetup.reset(); + Files.createSymbolicLink(filesSetup.getTestPath(), + filesSetup.getTestPath().toAbsolutePath()); + assertFalse(Files.notExists(filesSetup.getTestPath())); + assertFalse(Files.notExists(filesSetup.getTestPath(), LinkOption.NOFOLLOW_LINKS)); + } + + @Test + public void test_notExists_NPE() throws IOException { + try { + Files.notExists(null); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.notExists(filesSetup.getDataFilePath(), (LinkOption[]) null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_isReadable() throws IOException { + // When a readable file is available. + assertTrue(Files.isReadable(filesSetup.getDataFilePath())); + + // When a file doesn't exist. + assertFalse(Files.isReadable(filesSetup.getTestPath())); + + // Setting non readable permission for user + Set perm = PosixFilePermissions.fromString("-wxrwxrwx"); + Files.setPosixFilePermissions(filesSetup.getDataFilePath(), perm); + assertFalse(Files.isReadable(filesSetup.getDataFilePath())); + } + + @Test + public void test_isReadable_NPE() throws IOException { + try { + Files.isReadable(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_isWritable() throws IOException { + // When a readable file is available. + assertTrue(Files.isWritable(filesSetup.getDataFilePath())); + + // When a file doesn't exist. + assertFalse(Files.isWritable(filesSetup.getTestPath())); + + // Setting non writable permission for user + Set perm = PosixFilePermissions.fromString("r-xrwxrwx"); + Files.setPosixFilePermissions(filesSetup.getDataFilePath(), perm); + assertFalse(Files.isWritable(filesSetup.getDataFilePath())); + } + + @Test + public void test_isWritable_NPE() { + try { + Files.isWritable(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_isExecutable() throws IOException { + // When a readable file is available. + assertFalse(Files.isExecutable(filesSetup.getDataFilePath())); + + // When a file doesn't exist. + assertFalse(Files.isExecutable(filesSetup.getTestPath())); + + // Setting non executable permission for user + Set perm = PosixFilePermissions.fromString("rw-rwxrwx"); + Files.setPosixFilePermissions(filesSetup.getDataFilePath(), perm); + assertFalse(Files.isExecutable(filesSetup.getDataFilePath())); + } + + @Test + public void test_isExecutable_NPE() { + try { + Files.isExecutable(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_walkFileTree$Path$Set$int$FileVisitor_symbolicLinkFollow() + throws IOException, InterruptedException { + // Directory structure. + // root + // ├── dir1 + // │ └── dir2 ─ dir3-file1 - file3 + // │ + // └── file2 + // + // With follow link it should be able to traverse to dir3 and file1 when started from file2. + + // Directory setup. + Path rootDir = filesSetup.getPathInTestDir("root"); + Path dir1 = filesSetup.getPathInTestDir("root/dir1"); + Path dir2 = filesSetup.getPathInTestDir("root/dir1/dir2"); + Path dir3 = filesSetup.getPathInTestDir("root/dir1/dir2/dir3"); + Path file1 = filesSetup.getPathInTestDir("root/dir1/dir2/dir3/file1"); + Path file2 = filesSetup.getPathInTestDir("root/file2"); + + Files.createDirectories(dir3); + Files.createFile(file1); + Files.createSymbolicLink(file2, dir2.toAbsolutePath()); + assertTrue(Files.isSymbolicLink(file2)); + + Map dirMap = new HashMap<>(); + Map expectedDirMap = new HashMap<>(); + Set option = new HashSet<>(); + option.add(FileVisitOption.FOLLOW_LINKS); + Files.walkFileTree(file2, option, 50, new TestFileVisitor(dirMap, option)); + + expectedDirMap.put(file1.getFileName(), VisitOption.VISIT_FILE); + expectedDirMap.put(file2.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + expectedDirMap.put(dir3.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + + assertEquals(expectedDirMap, dirMap); + } + + @Test + public void test_walkFileTree$Path$FileVisitor() throws IOException { + // Directory structure. + // . + // ├── DATA_FILE + // └── root + // ├── dir1 + // │ ├── dir2 + // │ │ ├── dir3 + // │ │ └── file5 + // │ ├── dir4 + // │ └── file3 + // ├── dir5 + // └── file1 + // + + // Directory Setup. + Path rootDir = filesSetup.getPathInTestDir("root"); + Path dir1 = filesSetup.getPathInTestDir("root/dir1"); + Path dir2 = filesSetup.getPathInTestDir("root/dir1/dir2"); + Path dir3 = filesSetup.getPathInTestDir("root/dir1/dir2/dir3"); + Path dir4 = filesSetup.getPathInTestDir("root/dir1/dir4"); + Path dir5 = filesSetup.getPathInTestDir("root/dir5"); + Path file1 = filesSetup.getPathInTestDir("root/file1"); + Path file3 = filesSetup.getPathInTestDir("root/dir1/file3"); + Path file5 = filesSetup.getPathInTestDir("root/dir1/dir2/file5"); + + Files.createDirectories(dir3); + Files.createDirectories(dir4); + Files.createDirectories(dir5); + Files.createFile(file3); + Files.createFile(file5); + Files.createSymbolicLink(file1, filesSetup.getDataFilePath().toAbsolutePath()); + + Map dirMap = new HashMap<>(); + Map expectedDirMap = new HashMap<>(); + Path returnedPath = Files.walkFileTree(rootDir, new Files2Test.TestFileVisitor(dirMap)); + + assertEquals(rootDir, returnedPath); + + expectedDirMap.put(rootDir.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + expectedDirMap.put(dir1.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + expectedDirMap.put(dir2.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + expectedDirMap.put(dir3.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + expectedDirMap.put(file5.getFileName(), VisitOption.VISIT_FILE); + expectedDirMap.put(dir4.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + expectedDirMap.put(file3.getFileName(), VisitOption.VISIT_FILE); + expectedDirMap.put(dir5.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + expectedDirMap.put(file1.getFileName(), VisitOption.VISIT_FILE); + assertEquals(expectedDirMap, dirMap); + } + + @Test + public void test_walkFileTree_depthFirst() throws IOException { + // Directory structure. + // . + // ├── DATA_FILE + // └── root + // ├── dir1 ── file1 + // └── dir2 ── file2 + + // Directory Setup. + Path rootDir = filesSetup.getPathInTestDir("root"); + Path dir1 = filesSetup.getPathInTestDir("root/dir1"); + Path dir2 = filesSetup.getPathInTestDir("root/dir2"); + Path file1 = filesSetup.getPathInTestDir("root/dir1/file1"); + Path file2 = filesSetup.getPathInTestDir("root/dir2/file2"); + + Files.createDirectories(dir1); + Files.createDirectories(dir2); + Files.createFile(file1); + Files.createFile(file2); + + Map dirMap = new HashMap<>(); + List keyList = new ArrayList<>(); + Files.walkFileTree(rootDir, + new Files2Test.TestFileVisitor(dirMap, keyList)); + assertEquals(rootDir.getFileName(), keyList.get(0)); + if (keyList.get(1).equals(dir1.getFileName())) { + assertEquals(file1.getFileName(), keyList.get(2)); + assertEquals(dir2.getFileName(), keyList.get(3)); + assertEquals(file2.getFileName(), keyList.get(4)); + } else if (keyList.get(1).equals(dir2.getFileName())){ + assertEquals(file2.getFileName(), keyList.get(2)); + assertEquals(dir1.getFileName(), keyList.get(3)); + assertEquals(file1.getFileName(), keyList.get(4)); + } else { + fail(); + } + } + + @Test + public void test_walkFileTree_negativeDepth() throws IOException { + Path rootDir = filesSetup.getPathInTestDir("root"); + Path dir1 = filesSetup.getPathInTestDir("root/dir1"); + + Files.createDirectories(dir1); + + Map dirMap = new HashMap<>(); + Set option = new HashSet<>(); + option.add(FileVisitOption.FOLLOW_LINKS); + try { + Files.walkFileTree(rootDir, option, -1, + new Files2Test.TestFileVisitor(dirMap)); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test + public void test_walkFileTree_maximumDepth() throws IOException { + // Directory structure. + // root + // ├── dir1 + // │ ├── dir2 + // │ │ ├── dir3 + // │ │ └── file5 + // │ ├── dir4 + // │ └── file3 + // ├── dir5 + // └── file1 + // + // depth will be 2. file5, dir3 is not reachable. + // Directory Setup. + Path rootDir = filesSetup.getPathInTestDir("root"); + Path dir1 = filesSetup.getPathInTestDir("root/dir1"); + Path dir2 = filesSetup.getPathInTestDir("root/dir1/dir2"); + Path dir3 = filesSetup.getPathInTestDir("root/dir1/dir2/dir3"); + Path dir4 = filesSetup.getPathInTestDir("root/dir1/dir4"); + Path dir5 = filesSetup.getPathInTestDir("root/dir5"); + Path file1 = filesSetup.getPathInTestDir("root/file1"); + Path file3 = filesSetup.getPathInTestDir("root/dir1/file3"); + Path file5 = filesSetup.getPathInTestDir("root/dir1/dir2/file5"); + + Files.createDirectories(dir3); + Files.createDirectories(dir4); + Files.createDirectories(dir5); + Files.createFile(file1); + Files.createFile(file3); + Files.createFile(file5); + + Map dirMap = new HashMap<>(); + Map expectedDirMap = new HashMap<>(); + Set option = new HashSet<>(); + option.add(FileVisitOption.FOLLOW_LINKS); + Files.walkFileTree(rootDir, option, 2, new Files2Test.TestFileVisitor(dirMap)); + assertTrue(Files.isDirectory(dir4)); + expectedDirMap.put(rootDir.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + expectedDirMap.put(dir1.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + // Both of the directories are at maximum depth, therefore, will be treated as simple file. + expectedDirMap.put(dir2.getFileName(), VisitOption.VISIT_FILE); + expectedDirMap.put(dir4.getFileName(), VisitOption.VISIT_FILE); + expectedDirMap.put(dir5.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + expectedDirMap.put(file1.getFileName(), VisitOption.VISIT_FILE); + expectedDirMap.put(file3.getFileName(), VisitOption.VISIT_FILE); + + assertEquals(expectedDirMap, dirMap); + } + + @Test + public void test_walkFileTree$Path$FileVisitor_NPE() throws IOException { + Path rootDir = filesSetup.getPathInTestDir("root"); + try { + Files.walkFileTree(null, + new Files2Test.TestFileVisitor(new HashMap<>())); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.walkFileTree(rootDir, null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_walkFileTree$Path$FileVisitor_FileSystemLoopException() throws IOException { + // Directory structure. + // . + // ├── DATA_FILE + // └── root + // └── dir1 + // └── file1 + // + // file1 is symlink to dir1 + + // Directory Setup. + Path rootDir = filesSetup.getPathInTestDir("root"); + Path dir1 = filesSetup.getPathInTestDir("root/dir1"); + Path file1 = filesSetup.getPathInTestDir("root/dir1/file1"); + + Files.createDirectories(dir1); + Files.createSymbolicLink(file1, dir1.toAbsolutePath()); + assertEquals(dir1.getFileName(), Files.readSymbolicLink(file1).getFileName()); + + Map dirMap = new HashMap<>(); + Set option = new HashSet<>(); + option.add(FileVisitOption.FOLLOW_LINKS); + try { + Files.walkFileTree(rootDir, option, Integer.MAX_VALUE, + new Files2Test.TestFileVisitor(dirMap)); + fail(); + } catch (FileSystemLoopException expected) {} + } + + @Test + public void test_find() throws IOException { + // Directory structure. + // root + // ├── dir1 + // │ ├── dir2 + // │ │ ├── dir3 + // │ │ └── file5 + // │ ├── dir4 + // │ └── file3 + // ├── dir5 + // └── file1 + // + + // Directory setup. + Path rootDir = Paths.get(filesSetup.getTestDir(), "root"); + Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1"); + Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2"); + Path dir3 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/dir3"); + Path dir4 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir4"); + Path dir5 = Paths.get(filesSetup.getTestDir(), "root/dir5"); + Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1"); + Path file3 = Paths.get(filesSetup.getTestDir(), "root/dir1/file3"); + Path file5 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/file5"); + + Files.createDirectories(dir3); + Files.createDirectories(dir4); + Files.createDirectories(dir5); + Files.createFile(file1); + Files.createFile(file3); + Files.createFile(file5); + + // When depth is 2 then file4, file5 and dir3 are not reachable. + Set expectedDirSet = new HashSet<>(); + expectedDirSet.add(rootDir); + expectedDirSet.add(dir1); + expectedDirSet.add(dir2); + expectedDirSet.add(dir4); + expectedDirSet.add(dir5); + Set dirSet = new HashSet<>(); + Stream pathStream = Files.find(rootDir, 2, (path, attr) -> Files.isDirectory(path)); + pathStream.forEach(path -> dirSet.add(path)); + assertEquals(expectedDirSet, dirSet); + + // Test the case where depth is 0. + expectedDirSet.clear(); + dirSet.clear(); + + expectedDirSet.add(rootDir); + + pathStream = Files.find(rootDir, 0, (path, attr) -> Files.isDirectory(path)); + pathStream.forEach(path -> dirSet.add(path)); + assertEquals(expectedDirSet, dirSet); + + // Test the case where depth is -1. + try { + Files.find(rootDir, -1, (path, attr) -> Files.isDirectory(path)); + fail(); + } catch (IllegalArgumentException expected) {} + + // Test the case when BiPredicate always returns false. + expectedDirSet.clear(); + dirSet.clear(); + + pathStream = Files.find(rootDir, 2, (path, attr) -> false); + pathStream.forEach(path -> dirSet.add(path)); + assertEquals(expectedDirSet, dirSet); + + // Test the case when start is not a directory. + expectedDirSet.clear(); + dirSet.clear(); + + expectedDirSet.add(file1); + + pathStream = Files.find(file1, 2, (path, attr) -> true); + pathStream.forEach(path -> dirSet.add(path)); + assertEquals(expectedDirSet, dirSet); + } + + @Test + public void test_find_NPE() throws IOException { + Path rootDir = Paths.get(filesSetup.getTestDir(), "root"); + Files.createDirectories(rootDir); + try { + Files.find(null, 2, (path, attr) -> Files.isDirectory(path)); + fail(); + } catch(NullPointerException expected) {} + + try { + Files.find(rootDir, (Integer)null, (path, attr) -> Files.isDirectory(path)); + fail(); + } catch(NullPointerException expected) {} + + try(Stream pathStream = Files.find(rootDir, 2, null)) { + pathStream.forEach(path -> {/* do nothing */}); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_lines$Path$Charset() throws IOException { + List lines = new ArrayList<>(); + lines.add(UTF_16_DATA); + lines.add(TEST_FILE_DATA); + Files.write(filesSetup.getDataFilePath(), lines, StandardCharsets.UTF_16); + try (Stream readLines = Files.lines(filesSetup.getDataFilePath(), + StandardCharsets.UTF_16)) { + Iterator lineIterator = lines.iterator(); + readLines.forEach(line -> assertEquals(line, lineIterator.next())); + } + + // When Path is a directory + filesSetup.reset(); + try (Stream readLines = Files.lines(filesSetup.getTestDirPath(), + StandardCharsets.UTF_16)) { + try { + readLines.count(); + fail(); + } catch (UncheckedIOException expected) {} + } + + // When file doesn't exits. + filesSetup.reset(); + try (Stream readLines = Files.lines(filesSetup.getTestPath(), + StandardCharsets.UTF_16)) { + fail(); + } catch (NoSuchFileException expected) {} + } + + @Test + public void test_lines$Path$Charset_NPE() throws IOException { + try { + Files.lines(null, StandardCharsets.UTF_16); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.lines(filesSetup.getDataFilePath(), null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_lines$Path() throws IOException { + List lines = new ArrayList<>(); + lines.add(TEST_FILE_DATA_2); + lines.add(TEST_FILE_DATA); + Files.write(filesSetup.getDataFilePath(), lines, StandardCharsets.UTF_8); + try (Stream readLines = Files.lines(filesSetup.getDataFilePath())) { + Iterator lineIterator = lines.iterator(); + readLines.forEach(line -> assertEquals(line, lineIterator.next())); + } + + // When Path is a directory + filesSetup.reset(); + try (Stream readLines = Files.lines(filesSetup.getTestDirPath())) { + try { + readLines.count(); + fail(); + } catch (UncheckedIOException expected) {} + } + + // When file doesn't exits. + filesSetup.reset(); + try (Stream readLines = Files.lines(filesSetup.getTestPath())) { + fail(); + } catch (NoSuchFileException expected) {} + } + + @Test + public void test_line$Path_NPE() throws IOException { + try { + Files.lines(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_list() throws Exception { + // Directory Setup for the test. + Path rootDir = Paths.get(filesSetup.getTestDir(), "root"); + Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1"); + Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1"); + Path file2 = Paths.get(filesSetup.getTestDir(), "root/dir1/file2"); + Path symLink = Paths.get(filesSetup.getTestDir(), "root/symlink"); + Files.createDirectories(dir1); + Files.createFile(file1); + Files.createFile(file2); + Files.createSymbolicLink(symLink, file1.toAbsolutePath()); + + Set expectedVisitedFiles = new HashSet<>(); + expectedVisitedFiles.add(dir1); + expectedVisitedFiles.add(file1); + expectedVisitedFiles.add(symLink); + + Set visitedFiles = new HashSet<>(); + try (Stream pathStream = Files.list(rootDir)) { + pathStream.forEach(path -> visitedFiles.add(path)); + } + assertEquals(3, visitedFiles.size()); + + + // Test the case where directory is empty. + filesSetup.clearAll(); + try { + Files.list(Paths.get(filesSetup.getTestDir(), "newDir")); + fail(); + } catch (NoSuchFileException expected) {} + + // Test the case where path points to a file. + filesSetup.clearAll(); + filesSetup.setUp(); + try { + Files.list(filesSetup.getDataFilePath()); + fail(); + } catch (NotDirectoryException expected) {} + } + + @Test + public void test_list_NPE() throws IOException { + try { + Files.list(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_newBufferedReader() throws IOException { + // Test the case where file doesn't exists. + try { + Files.newBufferedReader(filesSetup.getTestPath()); + fail(); + } catch (NoSuchFileException expected) {} + + BufferedReader bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath()); + assertEquals(TEST_FILE_DATA, bufferedReader.readLine()); + + // Test the case where the file content has unicode characters. + writeToFile(filesSetup.getDataFilePath(), UTF_16_DATA); + bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath()); + assertEquals(UTF_16_DATA, bufferedReader.readLine()); + bufferedReader.close(); + + // Test the case where file is write-only. + Set perm = PosixFilePermissions.fromString("-w-------"); + Files.setPosixFilePermissions(filesSetup.getDataFilePath(), perm); + try { + Files.newBufferedReader(filesSetup.getDataFilePath()); + fail(); + } catch (AccessDeniedException expected) {} + } + + @Test + public void test_newBufferedReader_NPE() throws IOException { + try { + Files.newBufferedReader(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_newBufferedReader$Path$Charset() throws IOException { + BufferedReader bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath(), + StandardCharsets.US_ASCII); + assertEquals(TEST_FILE_DATA, bufferedReader.readLine()); + + // When the file has unicode characters. + writeToFile(filesSetup.getDataFilePath(), UTF_16_DATA); + bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath(), + StandardCharsets.US_ASCII); + try { + bufferedReader.readLine(); + fail(); + } catch (MalformedInputException expected) {} + } + + @Test + public void test_newBufferedReader$Path$Charset_NPE() throws IOException { + try { + Files.newBufferedReader(null, StandardCharsets.US_ASCII); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.newBufferedReader(filesSetup.getDataFilePath(), null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_newBufferedWriter() throws IOException { + BufferedWriter bufferedWriter = Files.newBufferedWriter(filesSetup.getTestPath()); + bufferedWriter.write(TEST_FILE_DATA); + bufferedWriter.close(); + assertEquals(TEST_FILE_DATA, + readFromFile(filesSetup.getTestPath())); + + // When file exists, it should start writing from the beginning. + bufferedWriter = Files.newBufferedWriter(filesSetup.getDataFilePath()); + bufferedWriter.write(TEST_FILE_DATA_2); + bufferedWriter.close(); + assertEquals(TEST_FILE_DATA_2, + readFromFile(filesSetup.getDataFilePath())); + + // When file is read-only. + Set perm = PosixFilePermissions.fromString("r--------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + Files.setPosixFilePermissions(filesSetup.getDataFilePath(), perm); + try { + Files.newBufferedWriter(filesSetup.getDataFilePath()); + fail(); + } catch (AccessDeniedException expected) {} + } + + @Test + public void test_newBufferedWriter_NPE() throws IOException { + try { + Files.newBufferedWriter(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_newBufferedWriter$Path$Charset() throws IOException { + BufferedWriter bufferedWriter = Files.newBufferedWriter(filesSetup.getTestPath(), + StandardCharsets.US_ASCII); + bufferedWriter.write(TEST_FILE_DATA); + bufferedWriter.close(); + assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath())); + } + + @Test + public void test_newBufferedWriter$Path$Charset_NPE() throws IOException { + try { + Files.newBufferedWriter(null, StandardCharsets.US_ASCII); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.newBufferedWriter(filesSetup.getTestPath(), (OpenOption[]) null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_newByteChannel() throws IOException { + // When file doesn't exist + try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getTestPath())) { + fail(); + } catch (NoSuchFileException expected) { + } + + // When file exists. + + // File opens in READ mode by default. The channel is non writable by default. + try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath())) { + sbc.write(ByteBuffer.allocate(10)); + fail(); + } catch (NonWritableChannelException expected) { + } + + // Read a file. + try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath())) { + ByteBuffer readBuffer = ByteBuffer.allocate(10); + int bytesReadCount = sbc.read(readBuffer); + + String readData = new String(Arrays.copyOf(readBuffer.array(), bytesReadCount), + StandardCharsets.UTF_8); + assertEquals(TEST_FILE_DATA, readData); + } + } + + @Test + public void test_newByteChannel_openOption_WRITE() throws IOException { + // When file doesn't exist + try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getTestPath(), WRITE)) { + fail(); + } catch (NoSuchFileException expected) { + } + + // When file exists. + + try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(), WRITE)) { + sbc.read(ByteBuffer.allocate(10)); + fail(); + } catch (NonReadableChannelException expected) { + } + + // Write in file. + try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(), WRITE)) { + sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes())); + sbc.close(); + + try (InputStream is = Files.newInputStream(filesSetup.getDataFilePath())) { + String expectedFileData = TEST_FILE_DATA_2 + + TEST_FILE_DATA.substring( + TEST_FILE_DATA_2.length()); + assertEquals(expectedFileData, readFromInputStream(is)); + } + } + } + + @Test + public void test_newByteChannel_openOption_WRITE_READ() throws IOException { + try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(), WRITE, + READ, SYNC/* Sync makes sure the that InputStream is able to read content written by + the seekable byte channel without closing/flushing it. */)) { + ByteBuffer readBuffer = ByteBuffer.allocate(10); + int bytesReadCount = sbc.read(readBuffer); + + String readData = new String(Arrays.copyOf(readBuffer.array(), bytesReadCount), + StandardCharsets.UTF_8); + assertEquals(TEST_FILE_DATA, readData); + + // Pointer will move to the end of the file after read operation. The write should + // append the data at the end of the file. + sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes())); + try (InputStream is = Files.newInputStream(filesSetup.getDataFilePath())) { + String expectedFileData = TEST_FILE_DATA + TEST_FILE_DATA_2; + assertEquals(expectedFileData, readFromInputStream(is)); + } + } + } + + @Test + public void test_newByteChannel_NPE() throws IOException { + try (SeekableByteChannel sbc = Files.newByteChannel(null)) { + fail(); + } catch(NullPointerException expected) {} + + try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(), + (OpenOption[]) null)) { + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_readAllLine() throws IOException { + // Multi-line file. + assertTrue(Files.exists(filesSetup.getDataFilePath())); + writeToFile(filesSetup.getDataFilePath(), "\n" + TEST_FILE_DATA_2, + APPEND); + List out = Files.readAllLines(filesSetup.getDataFilePath()); + assertEquals(2, out.size()); + assertEquals(TEST_FILE_DATA, out.get(0)); + assertEquals(TEST_FILE_DATA_2, out.get(1)); + + // When file doesn't exist. + filesSetup.reset(); + try { + Files.readAllLines(filesSetup.getTestPath()); + fail(); + } catch (NoSuchFileException expected) {} + + // When file is a directory. + filesSetup.reset(); + try { + Files.readAllLines(filesSetup.getTestDirPath()); + fail(); + } catch (IOException expected) {} + } + + @Test + public void test_readAllLine_NPE() throws IOException { + try { + Files.readAllLines(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_readAllLine$Path$Charset() throws IOException { + assertTrue(Files.exists(filesSetup.getDataFilePath())); + writeToFile(filesSetup.getDataFilePath(), "\n" + TEST_FILE_DATA_2, APPEND); + List out = Files.readAllLines(filesSetup.getDataFilePath(), StandardCharsets.UTF_8); + assertEquals(2, out.size()); + assertEquals(TEST_FILE_DATA, out.get(0)); + assertEquals(TEST_FILE_DATA_2, out.get(1)); + + // With UTF-16. + out = Files.readAllLines(filesSetup.getDataFilePath(), StandardCharsets.UTF_16); + assertEquals(1, out.size()); + + // UTF-8 data read as UTF-16 + String expectedOutput = new String((TEST_FILE_DATA + '\n' + TEST_FILE_DATA_2).getBytes(), + StandardCharsets.UTF_16); + assertEquals(expectedOutput, out.get(0)); + + // When file doesn't exist. + filesSetup.reset(); + try { + Files.readAllLines(filesSetup.getTestPath(), StandardCharsets.UTF_16); + fail(); + } catch (NoSuchFileException expected) {} + + // When file is a directory. + filesSetup.reset(); + try { + Files.readAllLines(filesSetup.getTestDirPath(), StandardCharsets.UTF_16); + fail(); + } catch (IOException expected) {} + } + + @Test + public void test_readAllLine$Path$Charset_NPE() throws IOException { + try { + Files.readAllLines(null, StandardCharsets.UTF_16); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.readAllLines(filesSetup.getDataFilePath(), null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_walk$Path$FileVisitOption() throws IOException { + // Directory structure. + // root + // ├── dir1 + // │ ├── dir2 + // │ │ ├── dir3 + // │ │ └── file5 + // │ ├── dir4 + // │ └── file3 + // ├── dir5 + // └── file1 + // + // depth will be 2. file4, file5, dir3 is not reachable. + + Path rootDir = Paths.get(filesSetup.getTestDir(), "root"); + Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1"); + Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2"); + Path dir3 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/dir3"); + Path dir4 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir4"); + Path dir5 = Paths.get(filesSetup.getTestDir(), "root/dir5"); + Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1"); + Path file3 = Paths.get(filesSetup.getTestDir(), "root/dir1/file3"); + Path file5 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/file5"); + + Files.createDirectories(dir3); + Files.createDirectories(dir4); + Files.createDirectories(dir5); + Files.createFile(file1); + Files.createFile(file3); + Files.createFile(file5); + + Set expectedDirSet = new HashSet<>(); + expectedDirSet.add(rootDir); + expectedDirSet.add(dir1); + expectedDirSet.add(dir2); + expectedDirSet.add(dir4); + expectedDirSet.add(file3); + expectedDirSet.add(dir5); + expectedDirSet.add(file1); + + Set dirSet = new HashSet<>(); + try(Stream pathStream = Files.walk(rootDir, 2, + FileVisitOption.FOLLOW_LINKS)) { + pathStream.forEach(path -> dirSet.add(path)); + } + + assertEquals(expectedDirSet, dirSet); + + // Test case when Path doesn't exist. + try (Stream pathStream = Files.walk(filesSetup.getTestPath(), 2, + FileVisitOption.FOLLOW_LINKS)){ + fail(); + } catch (NoSuchFileException expected) {} + + // Test case when Path is a not a directory. + expectedDirSet.clear(); + dirSet.clear(); + expectedDirSet.add(filesSetup.getDataFilePath()); + try (Stream pathStream = Files.walk(filesSetup.getDataFilePath(), 2, + FileVisitOption.FOLLOW_LINKS)){ + pathStream.forEach(path -> dirSet.add(path)); + } + assertEquals(expectedDirSet, dirSet); + + // Test case when Path doesn't exist. + try (Stream pathStream = Files.walk(rootDir, -1, FileVisitOption.FOLLOW_LINKS)){ + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test + public void test_walk_FileSystemLoopException() throws IOException { + // Directory structure. + // root + // └── dir1 + // └── file1 + // + // file1 is symbolic link to dir1 + + Path rootDir = Paths.get(filesSetup.getTestDir(), "root"); + Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir"); + Path file1 = Paths.get(filesSetup.getTestDir(), "root/dir/file1"); + Files.createDirectories(dir1); + Files.createSymbolicLink(file1, dir1.toAbsolutePath()); + assertTrue(Files.isSymbolicLink(file1)); + try(Stream pathStream = Files.walk(rootDir, FileVisitOption.FOLLOW_LINKS)) { + pathStream.forEach(path -> assertNotNull(path)); + fail(); + } catch (UncheckedIOException expected) { + assertTrue(expected.getCause() instanceof FileSystemLoopException); + } + } + + @Test + public void test_walk() throws IOException { + // Directory structure. + // root + // ├── dir1 + // │ ├── dir2 + // │ │ ├── dir3 + // │ │ └── file5 + // │ ├── dir4 + // │ └── file3 + // ├── dir5 + // └── file1 + // + + Path rootDir = Paths.get(filesSetup.getTestDir(), "root"); + Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1"); + Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2"); + Path dir3 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/dir3"); + Path dir4 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir4"); + Path dir5 = Paths.get(filesSetup.getTestDir(), "root/dir5"); + Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1"); + Path file3 = Paths.get(filesSetup.getTestDir(), "root/dir1/file3"); + Path file5 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/file5"); + + Files.createDirectories(dir3); + Files.createDirectories(dir4); + Files.createDirectories(dir5); + Files.createFile(file1); + Files.createFile(file3); + Files.createFile(file5); + + Set expectedDirSet = new HashSet<>(); + expectedDirSet.add(rootDir.getFileName()); + expectedDirSet.add(dir1.getFileName()); + expectedDirSet.add(dir2.getFileName()); + expectedDirSet.add(dir4.getFileName()); + expectedDirSet.add(file3.getFileName()); + expectedDirSet.add(dir5.getFileName()); + expectedDirSet.add(file1.getFileName()); + expectedDirSet.add(file5.getFileName()); + expectedDirSet.add(dir3.getFileName()); + + Set dirSet = new HashSet<>(); + try (Stream pathStream = Files.walk(rootDir)) { + pathStream.forEach(path -> dirSet.add(path.getFileName())); + } + + assertEquals(expectedDirSet, dirSet); + + + // Test case when Path doesn't exist. + try (Stream pathStream = Files.walk(filesSetup.getTestPath())){ + fail(); + } catch (NoSuchFileException expected) {} + + // Test case when Path is a not a directory. + expectedDirSet.clear(); + dirSet.clear(); + expectedDirSet.add(filesSetup.getDataFilePath()); + try (Stream pathStream = Files.walk(filesSetup.getDataFilePath())) { + pathStream.forEach(path -> dirSet.add(path)); + } + assertEquals(expectedDirSet, dirSet); + } + + @Test + public void test_walk_depthFirst() throws IOException { + // Directory structure. + // root + // ├── dir1 + // │ └── file1 + // └── dir2 + // └── file2 + // + + Path rootDir = Paths.get(filesSetup.getTestDir(), "root"); + Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1"); + Path file1 = Paths.get(filesSetup.getTestDir(), "root/dir1/file1"); + Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir2"); + Path file2 = Paths.get(filesSetup.getTestDir(), "root/dir2/file2"); + Files.createDirectories(dir1); + Files.createDirectories(dir2); + Files.createFile(file1); + Files.createFile(file2); + List fileKeyList = new ArrayList<>(); + try(Stream pathStream = Files.walk(rootDir, FileVisitOption.FOLLOW_LINKS)) { + pathStream.forEach(path -> fileKeyList.add(path.getFileName())); + } + assertEquals(rootDir.getFileName(), fileKeyList.get(0)); + if (fileKeyList.get(1).equals(dir1.getFileName())) { + assertEquals(file1.getFileName(), fileKeyList.get(2)); + assertEquals(dir2.getFileName(), fileKeyList.get(3)); + assertEquals(file2.getFileName(), fileKeyList.get(4)); + } else if (fileKeyList.get(1).equals(dir2.getFileName())) { + assertEquals(file2.getFileName(), fileKeyList.get(2)); + assertEquals(dir1.getFileName(), fileKeyList.get(3)); + assertEquals(file1.getFileName(), fileKeyList.get(4)); + } else { + fail(); + } + } + + @Test + public void test_walk$Path$Int$LinkOption_IllegalArgumentException() throws IOException { + Map dirMap = new HashMap<>(); + Path rootDir = Paths.get(filesSetup.getTestDir(), "rootDir"); + try (Stream pathStream = Files.walk(rootDir, -1, + FileVisitOption.FOLLOW_LINKS)) { + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test + public void test_walk$Path$FileVisitOption_NPE() throws IOException { + try { + Files.walk(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_write$Path$byte$OpenOption() throws IOException { + Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes()); + assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getDataFilePath())); + } + + @Test + public void test_write$Path$byte$OpenOption_OpenOption() throws IOException { + Files.write(filesSetup.getTestPath(), TEST_FILE_DATA_2.getBytes(), CREATE_NEW); + assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getTestPath())); + + filesSetup.reset(); + Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes(), TRUNCATE_EXISTING); + assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getDataFilePath())); + + filesSetup.reset(); + Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes(), APPEND); + assertEquals(TEST_FILE_DATA + TEST_FILE_DATA_2, readFromFile( + filesSetup.getDataFilePath())); + + filesSetup.reset(); + try { + Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes(), READ); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test + public void test_write$Path$byte$OpenOption_NPE() throws IOException { + try { + Files.write(null, TEST_FILE_DATA_2.getBytes(), CREATE_NEW); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.write(filesSetup.getTestPath(), (byte[]) null, CREATE_NEW); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.write(filesSetup.getTestPath(), TEST_FILE_DATA_2.getBytes(), (OpenOption[]) null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_write$Path$Iterable$Charset$OpenOption() throws IOException { + List lines = new ArrayList<>(); + lines.add(TEST_FILE_DATA_2); + lines.add(TEST_FILE_DATA); + Files.write(filesSetup.getDataFilePath(), lines, StandardCharsets.UTF_16); + List readLines = Files.readAllLines(filesSetup.getDataFilePath(), + StandardCharsets.UTF_16); + assertEquals(readLines, lines); + } + + @Test + public void test_write$Path$Iterable$Charset$OpenOption_NPE() throws IOException { + try { + Files.write(null, new ArrayList<>(), StandardCharsets.UTF_16); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.write(filesSetup.getDataFilePath(), null, StandardCharsets.UTF_16); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.write(filesSetup.getDataFilePath(), new ArrayList<>(), (OpenOption[]) null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_write$Path$Iterable$OpenOption() throws IOException { + List lines = new ArrayList<>(); + lines.add(TEST_FILE_DATA_2); + lines.add(TEST_FILE_DATA); + Files.write(filesSetup.getDataFilePath(), lines); + List readLines = Files.readAllLines(filesSetup.getDataFilePath()); + assertEquals(readLines, lines); + } + + @Test + public void test_write$Path$Iterable$OpenOption_NPE() throws IOException { + try { + Files.write(null, new ArrayList()); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.write(filesSetup.getDataFilePath(), (Iterable) null); + fail(); + } catch (NullPointerException expected) {} + } + + // The ability for Android apps to create hard links was removed in + // https://android-review.googlesource.com/144092 (March 2015). + // https://b/19953790. + @Test + public void test_createLink() throws IOException { + try { + Files.createLink(filesSetup.getTestPath(), filesSetup.getDataFilePath()); + fail(); + } catch (AccessDeniedException expected) {} + } + + @Test + public void test_createTempDirectory$Path$String$FileAttributes() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + + String tmpDir = "tmpDir"; + Path tmpDirPath = Files.createTempDirectory(filesSetup.getTestDirPath(), tmpDir, attr); + assertTrue(tmpDirPath.getFileName().toString().startsWith(tmpDir)); + assertEquals(filesSetup.getTestDirPath(), tmpDirPath.getParent()); + assertTrue(Files.isDirectory(tmpDirPath)); + assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name())); + + filesSetup.reset(); + // Test case when prefix is null. + tmpDirPath = Files.createTempDirectory(filesSetup.getTestDirPath(), null, attr); + assertEquals(filesSetup.getTestDirPath(), tmpDirPath.getParent()); + assertTrue(Files.isDirectory(tmpDirPath)); + assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name())); + + try { + Files.createTempDirectory(null, tmpDir, attr); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.createTempDirectory(filesSetup.getTestDirPath(), tmpDir, null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_createTempDirectory$String$FileAttributes() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + + Path tmpDirectoryLocation = Paths.get(System.getProperty("java.io.tmpdir")); + + String tmpDir = "tmpDir"; + Path tmpDirPath = Files.createTempDirectory(tmpDir, attr); + assertTrue(tmpDirPath.getFileName().toString().startsWith(tmpDir)); + assertEquals(tmpDirectoryLocation, tmpDirPath.getParent()); + assertTrue(Files.isDirectory(tmpDirPath)); + assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name())); + + // Test case when prefix is null. + filesSetup.reset(); + tmpDirPath = Files.createTempDirectory(null, attr); + assertEquals(tmpDirectoryLocation, tmpDirPath.getParent()); + assertTrue(Files.isDirectory(tmpDirPath)); + assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name())); + + try { + Files.createTempDirectory(tmpDir, null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_createTempFile$Path$String$String$FileAttributes() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + + String tmpFilePrefix = "prefix"; + String tmpFileSuffix = "suffix"; + + Path tmpFilePath = Files.createTempFile(filesSetup.getTestDirPath(), tmpFilePrefix, + tmpFileSuffix, attr); + + assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix)); + assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix)); + assertEquals(filesSetup.getTestDirPath(), tmpFilePath.getParent()); + assertTrue(Files.isRegularFile(tmpFilePath)); + assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name())); + + // Test case when prefix is null. + filesSetup.reset(); + tmpFilePath = Files.createTempFile(filesSetup.getTestDirPath(), null, + tmpFileSuffix, attr); + assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix)); + assertEquals(filesSetup.getTestDirPath(), tmpFilePath.getParent()); + assertTrue(Files.isRegularFile(tmpFilePath)); + assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name())); + + // Test case when suffix is null. + filesSetup.reset(); + tmpFilePath = Files.createTempFile(filesSetup.getTestDirPath(), tmpFilePrefix, + null, attr); + assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix)); + assertEquals(filesSetup.getTestDirPath(), tmpFilePath.getParent()); + assertTrue(Files.isRegularFile(tmpFilePath)); + assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name())); + + try { + Files.createTempFile(null, tmpFilePrefix, tmpFileSuffix, attr); + fail(); + } catch (NullPointerException expected) {} + + try { + Files.createTempFile(filesSetup.getTestDirPath(), tmpFilePrefix, tmpFileSuffix, + null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_createTempFile$String$String$FileAttributes() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + + Path tmpDirectoryLocation = Paths.get(System.getProperty( + "java.io.tmpdir")); + + String tmpFilePrefix = "prefix"; + String tmpFileSuffix = "suffix"; + Path tmpFilePath = Files.createTempFile(tmpFilePrefix, tmpFileSuffix, attr); + assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix)); + assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix)); + assertEquals(tmpDirectoryLocation, tmpFilePath.getParent()); + assertTrue(Files.isRegularFile(tmpFilePath)); + assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name())); + + // Test case when prefix is null. + filesSetup.reset(); + tmpFilePath = Files.createTempFile(null, tmpFileSuffix, attr); + assertEquals(tmpDirectoryLocation, tmpFilePath.getParent()); + assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix)); + assertTrue(Files.isRegularFile(tmpFilePath)); + assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name())); + + // Test case when suffix is null. + filesSetup.reset(); + tmpFilePath = Files.createTempFile(tmpFilePrefix, null, attr); + assertEquals(tmpDirectoryLocation, tmpFilePath.getParent()); + assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix)); + assertTrue(Files.isRegularFile(tmpFilePath)); + assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name())); + + try { + Files.createTempFile(tmpFilePrefix, tmpFileSuffix, null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_newByteChannel$Path$Set_OpenOption$FileAttributes() throws Exception { + FileAttribute stubFileAttribute = mock(FileAttribute.class); + Set stubSet = new HashSet<>(); + Files.newByteChannel(mockPath, stubSet, stubFileAttribute); + + verify(mockFileSystemProvider).newByteChannel(mockPath, stubSet, stubFileAttribute); + } + + // -- Mock Class -- + + private static class TestFileVisitor implements FileVisitor { + + final Map dirMap; + LinkOption option[]; + List keyList; + + public TestFileVisitor(Map dirMap) { + this(dirMap, (List) null); + } + + public TestFileVisitor(Map dirMap, Set option) { + this.dirMap = dirMap; + for (FileVisitOption fileVisitOption : option) { + if (fileVisitOption.equals(FileVisitOption.FOLLOW_LINKS)) { + this.option = new LinkOption[0]; + } + } + + if (this.option == null) { + this.option = new LinkOption[] {LinkOption.NOFOLLOW_LINKS}; + } + } + + public TestFileVisitor(Map dirMap, List pathList) { + this.dirMap = dirMap; + this.option = new LinkOption[] {LinkOption.NOFOLLOW_LINKS}; + keyList = pathList; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + if (keyList != null) { + keyList.add(dir.getFileName()); + } + dirMap.put(dir.getFileName(), VisitOption.PRE_VISIT_DIRECTORY); + return CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (keyList != null) { + keyList.add(file.getFileName()); + } + dirMap.put(file.getFileName(), VisitOption.VISIT_FILE); + return CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + if (exc != null) { + throw exc; + } + return TERMINATE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + if (exc != null) { + throw exc; + } + if (dirMap.getOrDefault(dir.getFileName(), VisitOption.UNVISITED) + != VisitOption.PRE_VISIT_DIRECTORY) { + return TERMINATE; + } else { + dirMap.put(dir.getFileName(), VisitOption.POST_VISIT_DIRECTORY); + return CONTINUE; + } + } + } + + private enum VisitOption { + PRE_VISIT_DIRECTORY, + VISIT_FILE, + POST_VISIT_DIRECTORY, + UNVISITED, + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/FilesSetup.java b/luni/src/test/java/libcore/java/nio/file/FilesSetup.java new file mode 100644 index 000000000..70e717d5f --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/FilesSetup.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.CopyOption; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; + +class FilesSetup implements TestRule { + + final static String DATA_FILE = "dataFile"; + + final static String NON_EXISTENT_FILE = "nonExistentFile"; + + final static String TEST_FILE_DATA = "hello"; + + final static String TEST_FILE_DATA_2 = "test"; + + /** + * Data that includes characters code above the US-ASCII range and will be more obviously + * corrupted if encoded / decoded incorrectly than + * {@link #TEST_FILE_DATA} / {@link #TEST_FILE_DATA_2}. + */ + final static String UTF_16_DATA = "परीक्षण"; + + private String testDir; + + private Path dataFilePath; + + private Path testPath; + + private Path testDirPath; + + private boolean filesInitialized = false; + + void setUp() throws Exception { + initializeFiles(); + } + + void tearDown() throws Exception { + filesInitialized = false; + clearAll(); + } + + private void initializeFiles() throws IOException { + testDirPath = Files.createTempDirectory("testDir"); + testDir = testDirPath.toString(); + dataFilePath = Paths.get(testDir, DATA_FILE); + testPath = Paths.get(testDir, NON_EXISTENT_FILE); + File testInputFile = new File(testDir, DATA_FILE); + if (!testInputFile.exists()) { + testInputFile.createNewFile(); + } + FileWriter fw = new FileWriter(testInputFile.getAbsoluteFile()); + BufferedWriter bw = new BufferedWriter(fw); + bw.write(TEST_FILE_DATA); + bw.close(); + filesInitialized = true; + } + + Path getTestPath() { + checkState(); + return testPath; + } + + Path getDataFilePath() { + checkState(); + return dataFilePath; + } + + Path getTestDirPath() { + checkState(); + return testDirPath; + } + + String getTestDir() { + checkState(); + return testDir; + } + + private void checkState() { + if (!filesInitialized) { + throw new IllegalStateException("Files are not setup."); + } + } + + void clearAll() throws IOException { + Path root = Paths.get(testDir); + delete(root); + } + + void reset() throws IOException { + clearAll(); + initializeFiles(); + } + + private static void delete(Path path) throws IOException { + if (Files.isDirectory(path)) { + DirectoryStream dirStream = Files.newDirectoryStream(path); + dirStream.forEach( + p -> { + try { + delete(p); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + ); + dirStream.close(); + } + try { + Files.deleteIfExists(path); + } catch (Exception e) { + // Do nothing + } + } + + static void writeToFile(Path file, String data, OpenOption... option) throws IOException { + OutputStream os = Files.newOutputStream(file, option); + os.write(data.getBytes()); + os.close(); + } + + static String readFromFile(Path file) throws IOException { + InputStream is = Files.newInputStream(file); + return readFromInputStream(is); + } + + static String readFromInputStream(InputStream is) throws IOException { + byte[] input = new byte[10000]; + is.read(input); + return new String(input, "UTF-8").trim(); + } + + static Process execCmdAndWaitForTermination(String... cmdList) + throws InterruptedException, IOException { + Process process = Runtime.getRuntime().exec(cmdList); + // Wait for the process to terminate. + process.waitFor(); + return process; + } + + @Override + public Statement apply(Statement statement, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + setUp(); + statement.evaluate(); + } finally { + tearDown(); + } + } + }; + } + + Path getPathInTestDir(String path) { + return Paths.get(getTestDir(), path); + } + + /** + * Non Standard CopyOptions. + */ + enum NonStandardOption implements CopyOption, OpenOption { + OPTION1, + } + +} diff --git a/luni/src/test/java/libcore/java/nio/file/FilesTest.java b/luni/src/test/java/libcore/java/nio/file/FilesTest.java new file mode 100644 index 000000000..e1250a001 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/FilesTest.java @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.FileChannel; +import java.nio.file.CopyOption; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.NotDirectoryException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.nio.file.spi.FileSystemProvider; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.PatternSyntaxException; + +import static java.nio.file.StandardOpenOption.APPEND; +import static java.nio.file.StandardOpenOption.READ; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class FilesTest { + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Rule + public FilesSetup filesSetup = new FilesSetup(); + + @Mock + private Path mockPath; + @Mock + private Path mockPath2; + @Mock + private FileSystem mockFileSystem; + @Mock + private FileSystemProvider mockFileSystemProvider; + + @Before + public void setUp() throws Exception { + when(mockPath.getFileSystem()).thenReturn(mockFileSystem); + when(mockPath2.getFileSystem()).thenReturn(mockFileSystem); + when(mockFileSystem.provider()).thenReturn(mockFileSystemProvider); + } + + @Test + public void test_newInputStream() throws IOException { + try (InputStream is = new ByteArrayInputStream(new byte[0])) { + + when(mockFileSystemProvider.newInputStream(mockPath, READ)).thenReturn(is); + + assertSame(is, Files.newInputStream(mockPath, READ)); + + verify(mockFileSystemProvider).newInputStream(mockPath, READ); + } + } + + @Test + public void test_newOutputStream() throws IOException { + try (OutputStream os = new ByteArrayOutputStream()) { + + when(mockFileSystemProvider.newOutputStream(mockPath, APPEND)).thenReturn(os); + + assertSame(os, Files.newOutputStream(mockPath, APPEND)); + + verify(mockFileSystemProvider).newOutputStream(mockPath, APPEND); + } + } + + @Test + public void test_newByteChannel() throws IOException { + try (FileChannel sfc = FileChannel.open(filesSetup.getDataFilePath())) { + HashSet openOptions = new HashSet<>(); + openOptions.add(READ); + + when(mockFileSystemProvider.newByteChannel(mockPath, openOptions)).thenReturn(sfc); + + assertSame(sfc, Files.newByteChannel(mockPath, READ)); + + verify(mockFileSystemProvider).newByteChannel(mockPath, openOptions); + } + } + + @Test + public void test_createFile() throws IOException { + assertFalse(Files.exists(filesSetup.getTestPath())); + Files.createFile(filesSetup.getTestPath()); + assertTrue(Files.exists(filesSetup.getTestPath())); + + // File with unicode name. + Path unicodeFilePath = filesSetup.getPathInTestDir("परीक्षण फ़ाइल"); + Files.createFile(unicodeFilePath); + Files.exists(unicodeFilePath); + + // When file exists. + try { + Files.createFile(filesSetup.getDataFilePath()); + fail(); + } catch(FileAlreadyExistsException expected) {} + } + + @Test + public void test_createFile_NPE() throws IOException { + try { + Files.createFile(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_createFile$String$Attr() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + Files.createFile(filesSetup.getTestPath(), attr); + assertEquals(attr.value(), Files.getAttribute(filesSetup.getTestPath(), attr.name())); + + // Creating a new file and passing multiple attribute of the same name. + perm = PosixFilePermissions.fromString("rw-------"); + FileAttribute> attr1 = PosixFilePermissions.asFileAttribute(perm); + Path filePath2 = filesSetup.getPathInTestDir("new_file"); + Files.createFile(filePath2, attr, attr1); + // Value should be equal to the last attribute passed. + assertEquals(attr1.value(), Files.getAttribute(filePath2, attr.name())); + + // When file exists. + try { + Files.createFile(filesSetup.getDataFilePath(), attr); + fail(); + } catch(FileAlreadyExistsException expected) {} + } + + @Test + public void test_createDirectory_delegation() throws IOException { + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + assertEquals(mockPath, Files.createDirectory(mockPath, attr)); + verify(mockFileSystemProvider).createDirectory(mockPath, attr); + } + + @Test + public void test_createDirectories() throws IOException { + // Should be able to create parent directories. + Path dirPath = filesSetup.getPathInTestDir("dir1/dir2/dir3"); + assertFalse(Files.exists(dirPath)); + Files.createDirectories(dirPath); + assertTrue(Files.isDirectory(dirPath)); + + // Creating an existing directory. Should not throw any error. + Files.createDirectories(dirPath); + } + + @Test + public void test_createDirectories_NPE() throws IOException { + try { + Files.createDirectories(null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_createDirectories$Path$Attr() throws IOException { + Path dirPath = filesSetup.getPathInTestDir("dir1/dir2/dir3"); + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + assertFalse(Files.exists(dirPath)); + Files.createDirectories(dirPath, attr); + assertEquals(attr.value(), Files.getAttribute(dirPath, attr.name())); + + // Creating an existing directory with new permissions. + perm = PosixFilePermissions.fromString("rw-------"); + FileAttribute> attr1 = PosixFilePermissions.asFileAttribute(perm); + Files.createDirectories(dirPath, attr); + + // Value should not change as the directory exists. + assertEquals(attr.value(), Files.getAttribute(dirPath, attr.name())); + + // Creating a new directory and passing multiple attribute of the same name. + Path dirPath2 = filesSetup.getPathInTestDir("dir1/dir2/dir4"); + Files.createDirectories(dirPath2, attr, attr1); + // Value should be equal to the last attribute passed. + assertEquals(attr1.value(), Files.getAttribute(dirPath2, attr.name())); + } + + @Test + public void test_createDirectories$Path$Attr_NPE() throws IOException { + Path dirPath = filesSetup.getPathInTestDir("dir1/dir2/dir3"); + Set perm = PosixFilePermissions.fromString("rwx------"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perm); + try { + Files.createDirectories(null, attr); + fail(); + } catch(NullPointerException expected) {} + + try { + Files.createDirectories(dirPath, (FileAttribute[]) null); + fail(); + } catch(NullPointerException expected) {} + } + + @Test + public void test_newDirectoryStream() throws IOException { + // Directory setup. + Path path_dir1 = filesSetup.getPathInTestDir("newDir1"); + Path path_dir2 = filesSetup.getPathInTestDir("newDir1/newDir2"); + Path path_dir3 = filesSetup.getPathInTestDir("newDir1/newDir3"); + Path path_file1 = filesSetup.getPathInTestDir("newDir1/newFile1"); + Path path_file2 = filesSetup.getPathInTestDir("newDir1/newFile2"); + Path path_file3 = filesSetup.getPathInTestDir("newDir1/newDir2/newFile3"); + + Files.createDirectory(path_dir1); + Files.createDirectory(path_dir2); + Files.createDirectory(path_dir3); + Files.createFile(path_file1); + Files.createFile(path_file2); + Files.createFile(path_file3); + + HashSet pathSet = new HashSet<>(); + HashSet expectedPathSet = new HashSet<>(); + expectedPathSet.add(path_dir2); + expectedPathSet.add(path_dir3); + expectedPathSet.add(path_file1); + expectedPathSet.add(path_file2); + + try (DirectoryStream directoryStream = Files.newDirectoryStream(path_dir1)) { + directoryStream.forEach(k -> pathSet.add(k)); + assertEquals(expectedPathSet, pathSet); + } + } + + @Test + public void test_newDirectoryStream_Exception() throws IOException { + + // Non existent directory. + Path path_dir1 = filesSetup.getPathInTestDir("newDir1"); + try (DirectoryStream directoryStream = Files.newDirectoryStream(path_dir1)) { + fail(); + } catch (NoSuchFileException expected) { + } + + // File instead of directory. + Path path_file1 = filesSetup.getPathInTestDir("newFile1"); + Files.createFile(path_file1); + try (DirectoryStream directoryStream = Files.newDirectoryStream(path_file1)) { + fail(); + } catch (NotDirectoryException expected) { + } + + try (DirectoryStream directoryStream = Files.newDirectoryStream(null)) { + fail(); + } catch (NullPointerException expected) { + } + } + + @Test + public void test_newDirectoryStream$Path$String() throws IOException { + // Directory setup. + Path path_root = filesSetup.getPathInTestDir("dir"); + Path path_java1 = filesSetup.getPathInTestDir("dir/f1.java"); + Path path_java2 = filesSetup.getPathInTestDir("dir/f2.java"); + Path path_java3 = filesSetup.getPathInTestDir("dir/f3.java"); + + Path path_txt1 = filesSetup.getPathInTestDir("dir/f1.txt"); + Path path_txt2 = filesSetup.getPathInTestDir("dir/f2.txt"); + Path path_txt3 = filesSetup.getPathInTestDir("dir/f3.txt"); + + Files.createDirectory(path_root); + // A directory with .java extension. + Files.createDirectory(path_java1); + Files.createFile(path_java2); + Files.createFile(path_java3); + Files.createFile(path_txt1); + Files.createFile(path_txt2); + Files.createFile(path_txt3); + + HashSet pathSet = new HashSet<>(); + HashSet expectedPathSet = new HashSet<>(); + expectedPathSet.add(path_java1); + expectedPathSet.add(path_java2); + expectedPathSet.add(path_java3); + + try (DirectoryStream directoryStream = Files.newDirectoryStream(path_root, "*.java")) + { + directoryStream.forEach(k -> pathSet.add(k)); + assertEquals(expectedPathSet, pathSet); + } + } + + @Test + public void test_newDirectoryStream$Path$String_Exception() throws IOException { + + // Non existent directory. + Path path_dir1 = filesSetup.getPathInTestDir("newDir1"); + try (DirectoryStream directoryStream = Files.newDirectoryStream(path_dir1, "*.c")) { + fail(); + } catch (NoSuchFileException expected) { + } + + // File instead of directory. + Path path_file1 = filesSetup.getPathInTestDir("newFile1"); + Files.createFile(path_file1); + try (DirectoryStream directoryStream = Files.newDirectoryStream(path_file1, "*.c")) { + fail(); + } catch (NotDirectoryException expected) { + } + + Files.createFile(path_dir1); + try (DirectoryStream directoryStream = Files.newDirectoryStream(path_file1, "[a")) { + fail(); + } catch (PatternSyntaxException expected) { + } + + try (DirectoryStream directoryStream = Files.newDirectoryStream(null, "[a")) { + fail(); + } catch (NullPointerException expected) { + } + + try (DirectoryStream directoryStream = Files.newDirectoryStream(path_dir1, + (String)null)) { + fail(); + } catch (NullPointerException expected) { + } + } + + @Test + public void test_createSymbolicLink() throws IOException { + FileAttribute mockFileAttribute = mock(FileAttribute.class); + assertEquals(mockPath, Files.createSymbolicLink(mockPath, mockPath2, mockFileAttribute)); + verify(mockFileSystemProvider).createSymbolicLink(mockPath, mockPath2, mockFileAttribute); + } + + @Test + public void test_delete() throws IOException { + Files.delete(mockPath); + verify(mockFileSystemProvider).delete(mockPath); + } + + @Test + public void test_deleteIfExist() throws IOException { + when(mockFileSystemProvider.deleteIfExists(mockPath)).thenReturn(true); + assertTrue(Files.deleteIfExists(mockPath)); + verify(mockFileSystemProvider).deleteIfExists(mockPath); + } + + @Test + public void test_copy() throws IOException { + CopyOption copyOption = mock(CopyOption.class); + Files.copy(mockPath, mockPath2, copyOption); + verify(mockFileSystemProvider).copy(mockPath, mockPath2, copyOption); + } +} \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/nio/file/InvalidPathExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/InvalidPathExceptionTest.java new file mode 100644 index 000000000..267581c24 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/InvalidPathExceptionTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.nio.file.InvalidPathException; + +public class InvalidPathExceptionTest extends TestCase { + + public void test_Constructor$String$String$Int() { + String reason = "reason"; + String input = "input"; + int index = 0; + + InvalidPathException exception = new InvalidPathException(input, reason, index); + assertEquals(index, exception.getIndex()); + assertEquals(reason, exception.getReason()); + assertEquals(input, exception.getInput()); + + // Test the case where index = -1. + index = -1; + exception = new InvalidPathException(input, reason, index); + assertEquals(index, exception.getIndex()); + assertEquals(reason, exception.getReason()); + assertEquals(input, exception.getInput()); + + // Test the case where index < -1; + index = -2; + try { + new InvalidPathException(input, reason, index); + fail(); + } catch (IllegalArgumentException expected) {} + + // Test the case where input is null, reason is not null and index >= -1. + try { + index = 0; + new InvalidPathException(null, reason, index); + fail(); + } catch (NullPointerException expected) {} + + // Test the case where input is null, reason is not null and index < -1. + try { + index = -1; + new InvalidPathException(null, reason, index); + fail(); + } catch (NullPointerException expected) {} + + // Test the case where reason is null, input is not null and index >= -1. + try { + index = 0; + new InvalidPathException(input, null, index); + fail(); + } catch (NullPointerException expected) {} + + // Test the case where input is not null, reason is null and index < -1. + try { + index = -1; + new InvalidPathException(input, null, index); + fail(); + } catch (NullPointerException expected) {} + } + + public void test_Constructor$String$String() { + String reason = "reason"; + String input = "input"; + + InvalidPathException exception = new InvalidPathException(input, reason); + assertEquals(-1, exception.getIndex()); + assertEquals(reason, exception.getReason()); + assertEquals(input, exception.getInput()); + + // Test the case where input is null and reason is not null. + try { + new InvalidPathException(null, reason); + fail(); + } catch (NullPointerException expected) {} + + // Test the case where reason is null and input is not null. + try { + new InvalidPathException(input, null); + fail(); + } catch (NullPointerException expected) {} + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/LinkPermissionTest.java b/luni/src/test/java/libcore/java/nio/file/LinkPermissionTest.java new file mode 100644 index 000000000..b01350d0d --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/LinkPermissionTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.nio.file.LinkPermission; + +public class LinkPermissionTest extends TestCase { + + public void test_constructor$String() { + // Only "hard" and "symbolic" are the supported permission target names. + LinkPermission linkPermission = new LinkPermission("hard"); + assertNull(linkPermission.getName()); + + linkPermission = new LinkPermission("symbolic"); + assertNull(linkPermission.getName()); + + // Non supported permission target names. + try { + new LinkPermission("test"); + fail(); + } catch (IllegalArgumentException expected) {} + } + + public void test_constructor$String$String() { + // Only empty string or null is accepted as action. + String actions = ""; + LinkPermission linkPermission = new LinkPermission("hard", actions); + assertEquals("", linkPermission.getActions()); + + actions = null; + linkPermission = new LinkPermission("hard", actions); + assertEquals("", linkPermission.getActions()); + + // When actions is non empty string. + try { + actions = "abc"; + new LinkPermission("hard", actions); + fail(); + } catch (IllegalArgumentException expected) {} + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/LinuxFileSystemTest.java b/luni/src/test/java/libcore/java/nio/file/LinuxFileSystemTest.java new file mode 100644 index 000000000..4d2f4cb71 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/LinuxFileSystemTest.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import sun.nio.fs.LinuxFileSystemProvider; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertTrue; +import static junit.framework.TestCase.fail; +import static libcore.java.nio.file.LinuxFileSystemTestData.getPathExceptionTestData; +import static libcore.java.nio.file.LinuxFileSystemTestData.getPathInputOutputTestData; + +@RunWith(JUnit4.class) +public class LinuxFileSystemTest { + + FileSystem fileSystem = FileSystems.getDefault(); + + @Test + public void test_provider() { + assertTrue(fileSystem.provider() instanceof LinuxFileSystemProvider); + } + + @Test + public void test_isOpen() throws IOException { + assertTrue(fileSystem.isOpen()); + } + + @Test + public void test_close() throws IOException { + // Close is not supported. + try { + fileSystem.close(); + fail(); + } catch (UnsupportedOperationException expected) {} + } + + @Test + public void test_isReadOnly() { + assertFalse(fileSystem.isReadOnly()); + } + + @Test + public void test_getSeparator() { + assertEquals("/", fileSystem.getSeparator()); + } + + @Test + public void test_getRootDirectories() { + Iterable rootDirectories = fileSystem.getRootDirectories(); + Map pathMap = new HashMap<>(); + rootDirectories.forEach(path -> pathMap.put(path, true)); + assertEquals(1, pathMap.size()); + assertTrue(pathMap.get(Paths.get("/"))); + } + + @Test + public void test_getFileStores() { + Iterable fileStores = fileSystem.getFileStores(); + // Asserting if the the list has non zero number stores. + assertTrue(fileStores.iterator().hasNext()); + } + + @Test + public void test_supportedFileAttributeViews() { + Set supportedFileAttributeViewsList = fileSystem.supportedFileAttributeViews(); + assertEquals(6, supportedFileAttributeViewsList.size()); + assertTrue(supportedFileAttributeViewsList.contains("posix")); + assertTrue(supportedFileAttributeViewsList.contains("user")); + assertTrue(supportedFileAttributeViewsList.contains("owner")); + assertTrue(supportedFileAttributeViewsList.contains("unix")); + assertTrue(supportedFileAttributeViewsList.contains("basic")); + assertTrue(supportedFileAttributeViewsList.contains("dos")); + } + + @Test + public void test_get() { + List inputOutputTestCases = getPathInputOutputTestData(); + for (LinuxFileSystemTestData.TestData inputOutputTestCase : inputOutputTestCases) { + Assert.assertEquals(inputOutputTestCase.output, fileSystem.getPath( + inputOutputTestCase.input, inputOutputTestCase.inputArray).toString()); + } + + List exceptionTestCases = getPathExceptionTestData(); + for (LinuxFileSystemTestData.TestData exceptionTestCase : exceptionTestCases) { + try { + fileSystem.getPath(exceptionTestCase.input, exceptionTestCase.inputArray); + Assert.fail(); + } catch (Exception expected) { + Assert.assertEquals(exceptionTestCase.exceptionClass, expected.getClass()); + } + } + } + + @Test + public void test_getPathMatcher_glob() { + PathMatcher pathMatcher = fileSystem.getPathMatcher("glob:" + "*.java"); + assertTrue(pathMatcher.matches(Paths.get("f.java"))); + assertFalse(pathMatcher.matches(Paths.get("f"))); + + pathMatcher = fileSystem.getPathMatcher("glob:" + "*.*"); + assertTrue(pathMatcher.matches(Paths.get("f.t"))); + assertFalse(pathMatcher.matches(Paths.get("f"))); + + pathMatcher = fileSystem.getPathMatcher("glob:" + "*.{java,class}"); + assertTrue(pathMatcher.matches(Paths.get("f.java"))); + assertTrue(pathMatcher.matches(Paths.get("f.class"))); + assertFalse(pathMatcher.matches(Paths.get("f.clas"))); + assertFalse(pathMatcher.matches(Paths.get("f.t"))); + + pathMatcher = fileSystem.getPathMatcher("glob:" + "f.?"); + assertTrue(pathMatcher.matches(Paths.get("f.t"))); + assertFalse(pathMatcher.matches(Paths.get("f.tl"))); + assertFalse(pathMatcher.matches(Paths.get("f."))); + + pathMatcher = fileSystem.getPathMatcher("glob:" + "/home/*/*"); + assertTrue(pathMatcher.matches(Paths.get("/home/f/d"))); + assertTrue(pathMatcher.matches(Paths.get("/home/f/*"))); + assertTrue(pathMatcher.matches(Paths.get("/home/*/*"))); + assertFalse(pathMatcher.matches(Paths.get("/home/f"))); + assertFalse(pathMatcher.matches(Paths.get("/home/f/d/d"))); + + pathMatcher = fileSystem.getPathMatcher("glob:" + "/home/**"); + assertTrue(pathMatcher.matches(Paths.get("/home/f/d"))); + assertTrue(pathMatcher.matches(Paths.get("/home/f/*"))); + assertTrue(pathMatcher.matches(Paths.get("/home/*/*"))); + assertTrue(pathMatcher.matches(Paths.get("/home/f"))); + assertTrue(pathMatcher.matches(Paths.get("/home/f/d/d"))); + assertTrue(pathMatcher.matches(Paths.get("/home/f/d/d/d"))); + } + + @Test + public void test_getPathMatcher_regex() { + PathMatcher pathMatcher = fileSystem.getPathMatcher("regex:" + "(hello|hi)*[^a|b]?k.*"); + assertTrue(pathMatcher.matches(Paths.get("k"))); + assertTrue(pathMatcher.matches(Paths.get("ck"))); + assertFalse(pathMatcher.matches(Paths.get("ak"))); + assertTrue(pathMatcher.matches(Paths.get("kanything"))); + assertTrue(pathMatcher.matches(Paths.get("hellohik"))); + assertTrue(pathMatcher.matches(Paths.get("hellok"))); + assertTrue(pathMatcher.matches(Paths.get("hellohellohellok"))); + assertFalse(pathMatcher.matches(Paths.get("hellohellohellobk"))); + assertFalse(pathMatcher.matches(Paths.get("hello"))); + } + + @Test + public void test_getPathMatcher_unsupported() { + try { + fileSystem.getPathMatcher("unsupported:test"); + fail(); + } catch (UnsupportedOperationException expected) {} + } + + @Test + public void test_getUserPrincipalLookupService() { + assertNotNull(fileSystem.getUserPrincipalLookupService()); + } + + @Test + public void test_newWatchService() throws IOException { + assertNotNull(fileSystem.newWatchService()); + } +} \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/nio/file/LinuxFileSystemTestData.java b/luni/src/test/java/libcore/java/nio/file/LinuxFileSystemTestData.java new file mode 100644 index 000000000..0780727b6 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/LinuxFileSystemTestData.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.InvalidPathException; +import java.util.ArrayList; +import java.util.List; + +/** + * The class provides test cases to libcore.java.nio.file.PathsTest#test_get_URI, + * libcore.java.nio.file.PathsTest#test_get_String, + * libcore.java.nio.file.LinuxFileSystemTest#test_getPath + */ +class LinuxFileSystemTestData { + static List getPathInputOutputTestData() { + List inputOutputTestCases = new ArrayList<>(); + inputOutputTestCases.add(new TestData("d1", "d1")); + inputOutputTestCases.add(new TestData("", "")); + inputOutputTestCases.add(new TestData("/", "//")); + inputOutputTestCases.add(new TestData("d1/d2/d3", "d1//d2/d3")); + inputOutputTestCases.add(new TestData("d1/d2", "d1", "", "d2")); + inputOutputTestCases.add(new TestData("foo", "", "foo")); + + // If the name separator is "/" and getPath("/foo","bar","gus") is invoked, then the path + // string "/foo/bar/gus" is converted to a Path. + inputOutputTestCases.add(new TestData("/foo/bar/gus", "/foo", "bar", "gus")); + return inputOutputTestCases; + } + + static List getPathExceptionTestData() { + List exceptionTestCases = new ArrayList<>(); + exceptionTestCases.add(new TestData(InvalidPathException.class, "'\u0000'")); + exceptionTestCases.add(new TestData(NullPointerException.class, null)); + return exceptionTestCases; + } + + static List getPath_URI_InputOutputTestData() { + // As of today, there is only one installed provider - LinuxFileSystemProvider and + // only scheme supported by it is "file". + List inputOutputTestCases = new ArrayList<>(); + inputOutputTestCases.add(new TestData("/d1", "file:///d1")); + inputOutputTestCases.add(new TestData("/", "file:///")); + inputOutputTestCases.add(new TestData("/d1//d2/d3", "file:///d1//d2/d3")); + return inputOutputTestCases; + } + + static List getPath_URI_ExceptionTestData() { + List exceptionTestCases = new ArrayList<>(); + exceptionTestCases.add(new TestData(IllegalArgumentException.class, "d1")); + exceptionTestCases.add(new TestData(FileSystemNotFoundException.class, "scheme://d")); + exceptionTestCases.add(new TestData(NullPointerException.class, null)); + exceptionTestCases.add(new TestData(IllegalArgumentException.class, "file:///d#row=4")); + exceptionTestCases.add(new TestData(IllegalArgumentException.class, "file:///d?q=5")); + exceptionTestCases.add(new TestData(IllegalArgumentException.class, "file://d:5000")); + return exceptionTestCases; + } + + static class TestData { + public String output; + public String input; + public String[] inputArray; + public Class exceptionClass; + + TestData(String output, String input, String... inputArray) { + this.output = output; + this.input = input; + this.inputArray = inputArray; + } + + TestData(Class exceptionClass, String input, String... inputArray) { + this.exceptionClass = exceptionClass; + this.input = input; + this.inputArray = inputArray; + } + } +} \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/nio/file/LinuxPathTest.java b/luni/src/test/java/libcore/java/nio/file/LinuxPathTest.java new file mode 100644 index 000000000..e95f5bc9b --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/LinuxPathTest.java @@ -0,0 +1,591 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import com.sun.nio.file.ExtendedWatchEventModifier; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.ClosedWatchServiceException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.NoSuchFileException; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; +import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNull; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class LinuxPathTest { + + @Rule + public FilesSetup filesSetup = new FilesSetup(); + + /** + * CTS doesn't allow creating files in the test directory, however, Vogar allows creation of + * new files in the test directory. Therefore, for the tests which don't require write + * permission, dummyPath would serve the purpose, however, for the others, {@link + * FilesSetup#getTestDirPath()} should be used. + */ + private static final Path dummyPath = Paths.get("dummyPath"); + + @Test + public void test_getFileSystem() { + assertTrue(dummyPath.getFileSystem().provider() instanceof + sun.nio.fs.LinuxFileSystemProvider); + } + + @Test + public void test_isAbsolute() { + assertFalse(dummyPath.isAbsolute()); + Path absolutePath = dummyPath.toAbsolutePath(); + assertTrue(absolutePath.isAbsolute()); + } + + @Test + public void test_getRoot() { + assertEquals(Paths.get("/"), dummyPath.toAbsolutePath().getRoot()); + assertNull(dummyPath.getRoot()); + } + + @Test + public void test_getFileName() { + assertEquals(dummyPath, dummyPath.getFileName()); + assertEquals(dummyPath, dummyPath.toAbsolutePath().getFileName()); + assertNull(dummyPath.getRoot()); + assertEquals(Paths.get("data"), Paths.get("/data").getFileName()); + assertEquals(Paths.get("data"), Paths.get("/data/").getFileName()); + assertEquals(Paths.get(".."), Paths.get("/data/dir1/..").getFileName()); + } + + @Test + public void test_getParent() { + assertNull(dummyPath.getParent()); + assertEquals(Paths.get("rootDir"), Paths.get("rootDir/dir").getParent()); + } + + @Test + public void test_getNameCount() { + assertEquals(0, Paths.get("/").getNameCount()); + assertEquals(1, Paths.get("/dir").getNameCount()); + assertEquals(2, Paths.get("/dir/dir").getNameCount()); + assertEquals(2, Paths.get("/dir/..").getNameCount()); + } + + @Test + public void test_getName() { + assertEquals(Paths.get("t"), Paths.get("/t/t1/t2/t3").getName(0)); + assertEquals(Paths.get("t2"), Paths.get("/t/t1/t2/t3").getName(2)); + assertEquals(Paths.get("t3"), Paths.get("/t/t1/t2/t3").getName(3)); + + // Without root. + assertEquals(Paths.get("t3"), Paths.get("t/t1/t2/t3").getName(3)); + + // Invalid index. + try { + Paths.get("/t/t1/t2/t3").getName(4); + fail(); + } catch (IllegalArgumentException expected) {} + + // Negative index value. + try { + Paths.get("/t/t1/t2/t3").getName(-1); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test + public void test_subPath() { + assertEquals(Paths.get("t1/t2"), Paths.get("t1/t2/t3").subpath(0, 2)); + assertEquals(Paths.get("t2"), Paths.get("t1/t2/t3").subpath(1, 2)); + + try { + Paths.get("t1/t2/t3").subpath(1, 1); + fail(); + } catch (IllegalArgumentException expected) {} + + try { + assertEquals(Paths.get("t1/t1"), Paths.get("t1/t2/t3").subpath(1, 0)); + fail(); + } catch (IllegalArgumentException expected) {} + + try { + assertEquals(Paths.get("t1/t1"), Paths.get("t1/t2/t3").subpath(1, 5)); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test + public void test_startsWith$String() { + assertTrue(Paths.get("t1/t2").startsWith("t1")); + assertTrue(dummyPath.toAbsolutePath().startsWith("/")); + assertTrue(Paths.get("t1/t2/t3").startsWith("t1/t2")); + assertFalse(Paths.get("t1/t2").startsWith("t2")); + } + + @Test(expected = NullPointerException.class) + public void test_startsWith$String_NPE() { + filesSetup.getTestPath().startsWith((String) null); + } + + @Test + public void test_startsWith$Path() { + assertTrue(Paths.get("t1/t2").startsWith(Paths.get("t1"))); + assertTrue(dummyPath.toAbsolutePath().startsWith(Paths.get("/"))); + assertTrue(Paths.get("t1/t2/t3").startsWith(Paths.get("t1/t2"))); + assertFalse(Paths.get("t1/t2").startsWith(Paths.get("t2"))); + } + + @Test(expected = NullPointerException.class) + public void test_startsWith$Path_NPE() { + filesSetup.getTestPath().startsWith((Path) null); + } + + @Test + public void test_endsWith$Path() { + assertTrue(Paths.get("t1/t2").endsWith(Paths.get("t2"))); + assertTrue(Paths.get("t1/t2/t3").endsWith(Paths.get("t2/t3"))); + assertFalse(Paths.get("t1/t2").endsWith(Paths.get("t1"))); + assertTrue(Paths.get("/").endsWith(Paths.get("/"))); + assertFalse(Paths.get("/data/").endsWith(Paths.get("/"))); + } + + @Test(expected = NullPointerException.class) + public void test_endsWith$Path_NPE() { + filesSetup.getTestPath().endsWith((Path)null); + } + + @Test + public void test_endsWith$String() { + assertTrue(Paths.get("t1/t2").endsWith("t2")); + assertTrue(Paths.get("t1/t2/t3").endsWith("t2/t3")); + assertFalse(Paths.get("t1/t2").endsWith("t1")); + assertTrue(Paths.get("/").endsWith("/")); + assertFalse(Paths.get("/data/").endsWith("/")); + } + + @Test(expected = NullPointerException.class) + public void test_endsWith$String_NPE() { + filesSetup.getTestPath().endsWith((String)null); + } + + @Test + public void test_normalize() { + assertEquals(Paths.get("t2/t3"), Paths.get("t1/../t2/t3").normalize()); + assertEquals(Paths.get("../t2/t3"), Paths.get("t1/../../t2/t3").normalize()); + assertEquals(Paths.get("t1/t2/t3"), Paths.get("t1/./t2/t3").normalize()); + assertEquals(Paths.get("t1/t2/t3"), Paths.get("t1/././t2/t3").normalize()); + assertEquals(Paths.get("t1/t2/t3"), Paths.get("t1/././t2/t3").normalize()); + assertEquals(Paths.get("t1"), Paths.get("t1/")); + } + + @Test + public void test_resolve$Path() { + Path p = Paths.get("p"); + Path p1 = Paths.get("p1"); + Path p1p = Paths.get("p1/p"); + assertEquals(p1p, p1.resolve(p)); + assertEquals(p.toAbsolutePath(), p1.resolve(p.toAbsolutePath())); + assertEquals(p1p.toAbsolutePath(), p1.toAbsolutePath().resolve(p)); + } + + @Test(expected = NullPointerException.class) + public void test_resolve$Path_NPE() { + dummyPath.resolve((Path)null); + } + + @Test + public void test_resolve$String() { + Path p = Paths.get("p"); + Path p1 = Paths.get("p1"); + Path p1p = Paths.get("p1/p"); + assertEquals(p1p, p1.resolve("p")); + assertEquals(p1p.toAbsolutePath(), p1.toAbsolutePath().resolve("p")); + } + + @Test(expected = NullPointerException.class) + public void test_resolve$String_NPE() { + dummyPath.resolve((String)null); + } + + @Test + public void test_resolveSibling$Path() { + Path c2 = Paths.get("c2"); + Path parent_c1 = Paths.get("parent/c1"); + Path parent_c2 = Paths.get("parent/c2"); + assertEquals(parent_c2, parent_c1.resolveSibling(c2)); + assertEquals(c2.toAbsolutePath(), parent_c1.resolveSibling(c2.toAbsolutePath())); + assertEquals(parent_c2.toAbsolutePath(), parent_c1.toAbsolutePath().resolveSibling(c2)); + } + + @Test(expected = NullPointerException.class) + public void test_resolveSibling$String_Path() { + dummyPath.resolveSibling((Path) null); + } + + @Test + public void test_resolveSibling$String() { + Path c2 = Paths.get("c2"); + Path parent_c1 = Paths.get("parent/c1"); + Path parent_c2 = Paths.get("parent/c2"); + assertEquals(parent_c2, parent_c1.resolveSibling(c2.toString())); + assertEquals(c2.toAbsolutePath(), parent_c1.resolveSibling(c2.toAbsolutePath().toString())); + assertEquals(parent_c2.toAbsolutePath(), parent_c1.toAbsolutePath() + .resolveSibling(c2.toString())); + } + + @Test(expected = NullPointerException.class) + public void test_resolveSibling$String_NPE() { + dummyPath.resolveSibling((String)null); + } + + @Test + public void test_relativize() { + Path p1 = Paths.get("t1/t2/t3"); + Path p2 = Paths.get("t1/t2"); + assertEquals(Paths.get(".."), p1.relativize(p2)); + assertEquals(Paths.get(".."), p1.toAbsolutePath().relativize(p2.toAbsolutePath())); + assertEquals(Paths.get("t3"), p2.relativize(p1)); + + // Can't be relativized as either of the paths are relative and the other is not. + try { + p1.relativize(p2.toAbsolutePath()); + fail(); + } catch (IllegalArgumentException expected) {} + + try { + p1.toAbsolutePath().relativize(p2); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test(expected = NullPointerException.class) + public void test_relativize_NPE() { + dummyPath.relativize(null); + } + + @Test + public void test_toURI() throws URISyntaxException { + assertEquals(new URI("file://" + dummyPath.toAbsolutePath().toString()), dummyPath.toUri()); + assertEquals(new URI("file:///"), Paths.get("/").toUri()); + assertEquals(new URI("file:///dir/.."), Paths.get(("/dir/..")).toUri()); + assertEquals(new URI("file:///../"), Paths.get(("/..")).toUri()); + assertEquals(new URI("file:///dir/.."), Paths.get(("/dir/..")).toUri()); + assertEquals(new URI("file:///./"), Paths.get(("/.")).toUri()); + assertEquals(new URI("file:///dir/."), Paths.get(("/dir/.")).toUri()); + // For unicode characters. + assertEquals(new URI("file:///%E0%A4%B0%E0%A4%BE%E0%A4%B9."), Paths.get(("/राह.")).toUri()); + } + + @Test + public void test_toAbsolutePath() { + assertFalse(dummyPath.isAbsolute()); + assertTrue(dummyPath.toAbsolutePath().isAbsolute()); + } + + @Test + public void test_toRealPath() throws IOException { + // When file doesn't exist. + try { + dummyPath.toRealPath(); + fail(); + } catch (NoSuchFileException expected) {} + Files.createFile(filesSetup.getTestPath()); + Path realPath = filesSetup.getTestPath().toRealPath(); + assertTrue(Files.isSameFile(filesSetup.getTestPath().toAbsolutePath(), realPath)); + assertTrue(realPath.isAbsolute()); + assertFalse(Files.isSymbolicLink(realPath)); + + Path dir = Paths.get(filesSetup.getTestDir(), "dir1/dir2"); + Path file = Paths.get(filesSetup.getTestDir(), "dir1/dir2/../../file"); + Files.createDirectories(dir); + Files.createFile(file); + realPath = file.toRealPath(); + assertTrue(Files.isSameFile(file.toAbsolutePath(), realPath)); + assertTrue(realPath.isAbsolute()); + assertFalse(Files.isSymbolicLink(realPath)); + + // Sym links. + Path symLink = Paths.get(filesSetup.getTestDir(), "symlink"); + Files.createSymbolicLink(symLink, filesSetup.getTestPath().toAbsolutePath()); + realPath = symLink.toRealPath(); + assertTrue(Files.isSameFile(symLink, realPath)); + assertTrue(realPath.isAbsolute()); + assertFalse(Files.isSymbolicLink(realPath)); + + realPath = symLink.toRealPath(LinkOption.NOFOLLOW_LINKS); + assertTrue(Files.isSameFile(symLink, realPath)); + assertTrue(realPath.isAbsolute()); + assertTrue(Files.isSymbolicLink(realPath)); + } + + @Test + public void test_toFile() { + File file = dummyPath.toFile(); + assertEquals(dummyPath.toAbsolutePath().toString(), file.getAbsolutePath()); + } + + @Test + public void test_register$WatchService$WatchEvent_Kind() throws IOException, + InterruptedException { + WatchService watchService = FileSystems.getDefault().newWatchService(); + WatchEvent.Kind[] events = {ENTRY_CREATE, ENTRY_DELETE}; + Path file = Paths.get(filesSetup.getTestDir(), "directory/file"); + assertFalse(Files.exists(file)); + Path directory = Paths.get(filesSetup.getTestDir(), "directory"); + Files.createDirectories(directory); + WatchKey key = directory.register(watchService, events); + + // Creating, modifying and deleting the file. + Files.createFile(file); + assertTrue(Files.exists(file)); + // EVENT_MODIFY should not be logged. + Files.newOutputStream(file).write("hello".getBytes()); + Files.delete(file); + assertFalse(Files.exists(file)); + + assertTrue(key.isValid()); + assertEquals(directory, key.watchable()); + List> eventList = new ArrayList<>(); + + // Wait for the events to be recorded by WatchService. + while(true) { + eventList.addAll(key.pollEvents()); + if (eventList.size() == 2) break; + Thread.sleep(1000); + } + // Wait for the events to be recorded by watchService. + assertEquals(2, eventList.size()); + assertEquals(ENTRY_CREATE, eventList.get(0).kind()); + assertEquals(ENTRY_DELETE, eventList.get(1).kind()); + } + + @Test + public void test_register$WatchService$WatchEvent_Kind_NPE() throws IOException, + InterruptedException { + WatchService watchService = FileSystems.getDefault().newWatchService(); + WatchEvent.Kind[] events = {ENTRY_CREATE, ENTRY_DELETE}; + Path directory = Paths.get(filesSetup.getTestDir(), "directory"); + Files.createDirectories(directory); + try { + directory.register(null, events); + fail(); + } catch (NullPointerException expected) {} + + try { + directory.register(watchService, (WatchEvent.Kind) null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_register$WatchService$WatchEvent_Kind_Exception() throws IOException { + WatchService watchService = FileSystems.getDefault().newWatchService(); + Path directory = Paths.get(filesSetup.getTestDir(), "directory1"); + Files.createFile(directory); + + // When file is not a directory. + try { + directory.register(watchService, ENTRY_CREATE); + fail(); + } catch (NotDirectoryException expected) {} + + // When the events are not supported. + Files.deleteIfExists(directory); + Files.createDirectories(directory); + WatchEvent.Kind[] events = {new NonStandardEvent<>()}; + try { + directory.register(watchService, events); + fail(); + } catch (UnsupportedOperationException expected) {} + + // When the watch service is closed. + watchService.close(); + try { + directory.register(watchService, ENTRY_CREATE); + fail(); + } catch (ClosedWatchServiceException expected) {} + } + + @Test + public void test_register$WatchService$WatchEvent_Kind_Exception_NPE() throws IOException { + WatchService watchService = FileSystems.getDefault().newWatchService(); + Path directory = Paths.get(filesSetup.getTestDir(), "directory1"); + Files.createDirectories(directory); + + // When file is not a directory. + try { + directory.register(null, ENTRY_CREATE); + fail(); + } catch (NullPointerException expected) {} + + try { + directory.register(watchService, null); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_register$WatchService$WatchEvent_Kind$WatchEvent_Modifier() throws IOException + { + WatchService watchService = FileSystems.getDefault().newWatchService(); + WatchEvent.Kind[] events = {ENTRY_CREATE}; + Path dirRoot = Paths.get(filesSetup.getTestDir(), "dir"); + Files.createDirectories(dirRoot); + try { + WatchKey key = dirRoot.register(watchService, events, + ExtendedWatchEventModifier.FILE_TREE); + fail(); + } catch (UnsupportedOperationException expected) { + assertTrue(expected.getMessage().contains("Modifier not supported")); + } + } + + @Test + public void test_register$WatchService$WatchEvent_Kind$WatchEvent_Modifier_NPE() + throws IOException { + WatchService watchService = FileSystems.getDefault().newWatchService(); + WatchEvent.Kind[] events = {ENTRY_CREATE}; + Path dirRoot = Paths.get(filesSetup.getTestDir(), "dir"); + Files.createDirectories(dirRoot); + try { + WatchKey key = dirRoot.register(null, events, + ExtendedWatchEventModifier.FILE_TREE); + fail(); + } catch (NullPointerException expected) {} + + try { + WatchKey key = dirRoot.register(watchService, null, + ExtendedWatchEventModifier.FILE_TREE); + fail(); + } catch (NullPointerException expected) {} + } + + @Test + public void test_iterator() { + Path p = Paths.get("f1/f2/f3"); + Iterator pathIterator = p.iterator(); + assertEquals(Paths.get("f1"), pathIterator.next()); + assertEquals(Paths.get("f2"), pathIterator.next()); + assertEquals(Paths.get("f3"), pathIterator.next()); + assertFalse(pathIterator.hasNext()); + } + + @Test + public void test_iterator_hasRoot() { + Path p = Paths.get("/f1/f2/f3"); + Iterator pathIterator = p.iterator(); + assertEquals(Paths.get("f1"), pathIterator.next()); + assertEquals(Paths.get("f2"), pathIterator.next()); + assertEquals(Paths.get("f3"), pathIterator.next()); + assertFalse(pathIterator.hasNext()); + } + + @Test + public void test_compareTo() { + Path p1 = Paths.get("d/a"); + Path p2 = Paths.get("d/b"); + assertTrue(p1.compareTo(p2) < 0); + assertTrue(p2.compareTo(p1) > 0); + assertTrue(p1.compareTo(p1) == 0); + } + + @Test(expected = NullPointerException.class) + public void test_compareTo_NPE() { + filesSetup.getTestPath().compareTo(null); + } + + @Test + public void test_equals() { + Path p1 = Paths.get("a/b"); + Path p2 = Paths.get("a/../a/b"); + Path p3 = p1.toAbsolutePath(); + assertFalse(p1.equals(p2)); + assertTrue(p1.equals(p1)); + assertFalse(p1.equals(p3)); + } + + @Test + public void test_equals_NPE() { + // Should not throw NPE. + filesSetup.getTestPath().equals(null); + } + + @Test + public void test_hashCode() { + Path p1 = Paths.get("f1/f2/f3"); + assertEquals(-642657684, p1.hashCode()); + + // With root component. + Path p2 = Paths.get("/f1/f2/f3"); + assertEquals(306328475, p2.hashCode()); + } + + @Test + public void test_toString() { + Path p = Paths.get("f1/f2/f3"); + assertEquals("f1/f2/f3", p.toString()); + + p = Paths.get(""); + assertEquals("", p.toString()); + + p = Paths.get(".."); + assertEquals("..", p.toString()); + + p = Paths.get("."); + assertEquals(".", p.toString()); + + p = Paths.get("dir/"); + assertEquals("dir", p.toString()); + + p = Paths.get("/dir"); + assertEquals("/dir", p.toString()); + } + + private static class NonStandardEvent implements WatchEvent.Kind { + + @Override + public String name() { + return null; + } + + @Override + public Class type() { + return null; + } + } +} \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/nio/file/NoSuchFileExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/NoSuchFileExceptionTest.java new file mode 100644 index 000000000..170879c37 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/NoSuchFileExceptionTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.FileSystemException; +import java.nio.file.NoSuchFileException; +import libcore.util.SerializationTester; + +public class NoSuchFileExceptionTest extends TestCase { + public void test_constructor$String() { + NoSuchFileException exception = new NoSuchFileException("file"); + assertEquals("file", exception.getFile()); + assertNull(exception.getOtherFile()); + assertNull(exception.getReason()); + + assertTrue(exception instanceof FileSystemException); + } + + public void test_constructor$String$String$String() { + NoSuchFileException exception = new NoSuchFileException("file", "otherFile", "reason"); + assertEquals("file", exception.getFile()); + assertEquals("otherFile", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } + + public void test_serialization() throws IOException, ClassNotFoundException { + String hex = "aced0005737200216a6176612e6e696f2e66696c652e4e6f5375636846696c654578636570746" + + "96f6eecb4b0fef4cd7a85020000787200216a6176612e6e696f2e66696c652e46696c65537973746" + + "56d457863657074696f6ed598f27876d360fc0200024c000466696c657400124c6a6176612f6c616" + + "e672f537472696e673b4c00056f7468657271007e0002787200136a6176612e696f2e494f4578636" + + "57074696f6e6c8073646525f0ab020000787200136a6176612e6c616e672e457863657074696f6ed" + + "0fd1f3e1a3b1cc4020000787200136a6176612e6c616e672e5468726f7761626c65d5c635273977b" + + "8cb0300044c000563617573657400154c6a6176612f6c616e672f5468726f7761626c653b4c000d6" + + "4657461696c4d65737361676571007e00025b000a737461636b547261636574001e5b4c6a6176612" + + "f6c616e672f537461636b5472616365456c656d656e743b4c0014737570707265737365644578636" + + "57074696f6e737400104c6a6176612f7574696c2f4c6973743b787071007e0009740006726561736" + + "f6e7572001e5b4c6a6176612e6c616e672e537461636b5472616365456c656d656e743b02462a3c3" + + "cfd22390200007870000000097372001b6a6176612e6c616e672e537461636b5472616365456c656" + + "d656e746109c59a2636dd8502000449000a6c696e654e756d6265724c000e6465636c6172696e674" + + "36c61737371007e00024c000866696c654e616d6571007e00024c000a6d6574686f644e616d65710" + + "07e000278700000002374002d6c6962636f72652e6a6176612e6e696f2e66696c652e4e6f5375636" + + "846696c65457863657074696f6e5465737474001c4e6f5375636846696c65457863657074696f6e5" + + "46573742e6a617661740025746573745f636f6e7374727563746f7224537472696e6724537472696" + + "e6724537472696e677371007e000dfffffffe7400186a6176612e6c616e672e7265666c6563742e4" + + "d6574686f6474000b4d6574686f642e6a617661740006696e766f6b657371007e000d000000f9740" + + "028766f6761722e7461726765742e6a756e69742e4a756e69743324566f6761724a556e697454657" + + "37474000b4a756e6974332e6a61766174000372756e7371007e000d00000063740020766f6761722" + + "e7461726765742e6a756e69742e4a556e697452756e6e657224317400104a556e697452756e6e657" + + "22e6a61766174000463616c6c7371007e000d0000005c740020766f6761722e7461726765742e6a7" + + "56e69742e4a556e697452756e6e657224317400104a556e697452756e6e65722e6a6176617400046" + + "3616c6c7371007e000d000000ed74001f6a6176612e7574696c2e636f6e63757272656e742e46757" + + "47572655461736b74000f4675747572655461736b2e6a61766174000372756e7371007e000d00000" + + "46d7400276a6176612e7574696c2e636f6e63757272656e742e546872656164506f6f6c457865637" + + "5746f72740017546872656164506f6f6c4578656375746f722e6a61766174000972756e576f726b6" + + "5727371007e000d0000025f74002e6a6176612e7574696c2e636f6e63757272656e742e546872656" + + "164506f6f6c4578656375746f7224576f726b6572740017546872656164506f6f6c4578656375746" + + "f722e6a61766174000372756e7371007e000d000002f97400106a6176612e6c616e672e546872656" + + "16474000b5468726561642e6a61766174000372756e7372001f6a6176612e7574696c2e436f6c6c6" + + "56374696f6e7324456d7074794c6973747ab817b43ca79ede02000078707874000466696c6574000" + + "96f7468657246696c65"; + NoSuchFileException exception = (NoSuchFileException) SerializationTester + .deserializeHex(hex); + + String hex1 = SerializationTester.serializeHex(exception).toString(); + assertEquals(hex, hex1); + assertEquals("file", exception.getFile()); + assertEquals("otherFile", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/NotDirectoryExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/NotDirectoryExceptionTest.java new file mode 100644 index 000000000..6719c0cb9 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/NotDirectoryExceptionTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.FileSystemException; +import java.nio.file.NotDirectoryException; +import libcore.util.SerializationTester; + +public class NotDirectoryExceptionTest extends TestCase { + public void test_constructor$String() { + NotDirectoryException exception = new NotDirectoryException("file"); + assertEquals("file", exception.getFile()); + assertNull(exception.getOtherFile()); + assertNull(exception.getReason()); + + assertTrue(exception instanceof FileSystemException); + } + + public void test_serialization() throws IOException, ClassNotFoundException { + String hex = "aced0005737200236a6176612e6e696f2e66696c652e4e6f744469726563746f7279457863657" + + "074696f6e82f0df36f87ce379020000787200216a6176612e6e696f2e66696c652e46696c6553797" + + "374656d457863657074696f6ed598f27876d360fc0200024c000466696c657400124c6a6176612f6" + + "c616e672f537472696e673b4c00056f7468657271007e0002787200136a6176612e696f2e494f457" + + "863657074696f6e6c8073646525f0ab020000787200136a6176612e6c616e672e457863657074696" + + "f6ed0fd1f3e1a3b1cc4020000787200136a6176612e6c616e672e5468726f7761626c65d5c635273" + + "977b8cb0300044c000563617573657400154c6a6176612f6c616e672f5468726f7761626c653b4c0" + + "00d64657461696c4d65737361676571007e00025b000a737461636b547261636574001e5b4c6a617" + + "6612f6c616e672f537461636b5472616365456c656d656e743b4c001473757070726573736564457" + + "863657074696f6e737400104c6a6176612f7574696c2f4c6973743b787071007e0009707572001e5" + + "b4c6a6176612e6c616e672e537461636b5472616365456c656d656e743b02462a3c3cfd223902000" + + "07870000000097372001b6a6176612e6c616e672e537461636b5472616365456c656d656e746109c" + + "59a2636dd8502000449000a6c696e654e756d6265724c000e6465636c6172696e67436c617373710" + + "07e00024c000866696c654e616d6571007e00024c000a6d6574686f644e616d6571007e000278700" + + "000002574002f6c6962636f72652e6a6176612e6e696f2e66696c652e4e6f744469726563746f727" + + "9457863657074696f6e5465737474001e4e6f744469726563746f7279457863657074696f6e54657" + + "3742e6a617661740012746573745f73657269616c697a6174696f6e7371007e000cfffffffe74001" + + "86a6176612e6c616e672e7265666c6563742e4d6574686f6474000b4d6574686f642e6a617661740" + + "006696e766f6b657371007e000c000000f9740028766f6761722e7461726765742e6a756e69742e4" + + "a756e69743324566f6761724a556e69745465737474000b4a756e6974332e6a61766174000372756" + + "e7371007e000c00000063740020766f6761722e7461726765742e6a756e69742e4a556e697452756" + + "e6e657224317400104a556e697452756e6e65722e6a61766174000463616c6c7371007e000c00000" + + "05c740020766f6761722e7461726765742e6a756e69742e4a556e697452756e6e657224317400104" + + "a556e697452756e6e65722e6a61766174000463616c6c7371007e000c000000ed74001f6a6176612" + + "e7574696c2e636f6e63757272656e742e4675747572655461736b74000f4675747572655461736b2" + + "e6a61766174000372756e7371007e000c0000046d7400276a6176612e7574696c2e636f6e6375727" + + "2656e742e546872656164506f6f6c4578656375746f72740017546872656164506f6f6c457865637" + + "5746f722e6a61766174000972756e576f726b65727371007e000c0000025f74002e6a6176612e757" + + "4696c2e636f6e63757272656e742e546872656164506f6f6c4578656375746f7224576f726b65727" + + "40017546872656164506f6f6c4578656375746f722e6a61766174000372756e7371007e000c00000" + + "2f97400106a6176612e6c616e672e54687265616474000b5468726561642e6a61766174000372756" + + "e7372001f6a6176612e7574696c2e436f6c6c656374696f6e7324456d7074794c6973747ab817b43" + + "ca79ede02000078707874000466696c6570"; + NotDirectoryException exception = (NotDirectoryException) SerializationTester + .deserializeHex(hex); + + String hex1 = SerializationTester.serializeHex(exception).toString(); + assertEquals(hex, hex1); + assertEquals("file", exception.getFile()); + assertNull(exception.getOtherFile()); + assertNull(exception.getReason()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/NotLinkExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/NotLinkExceptionTest.java new file mode 100644 index 000000000..0a058ec8d --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/NotLinkExceptionTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.FileSystemException; +import java.nio.file.NotLinkException; +import libcore.util.SerializationTester; + +public class NotLinkExceptionTest extends TestCase { + public void test_constructor$String() { + NotLinkException exception = new NotLinkException("file"); + assertEquals("file", exception.getFile()); + assertNull(exception.getOtherFile()); + assertNull(exception.getReason()); + + assertTrue(exception instanceof FileSystemException); + } + + public void test_constructor$String$String$String() { + NotLinkException exception = new NotLinkException("file", "otherFile", "reason"); + assertEquals("file", exception.getFile()); + assertEquals("otherFile", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } + + public void test_serialization() throws IOException, ClassNotFoundException { + String hex = "aced00057372001e6a6176612e6e696f2e66696c652e4e6f744c696e6b457863657074696f6ef" + + "a9b37cb53a0387b020000787200216a6176612e6e696f2e66696c652e46696c6553797374656d457" + + "863657074696f6ed598f27876d360fc0200024c000466696c657400124c6a6176612f6c616e672f5" + + "37472696e673b4c00056f7468657271007e0002787200136a6176612e696f2e494f4578636570746" + + "96f6e6c8073646525f0ab020000787200136a6176612e6c616e672e457863657074696f6ed0fd1f3" + + "e1a3b1cc4020000787200136a6176612e6c616e672e5468726f7761626c65d5c635273977b8cb030" + + "0044c000563617573657400154c6a6176612f6c616e672f5468726f7761626c653b4c000d6465746" + + "1696c4d65737361676571007e00025b000a737461636b547261636574001e5b4c6a6176612f6c616" + + "e672f537461636b5472616365456c656d656e743b4c0014737570707265737365644578636570746" + + "96f6e737400104c6a6176612f7574696c2f4c6973743b787071007e0009740006726561736f6e757" + + "2001e5b4c6a6176612e6c616e672e537461636b5472616365456c656d656e743b02462a3c3cfd223" + + "90200007870000000097372001b6a6176612e6c616e672e537461636b5472616365456c656d656e7" + + "46109c59a2636dd8502000449000a6c696e654e756d6265724c000e6465636c6172696e67436c617" + + "37371007e00024c000866696c654e616d6571007e00024c000a6d6574686f644e616d6571007e000" + + "278700000002c74002a6c6962636f72652e6a6176612e6e696f2e66696c652e4e6f744c696e6b457" + + "863657074696f6e546573747400194e6f744c696e6b457863657074696f6e546573742e6a6176617" + + "40012746573745f73657269616c697a6174696f6e7371007e000dfffffffe7400186a6176612e6c6" + + "16e672e7265666c6563742e4d6574686f6474000b4d6574686f642e6a617661740006696e766f6b6" + + "57371007e000d000000f9740028766f6761722e7461726765742e6a756e69742e4a756e697433245" + + "66f6761724a556e69745465737474000b4a756e6974332e6a61766174000372756e7371007e000d0" + + "0000063740020766f6761722e7461726765742e6a756e69742e4a556e697452756e6e65722431740" + + "0104a556e697452756e6e65722e6a61766174000463616c6c7371007e000d0000005c740020766f6" + + "761722e7461726765742e6a756e69742e4a556e697452756e6e657224317400104a556e697452756" + + "e6e65722e6a61766174000463616c6c7371007e000d000000ed74001f6a6176612e7574696c2e636" + + "f6e63757272656e742e4675747572655461736b74000f4675747572655461736b2e6a61766174000" + + "372756e7371007e000d0000046d7400276a6176612e7574696c2e636f6e63757272656e742e54687" + + "2656164506f6f6c4578656375746f72740017546872656164506f6f6c4578656375746f722e6a617" + + "66174000972756e576f726b65727371007e000d0000025f74002e6a6176612e7574696c2e636f6e6" + + "3757272656e742e546872656164506f6f6c4578656375746f7224576f726b6572740017546872656" + + "164506f6f6c4578656375746f722e6a61766174000372756e7371007e000d000002f97400106a617" + + "6612e6c616e672e54687265616474000b5468726561642e6a61766174000372756e7372001f6a617" + + "6612e7574696c2e436f6c6c656374696f6e7324456d7074794c6973747ab817b43ca79ede0200007" + + "8707874000466696c657400096f7468657246696c65"; + NotLinkException exception = (NotLinkException) SerializationTester.deserializeHex(hex); + + String hex1 = SerializationTester.serializeHex(exception).toString(); + assertEquals(hex, hex1); + assertEquals("file", exception.getFile()); + assertEquals("otherFile", exception.getOtherFile()); + assertEquals("reason", exception.getReason()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/PathsTest.java b/luni/src/test/java/libcore/java/nio/file/PathsTest.java new file mode 100644 index 000000000..881d5955c --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/PathsTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.nio.file; + + +import org.junit.Test; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.util.List; + +import static libcore.java.nio.file.LinuxFileSystemTestData.*; +import static libcore.java.nio.file.LinuxFileSystemTestData.getPathInputOutputTestData; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + + +public class PathsTest { + + @Test + public void test_get_String() { + List inputOutputTestCases = getPathInputOutputTestData(); + for (TestData inputOutputTestCase : inputOutputTestCases) { + assertEquals(inputOutputTestCase.output, Paths.get(inputOutputTestCase.input, + inputOutputTestCase.inputArray).toString()); + } + + List exceptionTestCases = getPathExceptionTestData(); + for (TestData exceptionTestCase : exceptionTestCases) { + try { + Paths.get(exceptionTestCase.input, exceptionTestCase.inputArray); + fail(); + } catch (Exception expected) { + assertEquals(exceptionTestCase.exceptionClass, expected.getClass()); + } + } + } + + @Test + public void test_get_URI() throws URISyntaxException { + List inputOutputTestCases = getPath_URI_InputOutputTestData(); + for (TestData inputOutputTestCase : inputOutputTestCases) { + assertEquals(inputOutputTestCase.output, Paths.get(new URI(inputOutputTestCase.input)). + toString()); + } + + List exceptionTestCases = getPath_URI_ExceptionTestData(); + for (TestData exceptionTestCase : exceptionTestCases) { + try { + System.out.println(exceptionTestCase.input); + Paths.get(new URI(exceptionTestCase.input)); + fail(); + } catch (Exception expected) { + assertEquals(exceptionTestCase.exceptionClass, expected.getClass()); + } + } + } +} \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/nio/file/ProviderMismatchExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/ProviderMismatchExceptionTest.java new file mode 100644 index 000000000..55640eaad --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/ProviderMismatchExceptionTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.nio.file.ProviderMismatchException; + +public class ProviderMismatchExceptionTest extends TestCase { + public void test_constructor$String() { + String testString = "testString"; + ProviderMismatchException exception = new ProviderMismatchException(testString); + assertEquals(testString, exception.getMessage()); + } + + public void test_constructor() { + ProviderMismatchException exception = new ProviderMismatchException(); + assertEquals(null, exception.getMessage()); + assertTrue(exception instanceof IllegalArgumentException); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/ProviderNotFoundExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/ProviderNotFoundExceptionTest.java new file mode 100644 index 000000000..277db4f14 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/ProviderNotFoundExceptionTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.nio.file.ProviderNotFoundException; + +public class ProviderNotFoundExceptionTest extends TestCase { + + public void test_constructor$String() { + String message = "message"; + ProviderNotFoundException exception = new ProviderNotFoundException(message); + assertEquals(message, exception.getMessage()); + + message = null; + exception = new ProviderNotFoundException(message); + assertEquals(message, exception.getMessage()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/SimpleFileVisitorTest.java b/luni/src/test/java/libcore/java/nio/file/SimpleFileVisitorTest.java new file mode 100644 index 000000000..f712cbfbb --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/SimpleFileVisitorTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +import static org.mockito.Mockito.mock; + +public class SimpleFileVisitorTest extends TestCase { + + public void test_preVisitDirectory() throws IOException { + Path stubPath = mock(Path.class); + BasicFileAttributes stubAttributes = mock(BasicFileAttributes.class); + SimpleFileVisitor fileVisitor = new TestSimpleFileVisitor(); + + assertEquals(FileVisitResult.CONTINUE, fileVisitor.preVisitDirectory(stubPath, + stubAttributes)); + + try { + fileVisitor.preVisitDirectory(null, stubAttributes); + fail(); + } catch (NullPointerException expected) {} + + try { + fileVisitor.preVisitDirectory(stubPath, null); + fail(); + } catch (NullPointerException expected) {} + } + + public void test_postVisitDirectory() throws IOException { + Path stubPath = mock(Path.class); + IOException ioException = new IOException(); + SimpleFileVisitor fileVisitor = new TestSimpleFileVisitor(); + + assertEquals(FileVisitResult.CONTINUE, fileVisitor.postVisitDirectory(stubPath, null)); + + try { + fileVisitor.postVisitDirectory(null, ioException); + fail(); + } catch (NullPointerException expected) {} + + try { + fileVisitor.postVisitDirectory(stubPath, ioException); + fail(); + } catch (IOException actual) { + assertSame(ioException, actual); + } + } + + public void test_visitFile() throws IOException { + Path stubPath = mock(Path.class); + BasicFileAttributes stubAttributes = mock(BasicFileAttributes.class); + SimpleFileVisitor fileVisitor = new TestSimpleFileVisitor(); + + assertEquals(FileVisitResult.CONTINUE, fileVisitor.visitFile(stubPath, stubAttributes)); + + try { + fileVisitor.visitFile(null, stubAttributes); + fail(); + } catch (NullPointerException expected) {} + + try { + fileVisitor.visitFile(stubPath, null); + fail(); + } catch (NullPointerException expected) {} + } + + public void test_visitFileFailed() throws IOException { + Path stubPath = mock(Path.class); + IOException ioException = new IOException(); + SimpleFileVisitor fileVisitor = new TestSimpleFileVisitor(); + + try { + assertEquals(FileVisitResult.CONTINUE, fileVisitor.visitFileFailed(stubPath, null)); + fail(); + } catch (NullPointerException expected) { + } + + try { + assertEquals(FileVisitResult.CONTINUE, fileVisitor.visitFileFailed(null, + ioException)); + fail(); + } catch (NullPointerException expected) { + } + + try { + assertEquals(FileVisitResult.CONTINUE, fileVisitor.visitFileFailed(stubPath, + ioException)); + fail(); + } catch (IOException actual) { + assertSame(ioException, actual); + } + } + + /** + * SimpleFileVisitor only has a protected constructor so we use a basic subclass for tests. + */ + private static class TestSimpleFileVisitor extends SimpleFileVisitor { + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java b/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java new file mode 100644 index 000000000..c53f5315d --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2017 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 + */ +package libcore.java.nio.file; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; + +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertNull; +import static junit.framework.TestCase.assertTrue; +import static junit.framework.TestCase.fail; + +import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; +import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; +import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; + +@RunWith(JUnit4.class) +public class WatchServiceTest { + private static final WatchEvent.Kind[] ALL_EVENTS_KINDS = + {ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY}; + + @Rule + public final FilesSetup filesSetup = new FilesSetup(); + + static class WatchEventResult { + final WatchEvent.Kind expectedKind; + final int expectedCount; + final boolean testCount; + + public WatchEventResult(WatchEvent.Kind expectedKind) { + this.expectedKind = expectedKind; + this.expectedCount = 0; + this.testCount = false; + } + + public WatchEventResult(WatchEvent.Kind expectedKind, + int expectedCount) { + this.expectedKind = expectedKind; + this.expectedCount = expectedCount; + this.testCount = true; + } + } + + static public void assertWatchServiceEvent(WatchService watchService, + WatchKey expectedWatchKey, + List expectedEvents, + boolean expectedResetResult) throws InterruptedException { + Iterator expectedEventsIterator = expectedEvents.iterator(); + + while (expectedEventsIterator.hasNext()) { + WatchKey watchKey = watchService.poll(2, TimeUnit.SECONDS); + assertEquals(expectedWatchKey, watchKey); + + for (WatchEvent event : watchKey.pollEvents()) { + WatchEventResult expectedEventResult = expectedEventsIterator.next(); + assertNotNull(expectedEventResult); + + assertEquals(expectedEventResult.expectedKind, event.kind()); + if (expectedEventResult.testCount) { + assertEquals(expectedEventResult.expectedCount, event.count()); + } + } + + assertEquals(expectedResetResult, watchKey.reset()); + } + } + + @Test + public void test_singleFile() throws Exception { + WatchService watchService = FileSystems.getDefault().newWatchService(); + Path file = Paths.get(filesSetup.getTestDir(), "directory/file"); + Path directory = Paths.get(filesSetup.getTestDir(), "directory"); + assertFalse(Files.exists(file)); + Files.createDirectories(directory); + WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS); + + // emit EVENT_CREATE + Files.createFile(file); + assertWatchServiceEvent(watchService, directoryKey1, + Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1)), true); + assertNull(watchService.poll()); + + // emit EVENT_MODIFY + Files.write(file, "hello1".getBytes()); + assertWatchServiceEvent(watchService, directoryKey1, + Arrays.asList(new WatchEventResult(ENTRY_MODIFY)), true); + + // http:///b/35346596 + // Sometimes we receive a second, latent EVENT_MODIFY that happens shortly + // after the first one. This will intercept it and make sure it won't + // mess with ENTRY_DELETE later. + Thread.sleep(500); + WatchKey doubleModifyKey = watchService.poll(); + if (doubleModifyKey != null) { + List> event = doubleModifyKey.pollEvents(); + assertEquals(ENTRY_MODIFY, event.get(0).kind()); + doubleModifyKey.reset(); + } + assertNull(watchService.poll()); + + // emit EVENT_DELETE + Files.delete(file); + assertWatchServiceEvent(watchService, directoryKey1, + Arrays.asList(new WatchEventResult(ENTRY_DELETE, 1)), true); + + // Assert no more events + assertNull(watchService.poll()); + watchService.close(); + } + + @Test + public void test_EventMask() throws Exception { + WatchService watchService = FileSystems.getDefault().newWatchService(); + WatchEvent.Kind[] events = {ENTRY_DELETE}; + Path file = Paths.get(filesSetup.getTestDir(), "directory/file"); + Path directory = Paths.get(filesSetup.getTestDir(), "directory"); + assertFalse(Files.exists(file)); + Files.createDirectories(directory); + WatchKey directoryKey1 = directory.register(watchService, events); + + // emit EVENT_CREATE + Files.createFile(file); + // emit EVENT_MODIFY (masked) + Files.write(file, "hello1".getBytes()); + // emit EVENT_DELETE (masked) + Files.delete(file); + + assertWatchServiceEvent(watchService, directoryKey1, + Arrays.asList(new WatchEventResult(ENTRY_DELETE, 1)), true); + assertNull(watchService.poll()); + watchService.close(); + } + + @Test + public void test_singleDirectory() throws Exception { + WatchService watchService = FileSystems.getDefault().newWatchService(); + Path dirInDir = Paths.get(filesSetup.getTestDir(), "directory/dir"); + Path directory = Paths.get(filesSetup.getTestDir(), "directory"); + assertFalse(Files.exists(dirInDir)); + Files.createDirectories(directory); + WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS); + + // emit EVENT_CREATE + Files.createDirectories(dirInDir); + + // Shouldn't emit EVENT_MODIFY + Path dirInDirInDir = Paths.get(filesSetup.getTestDir(), "directory/dir/dir"); + Files.createDirectories(dirInDirInDir); + Files.delete(dirInDirInDir); + + // emit EVENT_DELETE + Files.delete(dirInDir); + + assertWatchServiceEvent(watchService, directoryKey1, + Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), + new WatchEventResult(ENTRY_DELETE, 1)), true); + assertNull(watchService.poll()); + watchService.close(); + + watchService.close(); + } + + @Test + public void test_cancel() throws Exception { + WatchService watchService = FileSystems.getDefault().newWatchService(); + Path file = Paths.get(filesSetup.getTestDir(), "directory/file"); + Path directory = Paths.get(filesSetup.getTestDir(), "directory"); + assertFalse(Files.exists(file)); + Files.createDirectories(directory); + WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS); + + // emit EVENT_CREATE + Files.createFile(file); + + // Canceling the key may prevent the EVENT_CREATE from being picked-up... + // TODO: Fix this (b/35190858). + Thread.sleep(500); + + // Cancel the key + directoryKey1.cancel(); + assertFalse(directoryKey1.isValid()); + + // Shouldn't emit EVENT_MODIFY and EVENT_DELETE + Files.write(file, "hello1".getBytes()); + Files.delete(file); + + assertWatchServiceEvent(watchService, directoryKey1, + Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1)), false); + assertNull(watchService.poll()); + watchService.close(); + } + + @Test + public void test_removeTarget() throws Exception { + WatchService watchService = FileSystems.getDefault().newWatchService(); + Path file = Paths.get(filesSetup.getTestDir(), "directory/file"); + Path directory = Paths.get(filesSetup.getTestDir(), "directory"); + assertFalse(Files.exists(file)); + Files.createDirectories(directory); + WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS); + + // emit EVENT_CREATE x1 + Files.createFile(file); + Files.delete(file); + + // Delete underlying target. + assertTrue(directoryKey1.isValid()); + Files.delete(directory); + + // We need to give some time to watch service thread to catch up with the + // deletion + while (directoryKey1.isValid()) { + Thread.sleep(500); + } + + assertWatchServiceEvent(watchService, directoryKey1, + Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), + new WatchEventResult(ENTRY_DELETE, 1)), false); + assertNull(watchService.poll()); + + watchService.close(); + } + + @Test + public void test_multipleKeys() throws Exception { + WatchService watchService1 = FileSystems.getDefault().newWatchService(); + + Path directory1 = Paths.get(filesSetup.getTestDir(), "directory1"); + Path directory2 = Paths.get(filesSetup.getTestDir(), "directory2"); + + Path dir1file1 = Paths.get(filesSetup.getTestDir(), "directory1/file1"); + assertFalse(Files.exists(dir1file1)); + Path dir2file1 = Paths.get(filesSetup.getTestDir(), "directory2/file1"); + assertFalse(Files.exists(dir2file1)); + + Files.createDirectories(directory1); + Files.createDirectories(directory2); + WatchKey directoryKey1 = directory1.register(watchService1, ALL_EVENTS_KINDS); + WatchKey directoryKey2 = directory2.register(watchService1, ALL_EVENTS_KINDS); + + // emit EVENT_CREATE/DELETE for all + Path[] allFiles = new Path[]{dir1file1, dir2file1}; + for (Path path : allFiles) { + Files.createFile(path); + Files.delete(path); + } + + assertWatchServiceEvent(watchService1, directoryKey1, + Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), + new WatchEventResult(ENTRY_DELETE, 1)), true); + assertWatchServiceEvent(watchService1, directoryKey2, + Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), + new WatchEventResult(ENTRY_DELETE, 1)), true); + + assertNull(watchService1.poll()); + watchService1.close(); + } + + @Test + public void test_multipleServices() throws Exception { + WatchService watchService1 = FileSystems.getDefault().newWatchService(); + WatchService watchService2 = FileSystems.getDefault().newWatchService(); + + Path directory1 = Paths.get(filesSetup.getTestDir(), "directory1"); + Path directory2 = Paths.get(filesSetup.getTestDir(), "directory2"); + + Path dir1file1 = Paths.get(filesSetup.getTestDir(), "directory1/file1"); + assertFalse(Files.exists(dir1file1)); + Path dir2file1 = Paths.get(filesSetup.getTestDir(), "directory2/file1"); + assertFalse(Files.exists(dir2file1)); + + Files.createDirectories(directory1); + Files.createDirectories(directory2); + + // 2 services listening for distinct directories + WatchKey directoryKey1 = directory1.register(watchService1, ALL_EVENTS_KINDS); + WatchKey directoryKey2 = directory2.register(watchService2, ALL_EVENTS_KINDS); + // emit EVENT_CREATE/DELETE for all + Path[] allFiles = new Path[]{dir1file1, dir2file1}; + for (Path path : allFiles) { + Files.createFile(path); + Files.delete(path); + } + + assertWatchServiceEvent(watchService1, directoryKey1, + Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), + new WatchEventResult(ENTRY_DELETE, 1)), true); + assertWatchServiceEvent(watchService2, directoryKey2, + Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), + new WatchEventResult(ENTRY_DELETE, 1)), true); + + // 2 services listening for a same directory + WatchKey directoryKey3 = directory1.register(watchService2, ALL_EVENTS_KINDS); + { + Files.createFile(dir1file1); + Files.delete(dir1file1); + } + assertWatchServiceEvent(watchService1, directoryKey1, + Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), + new WatchEventResult(ENTRY_DELETE, 1)), true); + assertWatchServiceEvent(watchService2, directoryKey3, + Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), + new WatchEventResult(ENTRY_DELETE, 1)), true); + + + + assertNull(watchService1.poll()); + watchService1.close(); + assertNull(watchService2.poll()); + watchService2.close(); + } +} \ No newline at end of file diff --git a/luni/src/test/java/libcore/java/nio/file/attribute/AclEntryTest.java b/luni/src/test/java/libcore/java/nio/file/attribute/AclEntryTest.java new file mode 100644 index 000000000..6e0ccd8a0 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/attribute/AclEntryTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file.attribute; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static junit.framework.TestCase.assertTrue; + +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.UserPrincipal; +import java.nio.file.attribute.AclEntryType; +import java.nio.file.attribute.AclEntryPermission; +import java.nio.file.attribute.AclEntryFlag; +import java.nio.file.Files; +import java.nio.file.Paths; + +import java.util.Set; + + +public class AclEntryTest { + + @Test + public void testGetters() throws Exception { + UserPrincipal user = Files.getOwner(Paths.get(".")); + + AclEntry aclEntry = AclEntry.newBuilder() + .setType(AclEntryType.ALLOW) + .setPrincipal(user) + .setFlags(AclEntryFlag.INHERIT_ONLY) + .setPermissions(AclEntryPermission.READ_DATA, AclEntryPermission.READ_ATTRIBUTES) + .build(); + assertEquals(AclEntryType.ALLOW, aclEntry.type()); + assertEquals(user, aclEntry.principal()); + + Set permissions = aclEntry.permissions(); + assertEquals(2, permissions.size()); + assertTrue(permissions.contains(AclEntryPermission.READ_DATA)); + assertTrue(permissions.contains(AclEntryPermission.READ_ATTRIBUTES)); + + Set flags = aclEntry.flags(); + assertEquals(1, flags.size()); + assertTrue(flags.contains(AclEntryFlag.INHERIT_ONLY)); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/attribute/UserPrincipalNotFoundExceptionTest.java b/luni/src/test/java/libcore/java/nio/file/attribute/UserPrincipalNotFoundExceptionTest.java new file mode 100644 index 000000000..095e9e032 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/attribute/UserPrincipalNotFoundExceptionTest.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 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 + */ + +package libcore.java.nio.file.attribute; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +import java.nio.file.attribute.UserPrincipalNotFoundException; + +public class UserPrincipalNotFoundExceptionTest { + + @Test + public void testGetters() throws Exception { + final String name = "foobar"; + UserPrincipalNotFoundException upnfException = + new UserPrincipalNotFoundException(name); + assertEquals(name, upnfException.getName()); + } +} diff --git a/luni/src/test/java/libcore/java/nio/file/spi/FileTypeDetectorTest.java b/luni/src/test/java/libcore/java/nio/file/spi/FileTypeDetectorTest.java new file mode 100644 index 000000000..bf6c6b78e --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/file/spi/FileTypeDetectorTest.java @@ -0,0 +1,22 @@ +package libcore.java.nio.file.spi; + +import org.junit.Test; + +import java.nio.file.Paths; +import java.nio.file.spi.FileTypeDetector; + +import static org.junit.Assert.assertEquals; + +public class FileTypeDetectorTest { + + @Test + public void test_probeFileType() throws Exception { + FileTypeDetector defaultFileTypeDetector = sun.nio.fs.DefaultFileTypeDetector.create(); + // The method uses file extensions to deduce mime type, therefore, it doesn't check for + // file existence. + assertEquals("text/plain", + defaultFileTypeDetector.probeContentType(Paths.get("file.txt"))); + assertEquals("text/x-java", + defaultFileTypeDetector.probeContentType(Paths.get("file.java"))); + } +} diff --git a/luni/src/test/java/libcore/java/security/AccessControllerTest.java b/luni/src/test/java/libcore/java/security/AccessControllerTest.java index 1f7da9136..5746abd04 100644 --- a/luni/src/test/java/libcore/java/security/AccessControllerTest.java +++ b/luni/src/test/java/libcore/java/security/AccessControllerTest.java @@ -36,6 +36,7 @@ public final class AccessControllerTest extends TestCase { public void testDoPrivilegedWithCombiner() { final Permission permission = new RuntimePermission("do stuff"); final DomainCombiner union = new DomainCombiner() { + @Override public ProtectionDomain[] combine(ProtectionDomain[] a, ProtectionDomain[] b) { throw new AssertionFailedError("Expected combiner to be unused"); } @@ -48,12 +49,14 @@ public ProtectionDomain[] combine(ProtectionDomain[] a, ProtectionDomain[] b) { final AtomicInteger actionCount = new AtomicInteger(); AccessController.doPrivileged(new PrivilegedAction() { + @Override public Void run() { assertEquals(null, AccessController.getContext().getDomainCombiner()); AccessController.getContext().checkPermission(permission); // Calling doPrivileged again would have exercised the combiner AccessController.doPrivileged(new PrivilegedAction() { + @Override public Void run() { actionCount.incrementAndGet(); assertEquals(null, AccessController.getContext().getDomainCombiner()); diff --git a/luni/src/test/java/libcore/java/security/DomainLoadStoreParameterTest.java b/luni/src/test/java/libcore/java/security/DomainLoadStoreParameterTest.java new file mode 100644 index 000000000..2c2e4b8a6 --- /dev/null +++ b/luni/src/test/java/libcore/java/security/DomainLoadStoreParameterTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.security; + +import junit.framework.TestCase; + +import java.net.URI; +import java.security.KeyStore; +import java.security.DomainLoadStoreParameter; +import java.util.HashMap; +import java.util.Map; + +public class DomainLoadStoreParameterTest extends TestCase { + private static final String KEY_STORE_NAME = "keyStoreName"; + private KeyStore.ProtectionParameter protectionParameter; + private URI validConfigurationURI; + + @Override + public void setUp() throws Exception { + super.setUp(); + protectionParameter = new KeyStore.ProtectionParameter() {}; + validConfigurationURI = new URI("http://UriForConfiguration.SergioRulesTheWorld.com/"); + } + + public void testConstructor_nullValues_throwException() throws Exception { + try { + new DomainLoadStoreParameter(null /* configuration */, + createNonEmptyParameters(KEY_STORE_NAME, protectionParameter)); + fail("configuration can't be null when creating DomainLoadStoreParameter"); + } catch (NullPointerException expected) { + } + + try { + new DomainLoadStoreParameter(validConfigurationURI, null /* protectionParameters */); + fail("protection parameters can't be null when creating DomainLoadStoreParameter"); + } catch (NullPointerException expected) { + } + } + + /** + * Check that it returns the configuration specified in the constructor. + */ + public void testGetConfiguration() { + DomainLoadStoreParameter domainLoadStoreParameter = + new DomainLoadStoreParameter(validConfigurationURI, + createNonEmptyParameters(KEY_STORE_NAME, protectionParameter)); + assertSame(validConfigurationURI, domainLoadStoreParameter.getConfiguration()); + } + + public void testGetProtectionParams() { + Map protectionParameters = + createNonEmptyParameters(KEY_STORE_NAME, protectionParameter); + DomainLoadStoreParameter domainLoadStoreParameter = + new DomainLoadStoreParameter(validConfigurationURI, protectionParameters); + Map returnedParams = + domainLoadStoreParameter.getProtectionParams(); + assertEquals(protectionParameters, returnedParams); + + // Trying to add to the returned set throws an exception + try { + returnedParams.put("some_other_keystore", protectionParameter); + fail("The parameters returned by getProtectionParams should be unmodifiable"); + } catch (UnsupportedOperationException expected) { + } + + // Adding to the map passed as parameter doesn't change value in the + // {@code DomainLoadStoreParameter}, ie, it holds a copy. + Map originalProtectionParameters + = new HashMap<>(protectionParameters); + protectionParameters.put("some_other_keystore", protectionParameter); + assertEquals(originalProtectionParameters, + domainLoadStoreParameter.getProtectionParams()); + + } + + /** + * Getter for the protection parameters in this domain. + * + * Check that always returns null. Keystore domains do not support a protection parameter. + */ + public void testGetProtectionParameter() { + DomainLoadStoreParameter domainLoadStoreParameter = + new DomainLoadStoreParameter(validConfigurationURI, + createNonEmptyParameters("keyStoreName", protectionParameter)); + // Check that always returns null. Keystore domains do not support a protection parameter. + assertNull(domainLoadStoreParameter.getProtectionParameter()); + } + + private Map createNonEmptyParameters( + String keyStoreName, KeyStore.ProtectionParameter protectionParameter) { + Map protectionParameters = new HashMap<>(); + protectionParameters.put(keyStoreName, protectionParameter); + return protectionParameters; + } +} diff --git a/luni/src/test/java/libcore/java/security/MessageDigestTest.java b/luni/src/test/java/libcore/java/security/MessageDigestTest.java index 60dc36a77..1a00af437 100644 --- a/luni/src/test/java/libcore/java/security/MessageDigestTest.java +++ b/luni/src/test/java/libcore/java/security/MessageDigestTest.java @@ -307,4 +307,10 @@ public void testMessageDigestDelegateOverridesAllMethods() throws Exception { assertEquals(Collections.EMPTY_LIST, methodsNotOverridden); } + + public void testIsEqual_nullValues() { + assertTrue(MessageDigest.isEqual(null, null)); + assertFalse(MessageDigest.isEqual(null, new byte[1])); + assertFalse(MessageDigest.isEqual(new byte[1], null)); + } } diff --git a/luni/src/test/java/libcore/java/security/PKCS12AttributeTest.java b/luni/src/test/java/libcore/java/security/PKCS12AttributeTest.java new file mode 100644 index 000000000..f4b2802c4 --- /dev/null +++ b/luni/src/test/java/libcore/java/security/PKCS12AttributeTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.security; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.security.PKCS12Attribute; +import java.util.Arrays; + + +public class PKCS12AttributeTest extends TestCase { + private static final String PKCS9_EMAIL_ADDRESS_OID = "1.2.840.113549.1.9.1"; + private static final String PKCS9_CONTENT_TYPE_OID = "1.2.840.113549.1.9.3"; + private static final String PKCS7_SIGNED_DATA_OID = "1.2.840.113549.1.7.2"; + private static final String EXAMPLE_EMAIL_ADDRESS = "someemail@server.com"; + private static final String EXAMPLE_EMAIL_ADDRESS_2 = "someotheremail@server.com"; + private static final String EXAMPLE_SEQUENCE_OF_EMAILS = + "[" + EXAMPLE_EMAIL_ADDRESS + ", " + EXAMPLE_EMAIL_ADDRESS_2 + "]"; + + /* + * Encoded attribute obtained using BouncyCastle as an oracle for the known answer: + * + DERSequence s = new DERSequence(new ASN1Encodable[] { + new ASN1ObjectIdentifier("1.2.840.113549.1.9.1"), + new DERSet(new ASN1Encodable[] { new DERUTF8String("someemail@server.com") }) + }); + System.out.println(Arrays.toString(s.getEncoded())); + */ + private static final byte[] ENCODED_ATTRIBUTE_UTF8_EMAIL_ADDRESS = new byte[] { + 48, 35, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 1, 49, 22, 12, 20, 115, 111, 109, + 101, 101, 109, 97, 105, 108, 64, 115, 101, 114, 118, 101, 114, 46, 99, 111, 109 + }; + + /* + * Encoded attribute obtained using BouncyCastle as an oracle for the known answer: + * + DERSequence s = new DERSequence(new ASN1Encodable[] { + new ASN1ObjectIdentifier("1.2.840.113549.1.9.1"), + new DERSet(new ASN1Encodable[] { + new DEROctetString("someemail@server.com".getBytes()) + }) + }); + System.out.println(Arrays.toString(s.getEncoded())); + */ + private static final byte[] ENCODED_ATTRIBUTE_OCTET_EMAIL_ADDRESS = new byte[] { + 48, 35, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 1, 49, 22, 4, 20, 115, 111, 109, + 101, 101, 109, 97, 105, 108, 64, 115, 101, 114, 118, 101, 114, 46, 99, 111, 109 + }; + + /* + * Encoded attribute obtained using BouncyCastle as an oracle for the known answer: + * + DERSequence s = new DERSequence(new ASN1Encodable[] { + new ASN1ObjectIdentifier("1.2.840.113549.1.9.1"), + new DERSet(new ASN1Encodable[] { + new DERUTF8String("someemail@server.com"), + new DERUTF8String("someotheremail@server.com"), + }) + }); + */ + private static final byte[] ENCODED_ATTRIBUTE_SEQUENCE_OF_EMAIL_ADDRESSES = new byte[] { + 48, 62, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 1, 49, 49, 12, 20, 115, 111, 109, + 101, 101, 109, 97, 105, 108, 64, 115, 101, 114, 118, 101, 114, 46, 99, 111, 109, 12, 25, + 115, 111, 109, 101, 111, 116, 104, 101, 114, 101, 109, 97, 105, 108, 64, 115, 101, + 114, 118, 101, 114, 46, 99, 111, 109 + }; + + /* + * Encoded attribute obtained using BouncyCastle as an oracle for the known answer: + * + DERSequence s = new DERSequence(new ASN1Encodable[] { + new ASN1ObjectIdentifier("1.2.840.113549.1.9.3"), + new DERSet(new ASN1Encodable[] { + new ASN1ObjectIdentifier("1.2.840.113549.1.7.2") + }) + }); + System.out.println(Arrays.toString(s.getEncoded())); + */ + private static final byte[] ENCODED_ATTRIBUTE_CONTENT_TYPE_SIGNED_DATA = new byte[] { + 48, 24, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 3, 49, 11, 6, 9, 42, -122, 72, -122, -9, + 13, 1, 7, 2 + }; + + /* + echo -n 'someemail@server.com' | recode ../x1 | tr $'\x0a' ' ' \ + | sed 's/, /:/g' | sed 's/0x//g' + */ + private static final String EXAMPLE_EMAIL_AS_HEX_BYTES = + "73:6F:6D:65:65:6D:61:69:6C:40:73:65:72:76:65:72:2E:63:6F:6D"; + + public void test_Constructor_String_String_success() { + PKCS12Attribute att = new PKCS12Attribute(PKCS9_EMAIL_ADDRESS_OID, EXAMPLE_EMAIL_ADDRESS); + assertEquals(PKCS9_EMAIL_ADDRESS_OID, att.getName()); + assertEquals(EXAMPLE_EMAIL_ADDRESS, att.getValue()); + } + + public void test_Constructor_String_String_nullOID_throwsException() { + try { + new PKCS12Attribute(null, EXAMPLE_EMAIL_ADDRESS); + fail("Constructor allowed a null OID"); + } catch(NullPointerException expected) { + } + } + + public void test_Constructor_String_String_nullValue_throwsException() { + try { + new PKCS12Attribute(PKCS9_EMAIL_ADDRESS_OID, null); + fail("Constructor allowed a null value"); + } catch(NullPointerException expected) { + } + } + + public void test_Constructor_String_String_wrongOID_throwsException() { + try { + PKCS12Attribute att = + new PKCS12Attribute("IDontThinkThisIsAnOID", EXAMPLE_EMAIL_ADDRESS); + fail("Constructor allowed an invalid OID"); + } catch(IllegalArgumentException expected) { + } + } + + public void test_Constructor_byteArray_success() { + PKCS12Attribute att = new PKCS12Attribute(ENCODED_ATTRIBUTE_UTF8_EMAIL_ADDRESS); + assertEquals(PKCS9_EMAIL_ADDRESS_OID, att.getName()); + assertEquals(EXAMPLE_EMAIL_ADDRESS, att.getValue()); + } + + public void testConstructor_byteArray_nullEncoded_throwsException() { + try { + new PKCS12Attribute(null); + fail("Constructor accepted null encoded value"); + } catch (NullPointerException expected) { + } + } + + public void test_Constructor_byteArray_wrongEncoding_throwsException() { + try { + new PKCS12Attribute(new byte[]{3, 14, 16}); + fail("Constructor accepted invalid encoding"); + } catch (IllegalArgumentException expected) { + } + } + + public void test_Constructor_String_String_sequenceValue() { + PKCS12Attribute att = new PKCS12Attribute( + PKCS9_EMAIL_ADDRESS_OID, EXAMPLE_SEQUENCE_OF_EMAILS); + assertEquals(PKCS9_EMAIL_ADDRESS_OID, att.getName()); + assertEquals(EXAMPLE_SEQUENCE_OF_EMAILS, att.getValue()); + assertEquals(Arrays.toString(ENCODED_ATTRIBUTE_SEQUENCE_OF_EMAIL_ADDRESSES), + Arrays.toString(att.getEncoded())); + } + + public void test_Constructor_String_String_hexValues() { + PKCS12Attribute att = new PKCS12Attribute( + PKCS9_EMAIL_ADDRESS_OID, EXAMPLE_EMAIL_AS_HEX_BYTES); + assertEquals(PKCS9_EMAIL_ADDRESS_OID, att.getName()); + assertEquals(EXAMPLE_EMAIL_AS_HEX_BYTES, att.getValue()); + // When specified as hex bytes, the underlying encoding is a DER octet string. + assertEquals(Arrays.toString(ENCODED_ATTRIBUTE_OCTET_EMAIL_ADDRESS), + Arrays.toString(att.getEncoded())); + } + + @SuppressWarnings("SelfEquals") + public void test_Equals() { + PKCS12Attribute att = new PKCS12Attribute( + PKCS9_EMAIL_ADDRESS_OID, EXAMPLE_EMAIL_ADDRESS); + assertTrue(att.equals(att)); + assertFalse(att.equals(new Object())); + assertFalse(att.equals(null)); + assertTrue(att.equals(new PKCS12Attribute(ENCODED_ATTRIBUTE_UTF8_EMAIL_ADDRESS))); + assertFalse(att.equals( + new PKCS12Attribute(ENCODED_ATTRIBUTE_SEQUENCE_OF_EMAIL_ADDRESSES))); + } + + /* Test the case in which the value encoded is an object id.*/ + public void test_encoding_ObjectIdValue() { + PKCS12Attribute att = new PKCS12Attribute(ENCODED_ATTRIBUTE_CONTENT_TYPE_SIGNED_DATA); + assertEquals(PKCS9_CONTENT_TYPE_OID, att.getName()); + /* Value is correctly decoded to a string. */ + assertEquals(PKCS7_SIGNED_DATA_OID, att.getValue()); + } +} diff --git a/luni/src/test/java/libcore/java/security/PrincipalTest.java b/luni/src/test/java/libcore/java/security/PrincipalTest.java new file mode 100644 index 000000000..43a8e68a1 --- /dev/null +++ b/luni/src/test/java/libcore/java/security/PrincipalTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.java.security; + +import junit.framework.TestCase; + +import java.security.Principal; +import java.util.Collections; +import java.util.HashSet; + +import javax.security.auth.Subject; + + +public class PrincipalTest extends TestCase { + /** + * Default implementation of {@code implies} returns true iff the principal is one + * of the subject's principals, or if the subject is null. + */ + public void test_Principal_implies() throws Exception { + HashSet subjectPrincipals = new HashSet<>(); + subjectPrincipals.add(new PrincipalWithEqualityByName("a")); + subjectPrincipals.add(new PrincipalWithEqualityByName("b")); + Subject subject = new Subject( + true /* readOnly */, + subjectPrincipals, + Collections.EMPTY_SET /* pubCredentials */, + Collections.EMPTY_SET /* privCredentials */); + Principal principalA = new PrincipalWithEqualityByName("a"); + assertTrue(principalA.implies(subject)); + Principal principalC = new PrincipalWithEqualityByName("c"); + assertFalse(principalC.implies(subject)); + assertFalse(principalC.implies(null)); + } + + private static class PrincipalWithEqualityByName implements Principal { + + private final String name; + + PrincipalWithEqualityByName(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PrincipalWithEqualityByName)) { + return false; + } + return this.name.equals(((PrincipalWithEqualityByName) other).getName()); + } + } +} diff --git a/luni/src/test/java/libcore/java/security/PrivilegedActionExceptionTest.java b/luni/src/test/java/libcore/java/security/PrivilegedActionExceptionTest.java new file mode 100644 index 000000000..aeea8060a --- /dev/null +++ b/luni/src/test/java/libcore/java/security/PrivilegedActionExceptionTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016 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. + */ + +package libcore.java.security; + +import static org.junit.Assert.assertSame; + +import java.security.PrivilegedActionException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.Test; + +@RunWith(JUnit4.class) +public class PrivilegedActionExceptionTest { + + /** + * PrivilegedActionException's constructor argument may be rethrown by getException() or + * getCause(). + * b/31360928 + */ + @Test + public void testGetException() { + Exception e = new Exception(); + PrivilegedActionException pae = new PrivilegedActionException(e); + + assertSame(e, pae.getException()); + assertSame(e, pae.getCause()); + } +} diff --git a/luni/src/test/java/libcore/java/security/ProviderTest.java b/luni/src/test/java/libcore/java/security/ProviderTest.java index 7d9e63340..ffba98aa4 100644 --- a/luni/src/test/java/libcore/java/security/ProviderTest.java +++ b/luni/src/test/java/libcore/java/security/ProviderTest.java @@ -35,14 +35,21 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.TreeMap; +import java.util.TreeSet; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.crypto.Cipher; @@ -80,6 +87,20 @@ public void test_Provider_getServices() throws Exception { Set services = provider.getServices(); assertNotNull(services); assertFalse(services.isEmpty()); + if (LOG_DEBUG) { + Set originalServices = services; + services = new TreeSet( + new Comparator() { + public int compare(Provider.Service a, Provider.Service b) { + int typeCompare = a.getType().compareTo(b.getType()); + if (typeCompare != 0) { + return typeCompare; + } + return a.getAlgorithm().compareTo(b.getAlgorithm()); + } + }); + services.addAll(originalServices); + } for (Provider.Service service : services) { String type = service.getType(); @@ -144,10 +165,28 @@ public void test_Provider_getServices() throws Exception { // assert that we don't have any extra in the implementation Collections.sort(extra); // sort so that its grouped by type - assertEquals("Extra algorithms", Collections.EMPTY_LIST, extra); - + assertEquals("Algorithms are provided but not present in StandardNames", + Collections.EMPTY_LIST, extra); + + if (remainingExpected.containsKey("Cipher")) { + // For any remaining ciphers, they may be aliases for other ciphers or otherwise + // don't show up as a service but can still be instantiated. + for (Iterator cipherIt = remainingExpected.get("Cipher").iterator(); + cipherIt.hasNext(); ) { + String missingCipher = cipherIt.next(); + try { + Cipher.getInstance(missingCipher); + cipherIt.remove(); + } catch (NoSuchAlgorithmException|NoSuchPaddingException e) { + } + } + if (remainingExpected.get("Cipher").isEmpty()) { + remainingExpected.remove("Cipher"); + } + } // assert that we don't have any missing in the implementation - assertEquals("Missing algorithms", Collections.EMPTY_MAP, remainingExpected); + assertEquals("Algorithms are present in StandardNames but not provided", + Collections.EMPTY_MAP, remainingExpected); // assert that we don't have any missing classes Collections.sort(missing); // sort it for readability @@ -552,6 +591,46 @@ public boolean supportsParameter(Object parameter) { } } + @SuppressWarnings("serial") + public void testProviderService_newInstance_PrivateClass_throws() + throws Exception { + MockProvider provider = new MockProvider("MockProvider"); + + provider.putServiceForTest(new Provider.Service(provider, "CertStore", "FOO", + CertStoreSpiPrivateClass.class.getName(), null, null)); + + Security.addProvider(provider); + // The class for the service is private, it must fail with NoSuchAlgorithmException + try { + Provider.Service service = provider.getService("CertStore", "FOO"); + service.newInstance(null); + fail(); + } catch (NoSuchAlgorithmException expected) { + } finally { + Security.removeProvider(provider.getName()); + } + } + + @SuppressWarnings("serial") + public void testProviderService_newInstance_PrivateEmptyConstructor_throws() + throws Exception { + MockProvider provider = new MockProvider("MockProvider"); + + provider.putServiceForTest(new Provider.Service(provider, "CertStore", "FOO", + CertStoreSpiPrivateEmptyConstructor.class.getName(), null, null)); + + Security.addProvider(provider); + // The empty constructor is private, it must fail with NoSuchAlgorithmException + try { + Provider.Service service = provider.getService("CertStore", "FOO"); + service.newInstance(null); + fail(); + } catch (NoSuchAlgorithmException expected) { + } finally { + Security.removeProvider(provider.getName()); + } + } + @SuppressWarnings("serial") public void testProviderService_AliasDoesNotEraseCanonical_Success() throws Exception { @@ -631,6 +710,45 @@ public Collection engineGetCRLs(CRLSelector selector) } } + private static class CertStoreSpiPrivateClass extends CertStoreSpi { + public CertStoreSpiPrivateClass() + throws InvalidAlgorithmParameterException { + super(null); + } + + @Override + public Collection engineGetCertificates(CertSelector selector) + throws CertStoreException { + throw new UnsupportedOperationException(); + } + + @Override + public Collection engineGetCRLs(CRLSelector selector) + throws CertStoreException { + throw new UnsupportedOperationException(); + } + } + + private static class CertStoreSpiPrivateEmptyConstructor extends CertStoreSpi { + private CertStoreSpiPrivateEmptyConstructor(CertStoreParameters params) + throws InvalidAlgorithmParameterException { + super(null); + } + + @Override + public Collection engineGetCertificates(CertSelector selector) + throws CertStoreException { + throw new UnsupportedOperationException(); + } + + @Override + public Collection engineGetCRLs(CRLSelector selector) + throws CertStoreException { + throw new UnsupportedOperationException(); + } + } + + public static class MyCertStoreParameters implements CertStoreParameters { public Object clone() { return new MyCertStoreParameters(); @@ -661,6 +779,291 @@ public void setup() { } } + // TODO(29631070): this is a general testing mechanism to test other operations that are + // going to be added. + public void testHashMapOperations() { + performHashMapOperationAndCheckResults( + PUT /* operation */, + mapOf("class1.algorithm1", "impl1") /* initialStatus */, + new Pair("class2.algorithm2", "impl2") /* operationParameters */, + mapOf("class1.algorithm1", "impl1", + "class2.algorithm2", "impl2"), + true /* mustChangeSecurityVersion */); + performHashMapOperationAndCheckResults( + PUT_ALL, + mapOf("class1.algorithm1", "impl1"), + mapOf("class2.algorithm2", "impl2", "class3.algorithm3", "impl3"), + mapOf("class1.algorithm1", "impl1", + "class2.algorithm2", "impl2", + "class3.algorithm3", "impl3"), + true /* mustChangeSecurityVersion */); + performHashMapOperationAndCheckResults( + REMOVE, + mapOf("class1.algorithm1", "impl1"), + "class1.algorithm1", + mapOf(), + true /* mustChangeSecurityVersion */); + performHashMapOperationAndCheckResults( + REMOVE, + mapOf("class1.algorithm1", "impl1"), + "class2.algorithm1", + mapOf("class1.algorithm1", "impl1"), + true /* mustChangeSecurityVersion */); + performHashMapOperationAndCheckResults( + COMPUTE, + mapOf("class1.algorithm1", "impl1"), + // It's really difficult to find an example of this that sounds realistic + // for a Provider... + new Pair("class1.algorithm1", CONCAT), + mapOf("class1.algorithm1", "class1.algorithm1impl1"), + true); + performHashMapOperationAndCheckResults( + PUT_IF_ABSENT, + mapOf("class1.algorithm1", "impl1"), + new Pair("class1.algorithm1", "impl2"), + // Don't put because key is absent. + mapOf("class1.algorithm1", "impl1"), + true); + performHashMapOperationAndCheckResults( + PUT_IF_ABSENT, + mapOf("class1.algorithm1", "impl1"), + new Pair("class2.algorithm2", "impl2"), + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + true); + performHashMapOperationAndCheckResults( + PUT_IF_ABSENT, + mapOf("class1.algorithm1", "impl1"), + new Pair("class2.algorithm2", "impl2"), + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + true); + performHashMapOperationAndCheckResults( + COMPUTE_IF_PRESENT, + mapOf("class1.algorithm1", "impl1"), + new Pair("class1.algorithm1", CONCAT), + mapOf("class1.algorithm1", "class1.algorithm1impl1"), + true); + performHashMapOperationAndCheckResults( + COMPUTE_IF_PRESENT, + mapOf("class1.algorithm1", "impl1"), + new Pair("class2.algorithm2", CONCAT), + // Don't compute because is not present. + mapOf("class1.algorithm1", "impl1"), + true); + performHashMapOperationAndCheckResults( + COMPUTE_IF_ABSENT, + mapOf("class1.algorithm1", "impl1"), + new Pair("class2.algorithm2", TO_UPPER_CASE), + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "CLASS2.ALGORITHM2"), + true); + performHashMapOperationAndCheckResults( + COMPUTE_IF_ABSENT, + mapOf("class1.algorithm1", "impl1"), + new Pair("class1.algorithm1", TO_UPPER_CASE), + // Don't compute because if not absent. + mapOf("class1.algorithm1", "impl1"), + true); + performHashMapOperationAndCheckResults( + REPLACE_USING_KEY, + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + new Pair("class1.algorithm1", "impl3"), + mapOf("class1.algorithm1", "impl3", "class2.algorithm2", "impl2"), + true); + performHashMapOperationAndCheckResults( + REPLACE_USING_KEY, + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + new Pair("class1.algorithm3", "impl3"), + // Do not replace as the key is not present. + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + true); + performHashMapOperationAndCheckResults( + REPLACE_USING_KEY_AND_VALUE, + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + new Pair(new Pair("class1.algorithm1", "impl1"), "impl3"), + mapOf("class1.algorithm1", "impl3", "class2.algorithm2", "impl2"), + true); + performHashMapOperationAndCheckResults( + REPLACE_USING_KEY_AND_VALUE, + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + new Pair(new Pair("class1.algorithm1", "impl4"), "impl3"), + // Do not replace as the key/value pair is not present. + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + true); + performHashMapOperationAndCheckResults( + REPLACE_ALL, + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + // Applying simply CONCAT will affect internal mappings of the provider (version, + // info, name, etc) + CONCAT_IF_STARTING_WITH_CLASS, + mapOf("class1.algorithm1", "class1.algorithm1impl1", + "class2.algorithm2", "class2.algorithm2impl2"), + true); + performHashMapOperationAndCheckResults( + MERGE, + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + new Pair(new Pair("class1.algorithm1", "impl3"), CONCAT), + // The key is present, so the function is used. + mapOf("class1.algorithm1", "impl1impl3", + "class2.algorithm2", "impl2"), + true); + performHashMapOperationAndCheckResults( + MERGE, + mapOf("class1.algorithm1", "impl1", "class2.algorithm2", "impl2"), + new Pair(new Pair("class3.algorithm3", "impl3"), CONCAT), + // The key is not present, so the value is used. + mapOf("class1.algorithm1", "impl1", + "class2.algorithm2", "impl2", + "class3.algorithm3", "impl3"), + true); + } + + public void test_getOrDefault() { + Provider p = new MockProvider("MockProvider"); + p.put("class1.algorithm1", "impl1"); + assertEquals("impl1", p.getOrDefault("class1.algorithm1", "default")); + assertEquals("default", p.getOrDefault("thisIsNotInTheProvider", "default")); + } + + private static class Pair { + private final A first; + private final B second; + Pair(A first, B second) { + this.first = first; + this.second = second; + } + } + + /* Holder class for the provider parameter and the parameter for the operation. */ + private static class ProviderAndOperationParameter { + private final Provider provider; + private final T operationParameters; + ProviderAndOperationParameter(Provider p, T o) { + provider = p; + operationParameters = o; + } + } + + private static final Consumer>> PUT = + provAndParam -> + provAndParam.provider.put( + provAndParam.operationParameters.first, + provAndParam.operationParameters.second); + + private static final Consumer>> PUT_ALL = + provAndParam -> provAndParam.provider.putAll(provAndParam.operationParameters); + + private static final Consumer> REMOVE = + provAndParam -> provAndParam.provider.remove(provAndParam.operationParameters); + + private static final Consumer>>> COMPUTE = + provAndParam -> provAndParam.provider.compute( + provAndParam.operationParameters.first, + provAndParam.operationParameters.second); + + private static final BiFunction CONCAT = + (a, b) -> Objects.toString(a) + Objects.toString(b); + + private static final Consumer>> + PUT_IF_ABSENT = provAndParam -> + provAndParam.provider.putIfAbsent( + provAndParam.operationParameters.first, + provAndParam.operationParameters.second); + + private static final Consumer>>> COMPUTE_IF_PRESENT = + provAndParam -> provAndParam.provider.computeIfPresent( + provAndParam.operationParameters.first, + provAndParam.operationParameters.second); + + private static final Consumer>>> COMPUTE_IF_ABSENT = + provAndParam -> provAndParam.provider.computeIfAbsent( + provAndParam.operationParameters.first, + provAndParam.operationParameters.second); + + private static final Function TO_UPPER_CASE = + s -> Objects.toString(s).toUpperCase(); + + private static final Consumer>> + REPLACE_USING_KEY = provAndParam -> + provAndParam.provider.replace( + provAndParam.operationParameters.first, + provAndParam.operationParameters.second); + + private static final Consumer, String>>> + REPLACE_USING_KEY_AND_VALUE = provAndParam -> + provAndParam.provider.replace( + provAndParam.operationParameters.first.first, + provAndParam.operationParameters.first.second, + provAndParam.operationParameters.second); + + private static final Consumer>> REPLACE_ALL = + provAndParam -> provAndParam.provider.replaceAll( + provAndParam.operationParameters); + + private static final BiFunction CONCAT_IF_STARTING_WITH_CLASS = + (a, b) -> (Objects.toString(a).startsWith("class")) + ? Objects.toString(a) + Objects.toString(b) + : b; + + private static final Consumer, BiFunction>>> + MERGE = provAndParam -> provAndParam.provider.merge( + provAndParam.operationParameters.first.first, + provAndParam.operationParameters.first.second, + provAndParam.operationParameters.second); + + + + private static Map mapOf(String... elements) { + Map ret = new HashMap(); + for (int i = 0; i < elements.length; i += 2) { + ret.put(elements[i], elements[i + 1]); + } + return ret; + } + + + private void performHashMapOperationAndCheckResults( + Consumer> operation, + Map initialState, + A operationParameters, + Map expectedResult, + boolean mustChangeVersion) { + Provider p = new MockProvider("MockProvider"); + // Need to set as registered so that the security version will change on update. + p.setRegistered(); + int securityVersionBeforeOperation = Security.getVersion(); + p.putAll(initialState); + + // Perform the operation. + operation.accept(new ProviderAndOperationParameter(p, operationParameters)); + + // Check that elements are correctly mapped to services. + HashMap services = new HashMap(); + for (Provider.Service s : p.getServices()) { + services.put(s.getType() + "." + s.getAlgorithm(), s.getClassName()); + } + assertEquals(expectedResult.entrySet(), services.entrySet()); + + // Check that elements are in the provider hash map. + // The hash map in the provider has info other than services, include those in the + // expected results. + HashMap hashExpectedResult = new HashMap(); + hashExpectedResult.putAll(expectedResult); + hashExpectedResult.put("Provider.id info", p.getInfo()); + hashExpectedResult.put("Provider.id className", p.getClass().getName()); + hashExpectedResult.put("Provider.id version", String.valueOf(p.getVersion())); + hashExpectedResult.put("Provider.id name", p.getName()); + + assertEquals(hashExpectedResult.entrySet(), p.entrySet()); + + if (mustChangeVersion) { + assertTrue(securityVersionBeforeOperation != Security.getVersion()); + } + } + @SuppressWarnings("serial") private static class MockProvider extends Provider { public MockProvider(String name) { diff --git a/luni/src/test/java/libcore/java/security/SecureRandomTest.java b/luni/src/test/java/libcore/java/security/SecureRandomTest.java index 7eb3b45aa..39711a0f2 100644 --- a/luni/src/test/java/libcore/java/security/SecureRandomTest.java +++ b/luni/src/test/java/libcore/java/security/SecureRandomTest.java @@ -16,17 +16,13 @@ package libcore.java.security; -import java.security.NoSuchProviderException; import java.security.Provider; import java.security.SecureRandom; import java.security.Security; import java.util.Arrays; import java.util.Set; - import junit.framework.TestCase; -import dalvik.system.VMRuntime; - public class SecureRandomTest extends TestCase { private static final String EXPECTED_PROVIDER = "com.android.org.conscrypt.OpenSSLProvider"; @@ -115,63 +111,38 @@ public void testNewConstructors_Success() throws Exception { } /** - * http://b/28550092 : Removal of "Crypto" provider in N caused application compatibility - * issues for callers of SecureRandom. To improve compatibility the provider is not registered - * as a JCA Provider obtainable via Security.getProvider() but is made available for - * SecureRandom.getInstance() iff the application targets API <= 23. - */ - public void testCryptoProvider_withWorkaround_Success() throws Exception { - // Assert that SecureRandom is still using the default value. Sanity check. - assertEquals(SecureRandom.DEFAULT_SDK_TARGET_FOR_CRYPTO_PROVIDER_WORKAROUND, - SecureRandom.getSdkTargetForCryptoProviderWorkaround()); - - try { - // Modify the maximum target SDK to apply the workaround, thereby enabling the - // workaround for the current SDK and enabling it to be tested. - SecureRandom.setSdkTargetForCryptoProviderWorkaround( - VMRuntime.getRuntime().getTargetSdkVersion()); - - // Assert that the crypto provider is not installed... - assertNull(Security.getProvider("Crypto")); - SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto"); - assertNotNull(sr); - // ...but we can get a SecureRandom from it... - assertEquals("org.apache.harmony.security.provider.crypto.CryptoProvider", - sr.getProvider().getClass().getName()); - // ...yet it's not installed. So the workaround worked. - assertNull(Security.getProvider("Crypto")); - } finally { - // Reset the target SDK for the workaround to the default / real value. - SecureRandom.setSdkTargetForCryptoProviderWorkaround( - SecureRandom.DEFAULT_SDK_TARGET_FOR_CRYPTO_PROVIDER_WORKAROUND); + * Test that the strong instance is from OpenSSLProvider (as specified in security.properties) + * even if there are other providers installed. + */ + public void testGetInstanceStrong() throws Exception { + Provider openSSLProvider = null; + for (Provider p : Security.getProviders()) { + if (p.getClass().getName().equals("com.android.org.conscrypt.OpenSSLProvider")) { + openSSLProvider = p; + } + } + if (openSSLProvider == null) { + throw new IllegalStateException("OpenSSLProvider not found"); } - } - /** - * http://b/28550092 : Removal of "Crypto" provider in N caused application compatibility - * issues for callers of SecureRandom. To improve compatibility the provider is not registered - * as a JCA Provider obtainable via Security.getProvider() but is made available for - * SecureRandom.getInstance() iff the application targets API <= 23. - */ - public void testCryptoProvider_withoutWorkaround_Failure() throws Exception { - // Assert that SecureRandom is still using the default value. Sanity check. - assertEquals(SecureRandom.DEFAULT_SDK_TARGET_FOR_CRYPTO_PROVIDER_WORKAROUND, - SecureRandom.getSdkTargetForCryptoProviderWorkaround()); + // Default comes from the OpenSSLProvider + assertEquals(openSSLProvider, SecureRandom.getInstance("SHA1PRNG").getProvider()); + assertEquals(openSSLProvider, new SecureRandom().getProvider()); + + Provider weakProvider = new Provider("MockWeakSecureRandomProvider", 1.0, "For testing") { + }; + weakProvider.put("SecureRandom.SHA1PRNG", ProviderTest.SecureRandom1.class.getName()); + // Insert a different provider with highest priority. try { - // We set the limit SDK for the workaround at the previous one, indicating that the - // workaround shouldn't be in place. - SecureRandom.setSdkTargetForCryptoProviderWorkaround( - VMRuntime.getRuntime().getTargetSdkVersion() - 1); - - SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto"); - fail("Should throw " + NoSuchProviderException.class.getName()); - } catch(NoSuchProviderException expected) { - // The workaround doesn't work. As expected. + Security.insertProviderAt(weakProvider, 1); + // Default comes from the weak provider. + assertEquals(weakProvider, SecureRandom.getInstance("SHA1PRNG").getProvider()); + assertEquals(weakProvider, new SecureRandom().getProvider()); + // Strong SecureRandom comes from the OpenSSLProvider. + assertEquals(openSSLProvider, SecureRandom.getInstanceStrong().getProvider()); } finally { - // Reset the target SDK for the workaround to the default / real value. - SecureRandom.setSdkTargetForCryptoProviderWorkaround( - SecureRandom.DEFAULT_SDK_TARGET_FOR_CRYPTO_PROVIDER_WORKAROUND); + Security.removeProvider(weakProvider.getName()); } } } diff --git a/luni/src/test/java/libcore/java/security/SignatureTest.java b/luni/src/test/java/libcore/java/security/SignatureTest.java index 35712e087..055b8fe74 100644 --- a/luni/src/test/java/libcore/java/security/SignatureTest.java +++ b/luni/src/test/java/libcore/java/security/SignatureTest.java @@ -16,6 +16,7 @@ package libcore.java.security; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -78,6 +79,7 @@ public MockProvider(String name) { public void testSignature_getInstance_SuppliedProviderNotRegistered_Success() throws Exception { Provider mockProvider = new MockProvider("MockProvider") { + @Override public void setup() { put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName()); } @@ -92,6 +94,7 @@ public void setup() { public void testSignature_getInstance_DoesNotSupportKeyClass_Success() throws Exception { Provider mockProvider = new MockProvider("MockProvider") { + @Override public void setup() { put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName()); put("Signature.FOO SupportedKeyClasses", "None"); @@ -116,6 +119,7 @@ public void setup() { public void testSignature_init_DoesNotSupportKeyClass_throwsInvalidKeyException() throws Exception { Provider mockProvider = new MockProvider("MockProvider") { + @Override public void setup() { put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName()); put("Signature.FOO SupportedKeyClasses", "None"); @@ -136,6 +140,7 @@ public void setup() { public void testSignature_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success() throws Exception { Provider mockProvider = new MockProvider("MockProvider") { + @Override public void setup() { put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName()); } @@ -145,6 +150,7 @@ public void setup() { try { { Provider mockProvider2 = new MockProvider("MockProvider") { + @Override public void setup() { put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName()); } @@ -159,18 +165,21 @@ public void setup() { public void testSignature_getInstance_DelayedInitialization_KeyType() throws Exception { Provider mockProviderSpecific = new MockProvider("MockProviderSpecific") { + @Override public void setup() { put("Signature.FOO", MockSignatureSpi.SpecificKeyTypes.class.getName()); put("Signature.FOO SupportedKeyClasses", MockPrivateKey.class.getName()); } }; Provider mockProviderSpecific2 = new MockProvider("MockProviderSpecific2") { + @Override public void setup() { put("Signature.FOO", MockSignatureSpi.SpecificKeyTypes2.class.getName()); put("Signature.FOO SupportedKeyClasses", MockPrivateKey2.class.getName()); } }; Provider mockProviderAll = new MockProvider("MockProviderAll") { + @Override public void setup() { put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName()); } @@ -238,32 +247,28 @@ protected MySignature(String algorithm) { @Override protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { - throw new UnsupportedOperationException(); } @Override protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { - throw new UnsupportedOperationException(); } @Override protected void engineUpdate(byte b) throws SignatureException { - throw new UnsupportedOperationException(); } @Override protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { - throw new UnsupportedOperationException(); } @Override protected byte[] engineSign() throws SignatureException { - throw new UnsupportedOperationException(); + return new byte[10]; } @Override protected boolean engineVerify(byte[] sigBytes) throws SignatureException { - throw new UnsupportedOperationException(); + return true; } @Override @@ -277,8 +282,156 @@ protected Object engineGetParameter(String param) throws InvalidParameterExcepti } } + public void testSignature_signArray_nullArray_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.sign(null /* outbuf */, 1 /* offset */, 1 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_signArray_negativeOffset_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.sign(new byte[4], -1 /* offset */, 1 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_signArray_negativeLength_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.sign(new byte[4], 1 /* offset */ , -1 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_signArray_invalidLengths_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + // Start at offset 3 with length 2, thus attempting to overread from an array of size 4. + s.sign(new byte[4], 3 /* offset */ , 2 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + private static PublicKey createPublicKey() throws Exception { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(PK_BYTES); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePublic(keySpec); + } + + public void testSignature_verifyArray_nullArray_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.initVerify(createPublicKey()); + s.verify(null /* outbuf */, 1 /* offset */, 1 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_verifyArray_negativeOffset_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.initVerify(createPublicKey()); + s.verify(new byte[4], -1 /* offset */, 1 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_verifyArray_negativeLength_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.initVerify(createPublicKey()); + s.verify(new byte[4], 1 /* offset */ , -1 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_verifyArray_invalidLengths_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.initVerify(createPublicKey()); + // Start at offset 3 with length 2, thus attempting to overread from an array of size 4. + s.verify(new byte[4], 3 /* offset */ , 2 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_verifyArray_correctParameters_ok() throws Exception { + Signature s = new MySignature("FOO"); + s.initVerify(createPublicKey()); + // Start at offset 3 with length 2, thus attempting to overread from an array of size 4. + s.verify(new byte[4], 1 /* offset */, 2 /* length */); + } + + public void testSignature_updateArray_nullArray_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.initVerify(createPublicKey()); + s.update(null /* outbuf */, 1 /* offset */, 1 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_updateArray_negativeOffset_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.initVerify(createPublicKey()); + s.update(new byte[4], -1 /* offset */, 1 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_updateArray_negativeLength_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.initVerify(createPublicKey()); + s.update(new byte[4], 1 /* offset */ , -1 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_updateArray_invalidLengths_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.initVerify(createPublicKey()); + // Start at offset 3 with length 2, thus attempting to overread from an array of size 4. + s.update(new byte[4], 3 /* offset */ , 2 /* length */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testSignature_updateArray_wrongState_throws() throws Exception { + try { + Signature s = new MySignature("FOO"); + s.update(new byte[4], 0 /* offset */ , 1 /* length */); + fail(); + } catch (SignatureException expected) { + } + } + + public void testSignature_updateArray_correctStateAndParameters_ok() throws Exception { + Signature s = new MySignature("FOO"); + s.initVerify(createPublicKey()); + s.update(new byte[4], 0 /* offset */ , 1 /* length */); + } + public void testSignature_getProvider_Subclass() throws Exception { Provider mockProviderNonSpi = new MockProvider("MockProviderNonSpi") { + @Override public void setup() { put("Signature.FOO", MySignature.class.getName()); } @@ -2731,7 +2884,8 @@ public void testVerify_NONEwithRSA_Key_WrongSignature_Failure() throws Exception Signature sig = Signature.getInstance("NONEwithRSA"); sig.initVerify(pubKey); sig.update(Vector1Data); - assertFalse("Invalid signature must not verify", sig.verify("Invalid".getBytes())); + assertFalse("Invalid signature must not verify", + sig.verify("Invalid".getBytes(UTF_8))); } public void testSign_NONEwithRSA_Key_DataTooLarge_Failure() throws Exception { @@ -2830,7 +2984,8 @@ public void testVerify_NONEwithRSA_Key_SignatureTooSmall_Failure() throws Except sig.initVerify(pubKey); sig.update(Vector1Data); - assertFalse("Invalid signature should not verify", sig.verify("Invalid sig".getBytes())); + assertFalse("Invalid signature should not verify", + sig.verify("Invalid sig".getBytes(UTF_8))); } public void testVerify_NONEwithRSA_Key_SignatureTooLarge_Failure() throws Exception { @@ -3156,13 +3311,13 @@ public void testArbitraryCurve() throws Exception { Signature ecdsaVerify = Signature.getInstance("SHA1withECDSA"); ecdsaVerify.initVerify(pub); - ecdsaVerify.update("Satoshi Nakamoto".getBytes("UTF-8")); + ecdsaVerify.update("Satoshi Nakamoto".getBytes(UTF_8)); boolean result = ecdsaVerify.verify(SIGNATURE); assertEquals(true, result); ecdsaVerify = Signature.getInstance("SHA1withECDSA"); ecdsaVerify.initVerify(pub); - ecdsaVerify.update("Not Satoshi Nakamoto".getBytes("UTF-8")); + ecdsaVerify.update("Not Satoshi Nakamoto".getBytes(UTF_8)); result = ecdsaVerify.verify(SIGNATURE); assertEquals(false, result); } diff --git a/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java b/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java index a3a721ab9..7b82cc6f0 100644 --- a/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java +++ b/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java @@ -16,6 +16,8 @@ package libcore.java.security.cert; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.android.org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; import com.android.org.bouncycastle.asn1.x509.BasicConstraints; import com.android.org.bouncycastle.asn1.x509.Extension; @@ -50,6 +52,7 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.Iterator; +import java.util.Locale; import java.util.List; import java.util.TimeZone; @@ -103,7 +106,7 @@ public class CertificateFactoryTest extends TestCase { + "-----END CERTIFICATE-----\r\n"; private static final byte[] VALID_CERTIFICATE_PEM_HEADER = "-----BEGIN CERTIFICATE-----\n" - .getBytes(); + .getBytes(UTF_8); private static final byte[] VALID_CERTIFICATE_PEM_DATA = ("MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM" @@ -122,10 +125,10 @@ public class CertificateFactoryTest extends TestCase { + "ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF" + "AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5" + "u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6" - + "z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==").getBytes(); + + "z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==").getBytes(UTF_8); private static final byte[] VALID_CERTIFICATE_PEM_FOOTER = "\n-----END CERTIFICATE-----\n" - .getBytes(); + .getBytes(UTF_8); private static final String INVALID_CERTIFICATE_PEM = "-----BEGIN CERTIFICATE-----\n" @@ -183,19 +186,19 @@ public void test_generateCertificate() throws Exception { private void test_generateCertificate(CertificateFactory cf) throws Exception { { - byte[] valid = VALID_CERTIFICATE_PEM.getBytes(); + byte[] valid = VALID_CERTIFICATE_PEM.getBytes(UTF_8); Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid)); assertNotNull(c); } { - byte[] valid = VALID_CERTIFICATE_PEM_CRLF.getBytes(); + byte[] valid = VALID_CERTIFICATE_PEM_CRLF.getBytes(UTF_8); Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid)); assertNotNull(c); } try { - byte[] invalid = INVALID_CERTIFICATE_PEM.getBytes(); + byte[] invalid = INVALID_CERTIFICATE_PEM.getBytes(UTF_8); cf.generateCertificate(new ByteArrayInputStream(invalid)); fail(); } catch (CertificateException expected) { @@ -263,7 +266,7 @@ private void test_generateCertificate_InputStream_InvalidStart_Failure(Certifica throws Exception { try { Certificate c = cf.generateCertificate(new ByteArrayInputStream( - "-----BEGIN CERTIFICATE-----".getBytes())); + "-----BEGIN CERTIFICATE-----".getBytes(UTF_8))); if (!"BC".equals(cf.getProvider().getName())) { fail("should throw CertificateException: " + cf.getProvider().getName()); } @@ -277,7 +280,7 @@ private void test_generateCertificate_InputStream_InvalidStart_Failure(Certifica private void test_generateCertificate_InputStream_Offset_Correct(CertificateFactory cf) throws Exception { - byte[] valid = VALID_CERTIFICATE_PEM.getBytes(); + byte[] valid = VALID_CERTIFICATE_PEM.getBytes(UTF_8); byte[] doubleCertificateData = new byte[valid.length * 2]; System.arraycopy(valid, 0, doubleCertificateData, 0, valid.length); diff --git a/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java b/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java index 161112044..7178a59d9 100644 --- a/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java +++ b/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java @@ -16,6 +16,10 @@ package libcore.java.security.cert; +import static java.nio.charset.StandardCharsets.UTF_8; + +import sun.security.provider.X509Factory; +import sun.security.x509.X509CRLImpl; import tests.support.resource.Support_Resources; import java.io.BufferedReader; @@ -28,7 +32,6 @@ import java.security.InvalidKeyException; import java.security.Provider; import java.security.Security; -import java.security.SignatureException; import java.security.cert.CRL; import java.security.cert.CRLReason; import java.security.cert.CertificateFactory; @@ -127,7 +130,7 @@ private Map getCrlDates(String name) throws Exception { final InputStream ris = Support_Resources.getStream(name); try { - final BufferedReader buf = new BufferedReader(new InputStreamReader(ris)); + final BufferedReader buf = new BufferedReader(new InputStreamReader(ris, UTF_8)); String line; while ((line = buf.readLine()) != null) { @@ -146,6 +149,14 @@ private Map getCrlDates(String name) throws Exception { } } + public void test_X509CRLImpl_verify() throws Exception { + CertificateFactory f = CertificateFactory.getInstance("X.509"); + X509CRL crlRsa = getCRL(f, CRL_RSA); + X509CRLImpl interned = X509Factory.intern(crlRsa); + X509Certificate caCert = getCertificate(f, CERT_CRL_CA); + interned.verify(caCert.getPublicKey(), f.getProvider()); + } + public void test_Provider() throws Exception { final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream(); PrintStream out = new PrintStream(errBuffer); @@ -184,14 +195,45 @@ private void verify(CertificateFactory f) throws Exception { X509CRL crlRsa = getCRL(f, CRL_RSA); X509Certificate caCert = getCertificate(f, CERT_CRL_CA); + + // Test the "verify" method that doesn't specify the provider. crlRsa.verify(caCert.getPublicKey()); + // Test the "verify" method that does specify the provider. + try { + crlRsa.verify(caCert.getPublicKey(), f.getProvider()); + } catch (UnsupportedOperationException unsupportedOperationException) { + // TODO(31294527): X590CRL objects from AndroidOpenSSL do not have this method. + // The "default" implementation from OpenJDK results in an infinite loop, so in libcore + // we throw an UnsupportedOperationException instead. + if (!f.getProvider().getName().equals("AndroidOpenSSL")) { + throw unsupportedOperationException; + } + } + X509Certificate dsaCert = getCertificate(f, CERT_DSA); + + // Test the "verify" method that does specify the provider. try { crlRsa.verify(dsaCert.getPublicKey()); fail("should not verify using incorrect key type"); } catch (InvalidKeyException expected) { } + + // Test the "verify" method that does specify the provider. + try { + crlRsa.verify(dsaCert.getPublicKey(), f.getProvider()); + fail("should not verify using incorrect key type"); + } catch (InvalidKeyException expected) { + + } catch (UnsupportedOperationException unsupportedOperationException) { + // TODO(31294527): X590CRL objects from AndroidOpenSSL do not have this method. + // The "default" implementation from OpenJDK results in an infinite loop, so in libcore + // we throw an UnsupportedOperationException instead. + if (!f.getProvider().getName().equals("AndroidOpenSSL")) { + throw unsupportedOperationException; + } + } } private void getType(CertificateFactory f) throws Exception { diff --git a/luni/src/test/java/libcore/java/security/cert/X509CertSelectorTest.java b/luni/src/test/java/libcore/java/security/cert/X509CertSelectorTest.java index 4a5658c6c..cbaadd48d 100644 --- a/luni/src/test/java/libcore/java/security/cert/X509CertSelectorTest.java +++ b/luni/src/test/java/libcore/java/security/cert/X509CertSelectorTest.java @@ -67,13 +67,21 @@ public void testMatchMaskedIpv4NameConstraint() throws Exception { X509CertSelector certSelector = new X509CertSelector(); certSelector.addPathToName(GeneralName.iPAddress, "127.0.0.1"); + // This constraint matches 127.0.0.1/255.255.255.255 aka 127.0.0.1 byte[] directMatch = { 127, 0, 0, 1, -1, -1, -1, -1 }; assertTrue(certSelector.match(newCertWithNameConstraint(directMatch, excluded))); - byte[] noMatch = { 127, 0, 0, 2, -1, -1, -1, 127 }; + // This constraint matches 127.0.0.2/255.255.255.255 aka 127.0.0.2 + byte[] noMatch = { 127, 0, 0, 2, -1, -1, -1, -1 }; assertFalse(certSelector.match(newCertWithNameConstraint(noMatch, excluded))); - // TODO: test that requires mask to match + // This constraint matches 127.0.0.0/255.255.255.255 aka 127.0.0.0 + byte[] subnetWithNoMask = { 127, 0, 0, 0, -1, -1, -1, -1 }; + assertFalse(certSelector.match(newCertWithNameConstraint(subnetWithNoMask, excluded))); + + // This constraint matches 127.0.0.0/255.255.255.0 aka 127.0.0.0/24 + byte[] maskedMatch = { 127, 0, 0, 0, -1, -1, -1, 0 }; + assertTrue(certSelector.match(newCertWithNameConstraint(maskedMatch, excluded))); } public void testMatchMaskedIpv6NameConstraint() throws Exception { @@ -84,19 +92,33 @@ public void testMatchMaskedIpv6NameConstraint() throws Exception { X509CertSelector certSelector = new X509CertSelector(); certSelector.addPathToName(GeneralName.iPAddress, "1::1"); + // This constraint matches 1::1/128 aka 1::1 byte[] directMatch = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 127 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; assertTrue(certSelector.match(newCertWithNameConstraint(directMatch, excluded))); + // This constraint matches 1::2/128 aka 1::2 byte[] noMatch = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 127 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; assertFalse(certSelector.match(newCertWithNameConstraint(noMatch, excluded))); - // TODO: test that requires mask to match + // This constraint matches 1::/128 aka 1:: + byte[] subnetWithNoMask = { + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + assertFalse(certSelector.match(newCertWithNameConstraint(subnetWithNoMask, excluded))); + + // This constraint matches 1::/120 + byte[] maskedMatch = { + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0 + }; + assertTrue(certSelector.match(newCertWithNameConstraint(maskedMatch, excluded))); } public void testMatchMalformedSubjectAlternativeName() throws Exception { diff --git a/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java b/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java index f1cd4ff65..0b0154180 100644 --- a/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java +++ b/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java @@ -16,7 +16,7 @@ package libcore.java.security.cert; -import tests.support.resource.Support_Resources; +import static java.nio.charset.StandardCharsets.UTF_8; import java.io.BufferedInputStream; import java.io.BufferedReader; @@ -56,11 +56,10 @@ import java.util.List; import java.util.Locale; import java.util.Set; - import javax.security.auth.x500.X500Principal; - import junit.framework.TestCase; import libcore.java.security.StandardNames; +import tests.support.resource.Support_Resources; public class X509CertificateTest extends TestCase { private Provider[] mX509Providers; @@ -173,7 +172,7 @@ private Date[] getRsaCertificateDates() throws Exception { final SimpleDateFormat sdf = new SimpleDateFormat("MMM dd HH:mm:ss yyyy zzz", Locale.US); - final BufferedReader buf = new BufferedReader(new InputStreamReader(ris)); + final BufferedReader buf = new BufferedReader(new InputStreamReader(ris, UTF_8)); String line = buf.readLine(); int index = line.indexOf('='); assertEquals("notBefore", line.substring(0, index)); @@ -199,7 +198,7 @@ private Date[] getRsaCertificateDates() throws Exception { private BigInteger getRsaCertificateSerial() throws Exception { final InputStream ris = Support_Resources.getStream("x509/cert-rsa-serial.txt"); try { - final BufferedReader buf = new BufferedReader(new InputStreamReader(ris)); + final BufferedReader buf = new BufferedReader(new InputStreamReader(ris, UTF_8)); String line = buf.readLine(); int index = line.indexOf('='); diff --git a/luni/src/test/java/libcore/java/sql/OldConnectionTest.java b/luni/src/test/java/libcore/java/sql/OldConnectionTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/libcore/java/sql/OldPreparedStatementTest.java b/luni/src/test/java/libcore/java/sql/OldPreparedStatementTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/libcore/java/sql/OldResultSetMetaDataTest.java b/luni/src/test/java/libcore/java/sql/OldResultSetMetaDataTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/libcore/java/sql/OldSQLTest.java b/luni/src/test/java/libcore/java/sql/OldSQLTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/libcore/java/sql/OldStatementTest.java b/luni/src/test/java/libcore/java/sql/OldStatementTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/libcore/java/text/ChoiceFormatTest.java b/luni/src/test/java/libcore/java/text/ChoiceFormatTest.java new file mode 100644 index 000000000..419ef34c8 --- /dev/null +++ b/luni/src/test/java/libcore/java/text/ChoiceFormatTest.java @@ -0,0 +1,75 @@ +package libcore.java.text; + +import java.text.ChoiceFormat; +import junit.framework.TestCase; + +/** + */ +public class ChoiceFormatTest extends TestCase { + + /** + * Limits for {@link ChoiceFormat}, will be modified by some tests to ensure that ChoiceFormat + * stores a copy of the arrays provided. + */ + private final double[] limits = new double[] { 0, 1, 2, 3, 4 }; + + /** + * Format strings for {@link ChoiceFormat}, will be modified by some tests to ensure that + * ChoiceFormat stores a copy of the arrays provided. + */ + private final String[] formats = new String[] { "zero", "one", "a couple", "a few", "some" }; + + public void testConstructor_doubleArray_StringArray() throws Exception { + ChoiceFormat format = new ChoiceFormat(limits, formats); + + verifyChoiceFormatCopiesSuppliedArrays(format); + } + + public void testSetChoices() throws Exception { + ChoiceFormat format = new ChoiceFormat(new double[] { 0 }, new String[] { "" }); + assertEquals("", format.format(1.4)); + + // Change the limits. + format.setChoices(limits, formats); + + verifyChoiceFormatCopiesSuppliedArrays(format); + } + + private void verifyChoiceFormatCopiesSuppliedArrays(ChoiceFormat format) { + assertEquals("one", format.format(1.4)); + + // Change the formats array and make sure that it doesn't affect the ChoiceFormat. + formats[1] = "uno"; + assertEquals("ChoiceFormat doesn't make defensive copies of formats array", + "one", format.format(1.4)); + + // Change the limits array and make sure that it doesn't affect the ChoiceFormat. + limits[2] = 1.2; + assertEquals("ChoiceFormat doesn't make defensive copies of limits array", + "one", format.format(1.4)); + } + + public void testGetLimits() throws Exception { + ChoiceFormat format = new ChoiceFormat(limits, formats); + assertEquals("some", format.format(100)); + + // Get the limits array, change the contents and make sure it doesn't affect the behavior + // of the format. + double[] copiedLimits = format.getLimits(); + copiedLimits[4] = 200; + assertEquals("ChoiceFormat doesn't return a copy of choiceLimits array", + "some", format.format(100)); + } + + public void testGetFormats() throws Exception { + ChoiceFormat format = new ChoiceFormat(limits, formats); + assertEquals("zero", format.format(-4)); + + // Get the formats array, change the contents and make sure it doesn't affect the behavior + // of the format. + Object[] copiedFormats = format.getFormats(); + copiedFormats[0] = "none or less"; + assertEquals("ChoiceFormat doesn't return a copy of choiceFormats array", + "zero", format.format(-4)); + } +} diff --git a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java index 0c97f34af..faf4213a5 100644 --- a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java +++ b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java @@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.lang.reflect.Field; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -161,4 +162,43 @@ public void test_getZoneStrings_Apia() throws Exception { } } } + + public void test_setZoneStrings_checks_dimensions() throws Exception { + DateFormatSymbols dfs = DateFormatSymbols.getInstance(); + String[][] zoneStrings = dfs.getZoneStrings(); + zoneStrings[0] = new String[] { "id_only " }; + try { + dfs.setZoneStrings(zoneStrings); + fail("No IllegalArgumentException when setting incorrect zoneStrings"); + } catch (IllegalArgumentException e) { + // expected + } + } + + public void test_zoneStrings_are_lazy() throws Exception { + DateFormatSymbols dfs = DateFormatSymbols.getInstance(); + + assertFalse("Newly created DFS should have no zoneStrings", hasZoneStringsFieldValue(dfs)); + dfs.hashCode(); + assertFalse("hashCode() should not need zoneStrings", hasZoneStringsFieldValue(dfs)); + DateFormatSymbols otherDfs = DateFormatSymbols.getInstance(); + dfs.equals(otherDfs); + assertFalse("equals() should usually not need zoneStrings", hasZoneStringsFieldValue(dfs)); + otherDfs.getZoneStrings(); + assertTrue("getZoneStrings() needs zoneStrings", hasZoneStringsFieldValue(otherDfs)); + otherDfs.setZoneStrings(otherDfs.getZoneStrings()); + dfs.equals(otherDfs); + assertTrue("equals() needs zoneStrings when other object has user-provided values", + hasZoneStringsFieldValue(dfs)); + } + + /** + * Return {@code true} iff {@code dfs} has a non-null {@code zoneStrings}. This introspection is + * necessary, because as a lazy field it having a value should not otherwise be observable. + */ + private static boolean hasZoneStringsFieldValue(DateFormatSymbols dfs) throws Exception { + Field field = DateFormatSymbols.class.getDeclaredField("zoneStrings"); + field.setAccessible(true); + return field.get(dfs) != null; + } } diff --git a/luni/src/test/java/libcore/java/text/DateFormatTest.java b/luni/src/test/java/libcore/java/text/DateFormatTest.java new file mode 100644 index 000000000..8e215ab89 --- /dev/null +++ b/luni/src/test/java/libcore/java/text/DateFormatTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.text; + +import junit.framework.TestCase; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public class DateFormatTest extends TestCase { + + // Regression test for http://b/31762542. If this test fails it implies that changes to + // DateFormat.is24Hour will not be effective. + public void testIs24Hour_notCached() throws Exception { + Boolean originalIs24Hour = DateFormat.is24Hour; + try { + // These tests hardcode expectations for Locale.US. + DateFormat.is24Hour = null; // null == locale default (12 hour for US) + checkTimePattern(DateFormat.SHORT, "h:mm a"); + checkTimePattern(DateFormat.MEDIUM, "h:mm:ss a"); + + DateFormat.is24Hour = true; // Explicit 24 hour. + checkTimePattern(DateFormat.SHORT, "HH:mm"); + checkTimePattern(DateFormat.MEDIUM, "HH:mm:ss"); + + DateFormat.is24Hour = false; // Explicit 12 hour. + checkTimePattern(DateFormat.SHORT, "h:mm a"); + checkTimePattern(DateFormat.MEDIUM, "h:mm:ss a"); + } finally { + DateFormat.is24Hour = originalIs24Hour; + } + } + + private static void checkTimePattern(int style, String expectedPattern) { + final Locale locale = Locale.US; + final Date current = new Date(1468250177000L); // 20160711 15:16:17 GMT + DateFormat format = DateFormat.getTimeInstance(style, locale); + String actualDateString = format.format(current); + SimpleDateFormat sdf = new SimpleDateFormat(expectedPattern, locale); + String expectedDateString = sdf.format(current); + assertEquals(expectedDateString, actualDateString); + } +} diff --git a/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java b/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java index 5f440a4c9..26afa1d1b 100644 --- a/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java +++ b/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java @@ -110,10 +110,26 @@ public void testSerializationOfMultiCharNegativeAndPercentage() throws Exception // http://b/18785260 public void testMultiCharMinusSignAndPercentage() { - DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.forLanguageTag("ar-AR")); + DecimalFormatSymbols dfs; + + // There have during the years been numerous bugs and workarounds around the decimal format + // symbols used for Arabic and Farsi. Most of the problems have had to do with bidi control + // characters and the Unicode bidi algorithm, which have not worked well together with code + // assuming that these symbols can be represented as a single Java char. + // + // This test case exists to verify that java.text.DecimalFormatSymbols in Android gets some + // kind of sensible values for these symbols (and not, as bugs have caused in the past, + // empty strings or only bidi control characters without any actual symbols). + // + // It is expected that the symbols may change with future CLDR updates. + + dfs = new DecimalFormatSymbols(Locale.forLanguageTag("ar")); + assertEquals('%', dfs.getPercent()); + assertEquals('-', dfs.getMinusSign()); + dfs = new DecimalFormatSymbols(Locale.forLanguageTag("fa")); assertEquals('٪', dfs.getPercent()); - assertEquals('-', dfs.getMinusSign()); + assertEquals('−', dfs.getMinusSign()); } diff --git a/luni/src/test/java/libcore/java/text/OldBidiTest.java b/luni/src/test/java/libcore/java/text/OldBidiTest.java index fbf68ea71..fe8b6cb4f 100644 --- a/luni/src/test/java/libcore/java/text/OldBidiTest.java +++ b/luni/src/test/java/libcore/java/text/OldBidiTest.java @@ -17,6 +17,7 @@ package libcore.java.text; +import java.text.AttributedCharacterIterator; import java.text.Bidi; import junit.framework.TestCase; @@ -192,4 +193,16 @@ public void testConstructorIllegalArguments() { } } + // http://b/30652865 + public void testUnicode9EmojisAreLtrNeutral() { + String callMeHand = "\uD83E\uDD19"; // U+1F919 in UTF-16 + String hebrewAndEmoji = "\u05e9\u05dc" + callMeHand + "\u05d5\u05dd"; + String latinAndEmoji = "Hel" + callMeHand + "lo"; + Bidi hebrew = new Bidi(hebrewAndEmoji, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + assertFalse("Hebrew bidi is mixed: " + hebrew, hebrew.isMixed()); + assertTrue("Hebrew bidi is not right to left: " + hebrew, hebrew.isRightToLeft()); + Bidi latin = new Bidi(latinAndEmoji, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + assertFalse("Latin bidi is mixed: " + latin, latin.isMixed()); + assertTrue("latin bidi is not left to right: " + latin, latin.isLeftToRight()); + } } diff --git a/luni/src/test/java/libcore/java/text/OldNumberFormatTest.java b/luni/src/test/java/libcore/java/text/OldNumberFormatTest.java index 3c86bda10..ad7ef0bdd 100644 --- a/luni/src/test/java/libcore/java/text/OldNumberFormatTest.java +++ b/luni/src/test/java/libcore/java/text/OldNumberFormatTest.java @@ -230,7 +230,7 @@ public void test_formatLdouble() { out = nf1.format(5.0); assertEquals("Wrong result for for double: " + out, "5", out.toString()); - // END android-changed + // END Android-changed } public void test_formatLlong() { @@ -258,7 +258,7 @@ public void test_formatLlong() { out = nf1.format(0); assertEquals("Wrong result for for double: " + out, "0", out.toString()); - // END android-changed + // END Android-changed } public void test_getAvailableLocales() { @@ -315,9 +315,9 @@ public void test_getCurrencyInstanceLjava_util_Locale() { Locale atLocale = new Locale("de", "AT"); format = NumberFormat.getCurrencyInstance(atLocale); - // BEGIN android-changed: ICU uses non-breaking space after the euro sign; the RI uses ' '. + // BEGIN Android-changed: ICU uses non-breaking space after the euro sign; the RI uses ' '. assertEquals("\u20ac\u00a035,76", format.format(35.76)); - assertEquals("\u20ac\u00a0123\u00a0456,79", format.format(123456.789)); + assertEquals("\u20ac\u00a0123.456,79", format.format(123456.789)); assertEquals("\u20ac\u00a00,10", format.format(0.1)); assertEquals("\u20ac\u00a01,00", format.format(0.999)); try { diff --git a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java index fe4e56737..20fa986d6 100644 --- a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java +++ b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java @@ -17,6 +17,7 @@ package libcore.java.text; import java.text.DateFormat; +import java.text.DateFormatSymbols; import java.text.ParseException; import java.text.ParsePosition; import java.text.SimpleDateFormat; @@ -231,6 +232,9 @@ private static Calendar parseDate(Locale l, String fmt, String value, TimeZone t if (d == null) { fail(pp.toString()); } + if (pp.getIndex() != value.length()) { + fail("Value " + value + " must be fully consumed: " + pp.toString()); + } Calendar c = Calendar.getInstance(tz); c.setTime(d); return c; @@ -472,4 +476,111 @@ public void testTimeZoneNotChangedByParse() throws Exception { df.parse("22 Jul 1977 12:23:45 HST"); assertEquals(UTC, df.getTimeZone()); } + + public void testZoneStringsUsedForParsingWhenPresent() throws ParseException { + DateFormatSymbols symbols = DateFormatSymbols.getInstance(Locale.ENGLISH); + String[][] zoneStrings = symbols.getZoneStrings(); + TimeZone tz = TimeZone.getTimeZone(zoneStrings[0][0]); + zoneStrings[0][1] = "CustomTimeZone"; + symbols.setZoneStrings(zoneStrings); + + SimpleDateFormat sdf = new SimpleDateFormat("dd MM yyyy HH:mm zzz", symbols); + + Date gmtDate = sdf.parse("1 1 2000 12:00 GMT"); + Date customDate = sdf.parse("1 1 2000 12:00 CustomTimeZone"); + assertEquals(tz.getOffset(gmtDate.getTime()), customDate.getTime() - gmtDate.getTime()); + } + + public void testTimeZoneFormattingRespectsSetZoneStrings() throws ParseException { + DateFormatSymbols symbols = DateFormatSymbols.getInstance(Locale.ENGLISH); + String[][] zoneStrings = symbols.getZoneStrings(); + TimeZone tz = TimeZone.getTimeZone(zoneStrings[0][0]); + String originalTzName = zoneStrings[0][1]; + symbols.setZoneStrings(zoneStrings); + SimpleDateFormat sdf = new SimpleDateFormat("zzzz", symbols); + sdf.setTimeZone(tz); + + // just re-setting the default values + assertEquals(originalTzName, sdf.format(new Date(1376927400000L))); + + // providing a custom name + zoneStrings[0][1] = "CustomTimeZone"; + symbols.setZoneStrings(zoneStrings); + sdf = new SimpleDateFormat("zzzz", symbols); + sdf.setTimeZone(tz); + assertEquals("CustomTimeZone", sdf.format(new Date(1376927400000L))); + + // setting the name to null should format as GMT[+-]... + zoneStrings[0][1] = null; + symbols.setZoneStrings(zoneStrings); + sdf = new SimpleDateFormat("zzzz", symbols); + sdf.setTimeZone(tz); + assertTrue(sdf.format(new Date(1376927400000L)).startsWith("GMT")); + } + + // http://b/30323478 + public void testStandaloneWeekdayParsing() throws Exception { + Locale fi = new Locale("fi"); // Finnish has separate standalone weekday names + // tiistaina = Tuesday (regular) + // tiistai = Tuesday (standalone) + assertEquals(Calendar.TUESDAY, + parseDateUtc(fi, "cccc yyyy", "tiistai 2000").get(Calendar.DAY_OF_WEEK)); + assertEquals(Calendar.TUESDAY, + parseDateUtc(fi, "EEEE yyyy", "tiistaina 2000").get(Calendar.DAY_OF_WEEK)); + assertCannotParse(fi, "cccc yyyy", "tiistaina 2000"); + assertCannotParse(fi, "EEEE yyyy", "tiistai 2000"); + } + + // http://b/30323478 + public void testStandaloneWeekdayFormatting() throws Exception { + Locale fi = new Locale("fi"); // Finnish has separate standalone weekday names + assertEquals("torstai", formatDateUtc(fi, "cccc")); + assertEquals("torstaina", formatDateUtc(fi, "EEEE")); + } + + public void testDayNumberOfWeek() throws Exception { + Locale en = Locale.ENGLISH; + Locale pl = new Locale("pl"); + + assertEquals("4", formatDateUtc(en, "u")); + assertEquals("04", formatDateUtc(en, "uu")); + assertEquals("4", formatDateUtc(pl, "u")); + assertEquals("04", formatDateUtc(pl, "uu")); + + assertEquals(Calendar.THURSDAY, parseDateUtc(en, "u", "4").get(Calendar.DAY_OF_WEEK)); + assertEquals(Calendar.MONDAY, parseDateUtc(en, "uu", "1").get(Calendar.DAY_OF_WEEK)); + } + + // http://b/20879084 + public void testFormatUtc() { + DateFormat dateFormat = new SimpleDateFormat("z", Locale.ENGLISH); + dateFormat.setTimeZone(UTC); + assertEquals("UTC", dateFormat.format(new Date(0))); + } + + // http://b/35134326 + public void testTimeZoneParsingErrorIndex() { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy z", Locale.ENGLISH); + + checkTimeZoneParsingErrorIndex(dateFormat); + } + + // http://b/35134326 + public void testTimeZoneParsingErrorIndexWithZoneStrings() { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy z", Locale.ENGLISH); + // Force legacy code path by using zone strings. + DateFormatSymbols dfs = dateFormat.getDateFormatSymbols(); + dfs.setZoneStrings(dfs.getZoneStrings()); + dateFormat.setDateFormatSymbols(dfs); + + checkTimeZoneParsingErrorIndex(dateFormat); + } + + private void checkTimeZoneParsingErrorIndex(SimpleDateFormat dateFormat) { + ParsePosition pos = new ParsePosition(0); + Date parsed; + parsed = dateFormat.parse("2000 foobar", pos); + assertNull(parsed); + assertEquals("Wrong error index", 5, pos.getErrorIndex()); + } } diff --git a/luni/src/test/java/libcore/java/time/DateTimeExceptionTest.java b/luni/src/test/java/libcore/java/time/DateTimeExceptionTest.java new file mode 100644 index 000000000..3b7255f4d --- /dev/null +++ b/luni/src/test/java/libcore/java/time/DateTimeExceptionTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.DateTimeException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +/** + * Tests for {@link DateTimeException}. + */ +public class DateTimeExceptionTest { + + @Test + public void test_constructor_message() { + DateTimeException ex = new DateTimeException("message"); + assertEquals("message", ex.getMessage()); + assertNull(ex.getCause()); + + } + + @Test + public void test_constructor_message_cause() { + Throwable cause = new Exception(); + DateTimeException ex = new DateTimeException("message", cause); + assertEquals("message", ex.getMessage()); + assertSame(cause, ex.getCause()); + } +} diff --git a/luni/src/test/java/libcore/java/time/DurationTest.java b/luni/src/test/java/libcore/java/time/DurationTest.java new file mode 100644 index 000000000..574a1d5b1 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/DurationTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.Year; +import java.time.YearMonth; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.chrono.MinguoChronology; +import java.time.temporal.Temporal; +import java.time.temporal.UnsupportedTemporalTypeException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +/** + * Additional tests for {@link Duration}. + * + * @see tck.java.time.TCKDuration + * @see test.java.time.TestDuration + */ +public class DurationTest { + + // Hardcoded maximum representable duration. + public static final Duration MAX_DURATION = Duration.ofSeconds(Long.MAX_VALUE, 999_999_999); + + @Test + public void test_addTo() { + assertSame(Instant.EPOCH, Duration.ZERO.addTo(Instant.EPOCH)); + + // These tests are a little tautological, but since Duration.between is well-tested, + // they are still valuable. + assertEquals(Instant.MAX, + Duration.between(Instant.EPOCH, Instant.MAX).addTo(Instant.EPOCH)); + assertEquals(Instant.MAX, + Duration.between(Instant.MIN, Instant.MAX).addTo(Instant.MIN)); + assertEquals(Instant.EPOCH, + Duration.between(Instant.MIN, Instant.EPOCH).addTo(Instant.MIN)); + } + + @Test + public void test_subtractFrom() { + assertSame(Instant.EPOCH, Duration.ZERO.subtractFrom(Instant.EPOCH)); + assertEquals(Instant.MIN, + Duration.between(Instant.MIN, Instant.EPOCH).subtractFrom(Instant.EPOCH)); + assertEquals(Instant.MIN, + Duration.between(Instant.MIN, Instant.MAX).subtractFrom(Instant.MAX)); + assertEquals(Instant.EPOCH, + Duration.between(Instant.EPOCH, Instant.MAX).subtractFrom(Instant.MAX)); + } + + @Test + public void test_addTo_exceeds() { + Object[][] breakingValues = new Object[][] { + { Instant.EPOCH, Duration.between(Instant.EPOCH, Instant.MAX).plusNanos(1) }, + // Adding a negative duration is the same as subtracting the negated value. + { Instant.EPOCH, Duration.between(Instant.EPOCH, Instant.MIN).minusNanos(1) }, + { Instant.EPOCH, Duration.between(Instant.MIN, Instant.MAX) }, + { Instant.EPOCH, MAX_DURATION }, + { Instant.MIN, MAX_DURATION }, + { Instant.MAX, Duration.ofNanos(1) }, + { LocalDateTime.MAX, Duration.ofNanos(1) }, + { LocalDateTime.now(), MAX_DURATION }, + { ZonedDateTime.of(LocalDateTime.MAX, ZoneOffset.UTC ), Duration.ofNanos(1) }, + }; + + for (Object[] values : breakingValues) { + Temporal temporal = (Temporal) values[0]; + Duration duration = (Duration) values[1]; + + try { + duration.addTo(temporal); + fail(" Should have failed to add " + duration + " to " + temporal); + } catch (DateTimeException expected) { + } + } + } + + @Test + public void test_subtractFrom_exceeds() { + Object[][] breakingValues = new Object[][] { + { Instant.EPOCH, Duration.between(Instant.MIN, Instant.EPOCH).plusNanos(1) }, + // Subtracting a negative Duration is the same as adding the negated value. + { Instant.EPOCH, Duration.between(Instant.MAX, Instant.EPOCH).minusNanos(1) }, + { Instant.EPOCH, Duration.between(Instant.MIN, Instant.MAX) }, + { Instant.EPOCH, MAX_DURATION }, + { Instant.MAX, MAX_DURATION }, + { Instant.MIN, Duration.ofNanos(1) }, + { LocalDateTime.MIN, Duration.ofNanos(1) }, + { LocalDateTime.now(), MAX_DURATION }, + { LocalDateTime.MAX, MAX_DURATION }, + { ZonedDateTime.of(LocalDateTime.MIN, ZoneOffset.UTC ), Duration.ofNanos(1) }, + }; + + for (Object[] values : breakingValues) { + Temporal temporal = (Temporal) values[0]; + Duration duration = (Duration) values[1]; + + try { + duration.subtractFrom(temporal); + fail("Should have failed to subtract " + duration + " from " + temporal); + } catch (DateTimeException expected) { + } + } + } + + @Test + public void test_addTo_subtractFrom_unsupported() { + // These Temporal objects don't supports seconds/nanos. + // The actual values of those Temporals don't matter, only their type is checked. + Temporal[] unsupportedTemporals = new Temporal[] { + Year.now(), + YearMonth.now(), + LocalDate.now(), + // An arbitrary ChronoLocalDateImpl as a representative example. + MinguoChronology.INSTANCE.dateNow(), + }; + + Duration second = Duration.ofSeconds(1); + for (Temporal temporal : unsupportedTemporals) { + // Adding/subtracting zero should not fail. + assertSame(temporal, Duration.ZERO.addTo(temporal)); + assertSame(temporal, Duration.ZERO.subtractFrom(temporal)); + + try { + second.addTo(temporal); + fail("Should not be able to add a duration to " + temporal); + } catch (UnsupportedTemporalTypeException expected) { + } + try { + second.subtractFrom(temporal); + fail("Should not be able to subtract a duration from " + temporal); + } catch (UnsupportedTemporalTypeException expected) { + } + } + + } +} diff --git a/luni/src/test/java/libcore/java/time/InstantTest.java b/luni/src/test/java/libcore/java/time/InstantTest.java new file mode 100644 index 000000000..ba31264bf --- /dev/null +++ b/luni/src/test/java/libcore/java/time/InstantTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; + +import static org.junit.Assert.assertEquals; + +/** + * Additional tests for {@link Instant}. + * + * @see tck.java.time.TCKInstant + * @see test.java.time.TestInstant + */ +public class InstantTest { + + @Test + public void test_isSupported_TemporalUnit() { + assertEquals(false, Instant.EPOCH.isSupported((TemporalUnit) null)); + assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.NANOS)); + assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.MICROS)); + assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.MILLIS)); + assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.SECONDS)); + assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.MINUTES)); + assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.HOURS)); + assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.HALF_DAYS)); + assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.DAYS)); + assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.WEEKS)); + assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.MONTHS)); + assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.YEARS)); + assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.DECADES)); + assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.CENTURIES)); + assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.MILLENNIA)); + assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.ERAS)); + assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.FOREVER)); + } +} diff --git a/luni/src/test/java/libcore/java/time/LocalDateTest.java b/luni/src/test/java/libcore/java/time/LocalDateTest.java new file mode 100644 index 000000000..f0c8db22b --- /dev/null +++ b/luni/src/test/java/libcore/java/time/LocalDateTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.LocalDate; +import java.time.chrono.IsoChronology; + +import static org.junit.Assert.assertSame; + +/** + * Additional tests for {@link LocalDate}. + * + * @see tck.java.time.TCKLocalDate + * @see test.java.time.TestLocalDate + */ +public class LocalDateTest { + + @Test + public void test_getChronology() { + // LocalDate always uses the IsoChronology. + assertSame(IsoChronology.INSTANCE, LocalDate.MIN.getChronology()); + } +} diff --git a/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java b/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java new file mode 100644 index 000000000..09a258a60 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; + +import static org.junit.Assert.assertEquals; + +/** + * Additional tests for {@link OffsetDateTime}. + * + * @see tck.java.time.TCKOffsetDateTime + * @see test.java.time.TestOffsetDateTime + * @see test.java.time.TestOffsetDateTime_instants + */ +public class OffsetDateTimeTest { + + private static final OffsetDateTime ODT = + OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 6, ZoneOffset.UTC); + // 2000-01-02T03:04:05.000000006 UTC + + @Test + public void test_plus() { + // Most of the logic is in LocalDateTime, to which OffsetDateTime#plus() delegates, verify + // only some simple cases here. In-depth tests for LocalDateTime#plus() can be found in + // TCKLocalDateTime. + assertEquals(OffsetDateTime.of(2000, 1, 2, 4, 4, 5, 6, ZoneOffset.UTC), + ODT.plus(1, ChronoUnit.HOURS)); + assertEquals(OffsetDateTime.of(2000, 1, 3, 2, 4, 5, 6, ZoneOffset.UTC), + ODT.plus(23, ChronoUnit.HOURS)); + assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 5, 5, 6, ZoneOffset.UTC), + ODT.plus(1, ChronoUnit.MINUTES)); + assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 5, 5, 6, ZoneOffset.UTC), + ODT.plus(60, ChronoUnit.SECONDS)); + assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 1_000_006, ZoneOffset.UTC), + ODT.plus(1, ChronoUnit.MILLIS)); + assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 7, ZoneOffset.UTC), + ODT.plus(1, ChronoUnit.NANOS)); + } +} diff --git a/luni/src/test/java/libcore/java/time/OffsetTimeTest.java b/luni/src/test/java/libcore/java/time/OffsetTimeTest.java new file mode 100644 index 000000000..880343f3a --- /dev/null +++ b/luni/src/test/java/libcore/java/time/OffsetTimeTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.OffsetTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; +import java.time.temporal.UnsupportedTemporalTypeException; +import java.util.EnumSet; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +/** + * Additional tests for {@link OffsetTime}. + * + * @see tck.java.time.TCKOffsetTime + * @see test.java.time.TestOffsetTime + */ +public class OffsetTimeTest { + + private static final OffsetTime NOON_UTC = OffsetTime + .of(/* hour */ 12, /* minute */ 0, /* second */ 0, /* nano */ 0, ZoneOffset.UTC); + + @Test + public void test_plus() { + // Most of the logic is in LocalTime, to which OffsetTime#plus() delegates, verify only some + // simple cases here. In-depth tests for LocalTime#plus() can be found in TCKLocalTime. + assertEquals(OffsetTime.of(13, 0, 0, 0, ZoneOffset.UTC), + NOON_UTC.plus(1, ChronoUnit.HOURS)); + assertEquals(OffsetTime.of(11, 0, 0, 0, ZoneOffset.UTC), + NOON_UTC.plus(23, ChronoUnit.HOURS)); + assertEquals(OffsetTime.of(12, 1, 0, 0, ZoneOffset.UTC), + NOON_UTC.plus(1, ChronoUnit.MINUTES)); + assertEquals(OffsetTime.of(12, 1, 0, 0, ZoneOffset.UTC), + NOON_UTC.plus(60, ChronoUnit.SECONDS)); + assertEquals(OffsetTime.of(12, 0, 0, 1_000_000, ZoneOffset.UTC), + NOON_UTC.plus(1, ChronoUnit.MILLIS)); + assertEquals(OffsetTime.of(12, 0, 0, 1, ZoneOffset.UTC), + NOON_UTC.plus(1, ChronoUnit.NANOS)); + } + + @Test + public void test_plus_noop() { + assertPlusIsNoop(0, ChronoUnit.MINUTES); + assertPlusIsNoop(2, ChronoUnit.HALF_DAYS); + assertPlusIsNoop(24, ChronoUnit.HOURS); + assertPlusIsNoop(24 * 60, ChronoUnit.MINUTES); + assertPlusIsNoop(24 * 60 * 60, ChronoUnit.SECONDS); + assertPlusIsNoop(24 * 60 * 60 * 1_000, ChronoUnit.MILLIS); + assertPlusIsNoop(24 * 60 * 60 * 1_000_000_000L, ChronoUnit.NANOS); + } + + private static void assertPlusIsNoop(long amount, TemporalUnit unit) { + assertSame(NOON_UTC, NOON_UTC.plus(amount, unit)); + } + + @Test + public void test_plus_minus_invalidUnits() { + for (ChronoUnit unit : EnumSet.range(ChronoUnit.DAYS, ChronoUnit.FOREVER)) { + try { + NOON_UTC.plus(1, unit); + fail("Adding 1 " + unit + " should have failed."); + } catch (UnsupportedTemporalTypeException expected) { + } + try { + NOON_UTC.minus(1, unit); + fail("Subtracting 1 " + unit + " should have failed."); + } catch (UnsupportedTemporalTypeException expected) { + } + } + } +} diff --git a/luni/src/test/java/libcore/java/time/PeriodTest.java b/luni/src/test/java/libcore/java/time/PeriodTest.java new file mode 100644 index 000000000..5d312a9a5 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/PeriodTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.Period; +import java.time.chrono.IsoChronology; + +import static org.junit.Assert.assertSame; + +/** + * Additional tests for {@link Period}. + * + * @see tck.java.time.TCKPeriod + * @see test.java.time.TestPeriod + */ +public class PeriodTest { + @Test + public void test_getChronology() { + // Period always uses the IsoChronology. + assertSame(IsoChronology.INSTANCE, Period.ZERO.getChronology()); + } + +} diff --git a/luni/src/test/java/libcore/java/time/YearMonthTest.java b/luni/src/test/java/libcore/java/time/YearMonthTest.java new file mode 100644 index 000000000..c4a3e79d0 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/YearMonthTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.DateTimeException; +import java.time.Month; +import java.time.Year; +import java.time.YearMonth; +import java.time.chrono.IsoEra; +import java.time.temporal.ChronoField; +import java.time.temporal.UnsupportedTemporalTypeException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +/** + * Additional tests for {@link YearMonth}. + * + * @see tck.java.time.TCKYearMonth + * @see test.java.time.TestYearMonth + */ +public class YearMonthTest { + + @Test + public void test_with_TemporalField_long() { + YearMonth ym = YearMonth.of(2000, Month.JANUARY); + // -1999 is actually 2000 BCE (and 0 is 1 BCE). + YearMonth bceYm = YearMonth.of(-1999, Month.JANUARY); + + assertEquals(YearMonth.of(1000, Month.JANUARY), ym.with(ChronoField.YEAR, 1000)); + assertEquals(YearMonth.of(-1, Month.JANUARY), ym.with(ChronoField.YEAR, -1)); + assertEquals(YearMonth.of(2000, Month.FEBRUARY), ym.with(ChronoField.MONTH_OF_YEAR, 2)); + assertEquals(YearMonth.of(-1999, Month.DECEMBER), + bceYm.with(ChronoField.MONTH_OF_YEAR, 12)); + assertSame(ym, ym.with(ChronoField.ERA, IsoEra.CE.getValue())); + assertSame(bceYm, bceYm.with(ChronoField.ERA, IsoEra.BCE.getValue())); + + assertEquals(bceYm, ym.with(ChronoField.ERA, IsoEra.BCE.getValue())); + assertEquals(ym, bceYm.with(ChronoField.ERA, IsoEra.CE.getValue())); + assertEquals(YearMonth.of(1, Month.JANUARY), ym.with(ChronoField.YEAR_OF_ERA, 1)); + // Proleptic year 0 is 1 BCE. + assertEquals(YearMonth.of(0, Month.JANUARY), bceYm.with(ChronoField.YEAR_OF_ERA, 1)); + assertEquals(YearMonth.of(0, Month.JANUARY), ym.with(ChronoField.PROLEPTIC_MONTH, 0)); + assertEquals(YearMonth.of(Year.MAX_VALUE, Month.DECEMBER), ym.with(ChronoField.PROLEPTIC_MONTH, Year.MAX_VALUE * 12L + 11)); + assertEquals(YearMonth.of(Year.MIN_VALUE, Month.JANUARY), ym.with(ChronoField.PROLEPTIC_MONTH, Year.MIN_VALUE * 12L)); + } + + @Test + public void test_with_TemporalField_long_invalidValue() { + Object[][] invalidValues = new Object[][] { + { ChronoField.YEAR_OF_ERA, 0 }, + { ChronoField.YEAR_OF_ERA, Year.MAX_VALUE + 1 }, + { ChronoField.YEAR, Year.MIN_VALUE - 1 }, + { ChronoField.YEAR, Year.MAX_VALUE + 1 }, + { ChronoField.ERA, -1 }, + { ChronoField.ERA, 2 }, + { ChronoField.MONTH_OF_YEAR, -1 }, + { ChronoField.MONTH_OF_YEAR, 0 }, + { ChronoField.MONTH_OF_YEAR, 13 }, + { ChronoField.PROLEPTIC_MONTH, Year.MAX_VALUE * 12L + 12 }, + { ChronoField.PROLEPTIC_MONTH, Year.MIN_VALUE * 12L - 1 }, + }; + + YearMonth ym = YearMonth.of(2000, Month.JANUARY); + for (Object[] values : invalidValues) { + ChronoField field = (ChronoField) values[0]; + long value = ((Number) values[1]).longValue(); + try { + ym.with(field, value); + fail("ym.with(" + field + ", " + value + ") should have failed."); + } catch (DateTimeException expected) { + } + } + + } + + @Test + public void test_with_TemporalField_long_invalidField() { + ChronoField[] invalidFields = new ChronoField[] { + ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH, + ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR, + ChronoField.ALIGNED_WEEK_OF_MONTH, + ChronoField.ALIGNED_WEEK_OF_YEAR, + ChronoField.AMPM_OF_DAY, + ChronoField.CLOCK_HOUR_OF_AMPM, + ChronoField.CLOCK_HOUR_OF_DAY, + ChronoField.DAY_OF_MONTH, + ChronoField.DAY_OF_WEEK, + ChronoField.DAY_OF_YEAR, + ChronoField.EPOCH_DAY, + ChronoField.HOUR_OF_AMPM, + ChronoField.HOUR_OF_DAY, + ChronoField.INSTANT_SECONDS, + ChronoField.MICRO_OF_DAY, + ChronoField.MICRO_OF_SECOND, + ChronoField.MILLI_OF_DAY, + ChronoField.MILLI_OF_SECOND, + ChronoField.MINUTE_OF_DAY, + ChronoField.MINUTE_OF_HOUR, + ChronoField.NANO_OF_DAY, + ChronoField.NANO_OF_SECOND, + ChronoField.OFFSET_SECONDS, + ChronoField.SECOND_OF_DAY, + ChronoField.SECOND_OF_MINUTE, + }; + + YearMonth ym = YearMonth.of(2000, Month.JANUARY); + for (ChronoField invalidField : invalidFields) { + // Get a valid value to ensure we fail to due invalid field, not due to invalid value. + long value = invalidField.range().getMinimum(); + try { + ym.with(invalidField, value); + fail("TemporalField.with() should not accept " + invalidField); + } catch (UnsupportedTemporalTypeException expected) { + } + } + + } +} diff --git a/luni/src/test/java/libcore/java/time/YearTest.java b/luni/src/test/java/libcore/java/time/YearTest.java new file mode 100644 index 000000000..345439188 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/YearTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.Year; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Additional tests for {@link Year}. + * + * @see tck.java.time.TCKYear + * @see test.java.time.TestYear + */ +public class YearTest { + @Test + public void test_isLeap() { + // More extensive tests for Year.isLeap() (which delegates to this static method) can be + // found in tck.java.time.TCKYear.test_isLeap() + assertFalse(Year.isLeap(1900)); + assertFalse(Year.isLeap(1999)); + assertTrue(Year.isLeap(2000)); + assertFalse(Year.isLeap(2001)); + assertFalse(Year.isLeap(2002)); + assertFalse(Year.isLeap(2003)); + assertTrue(Year.isLeap(2004)); + assertFalse(Year.isLeap(2005)); + } +} diff --git a/luni/src/test/java/libcore/java/time/ZoneOffsetTest.java b/luni/src/test/java/libcore/java/time/ZoneOffsetTest.java new file mode 100644 index 000000000..1a3725784 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/ZoneOffsetTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.ZoneOffset; +import java.time.temporal.ChronoField; +import java.time.temporal.UnsupportedTemporalTypeException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * Additional tests for {@link ZoneOffset}. + * + * @see tck.java.time.TCKZoneOffset + * @see test.java.time.TestZoneOffset + */ +public class ZoneOffsetTest { + + private static final ZoneOffset OFFSET_P1 = ZoneOffset.ofHours(1); + + @Test + public void test_isSupported() { + for (ChronoField field : ChronoField.values()) { + // Only OFFSET_SECONDS is supported. + assertEquals("isSupported(" + field + ")", + field == ChronoField.OFFSET_SECONDS, OFFSET_P1.isSupported(field)); + } + } + + @Test + public void test_range() { + assertEquals(ChronoField.OFFSET_SECONDS.range(), + OFFSET_P1.range(ChronoField.OFFSET_SECONDS)); + } + + @Test(expected = NullPointerException.class) + public void test_range_null() { + OFFSET_P1.range(null); + } + + @Test + public void test_range_unsupported() { + for (ChronoField field : ChronoField.values()) { + // Only OFFSET_SECONDS is supported. + if (field == ChronoField.OFFSET_SECONDS) { + continue; + } + try { + OFFSET_P1.range(field); + fail("ZoneOffset.range(" + field + ") should have failed."); + } catch (UnsupportedTemporalTypeException expected) { + } + } + } +} diff --git a/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java b/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java new file mode 100644 index 000000000..09422dd46 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time; + +import org.junit.Test; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import static org.junit.Assert.assertEquals; + + +/** + * Additional tets for {@link ZonedDateTime}. + * + * @see tck.java.time.TCKZonedDateTime + * @see test.java.time.TestZonedDateTime + */ +public class ZonedDateTimeTest { + + // Europe/Vienna is UTC+2 during summer, UTC+1 during winter. + private static final ZoneId ZONE_VIENNA = ZoneId.of("Europe/Vienna"); + + // UTC+1, the offset during winter time. + private static final ZoneOffset OFFSET_P1 = ZoneOffset.ofHours(1); + + // UTC+2, the offset during summer time. + private static final ZoneOffset OFFSET_P2 = ZoneOffset.ofHours(2); + + // LocalDateTime during winter time (OFFSET_P1 in ZONE_VIENNA). + private static final LocalDateTime LDT_P1 = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0); + + // LocalDateTime during summer time (OFFSET_P2 in ZONE_VIENNA). + private static final LocalDateTime LDT_P2 = LocalDateTime.of(2000, Month.JUNE, 1, 0, 0); + + // LocalDateTime that is in a gap that occurs at the switch from winter time to summer time. + // This is not a valid local time in ZONE_VIENNA. + private static final LocalDateTime LDT_IN_GAP = LocalDateTime.of(2000, Month.MARCH, 26, 2, 30); + + // LocalDateTime that is in an overlap that occurs at the switch from summer time to winter + // time. This LDT actually occurs twice in ZONE_VIENNA. + private static final LocalDateTime LDT_IN_OVERLAP = + LocalDateTime.of(2000, Month.OCTOBER, 29, 2, 30); + + @Test + public void test_ofInstant() { + // ofInstant behaves as if it calculated an Instant from the LocalDateTime/ZoneOffset + // and then calling ofInstant(Instant, ZoneId). That's why "invalid" zone offsets are + // tolerated and basically just change how the LocalDateTime is interpreted. + + // checkOfInstant(localDateTime, offset, zone, expectedDateTime, expectedOffset) + + // Correct offset in summer. + checkOfInstant(LDT_P1, OFFSET_P1, ZONE_VIENNA, LDT_P1, OFFSET_P1); + // Correct offset in winter. + checkOfInstant(LDT_P2, OFFSET_P2, ZONE_VIENNA, LDT_P2, OFFSET_P2); + // "Wrong" offset in winter. + checkOfInstant(LDT_P1, OFFSET_P2, ZONE_VIENNA, LDT_P1.minusDays(1).withHour(23), OFFSET_P1); + // "Wrong" offset in summer. + checkOfInstant(LDT_P2, OFFSET_P1, ZONE_VIENNA, LDT_P2.withHour(1), OFFSET_P2); + + // Very wrong offset in winter. + checkOfInstant(LDT_P1, ZoneOffset.ofHours(-10), ZONE_VIENNA, LDT_P1.withHour(11), + OFFSET_P1); + + // Neither of those combinations exist, so they are interpreted as either before or after + // the gap, depending on the offset. + checkOfInstant(LDT_IN_GAP, OFFSET_P1, ZONE_VIENNA, LDT_IN_GAP.plusHours(1), OFFSET_P2); + checkOfInstant(LDT_IN_GAP, OFFSET_P2, ZONE_VIENNA, LDT_IN_GAP.minusHours(1), OFFSET_P1); + + // Both combinations exist and are valid, so they produce exactly the input. + checkOfInstant(LDT_IN_OVERLAP, OFFSET_P1, ZONE_VIENNA, LDT_IN_OVERLAP, OFFSET_P1); + checkOfInstant(LDT_IN_OVERLAP, OFFSET_P2, ZONE_VIENNA, LDT_IN_OVERLAP, OFFSET_P2); + } + + /** + * Assert that calling {@link ZonedDateTime#ofInstant(LocalDateTime, ZoneOffset, ZoneId)} with + * the first three parameters produces a sane result with the localDateTime and offset equal to + * the last two. + */ + private static void checkOfInstant(LocalDateTime localDateTime, ZoneOffset offset, + ZoneId zone, LocalDateTime expectedDateTime, ZoneOffset expectedOffset) { + ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(localDateTime, offset, zone); + String message = String.format(" for ofInstant(%s, %s, %s) = %s, ", + localDateTime, offset, zone, zonedDateTime); + // Note that localDateTime doesn't necessarily equal zoneDateTime.toLocalDateTime(), + // specifically when offset is not a valid offset for zone at localDateTime (or ever). + assertEquals("zone" + message, zone, zonedDateTime.getZone()); + + assertEquals("offset" + message, expectedOffset, zonedDateTime.getOffset()); + assertEquals("localDateTime" + message, expectedDateTime, zonedDateTime.toLocalDateTime()); + if (offset.equals(expectedOffset)) { + // When we get same offset, the localDateTime must be the same as the input. This + // assert basically just verifies that the test is written correctly. + assertEquals("expected localDateTime" + message, + expectedDateTime, zonedDateTime.toLocalDateTime()); + } + } + + @Test(expected = NullPointerException.class) + public void test_ofInstant_localDateTime_null() { + ZonedDateTime.ofInstant(null, OFFSET_P1, ZONE_VIENNA); + } + + @Test(expected = NullPointerException.class) + public void test_ofInstant_offset_null() { + ZonedDateTime.ofInstant(LDT_P1, null, ZONE_VIENNA); + } + + @Test(expected = NullPointerException.class) + public void test_ofInstant_zone_null() { + ZonedDateTime.ofInstant(LDT_P1, OFFSET_P1, null); + } + + @Test + public void test_ofLocal() { + // checkOfLocal(localDateTime, zone, preferredOffset, expectedDateTime, expectedOffset) + + // Correct offset in summer. + checkOfLocal(LDT_P1, ZONE_VIENNA, OFFSET_P1, LDT_P1, OFFSET_P1); + // Correct offset in winter. + checkOfLocal(LDT_P2, ZONE_VIENNA, OFFSET_P2, LDT_P2, OFFSET_P2); + // "Wrong" offset in winter. + checkOfLocal(LDT_P1, ZONE_VIENNA, OFFSET_P2, LDT_P1, OFFSET_P1); + // "Wrong" offset in summer. + checkOfLocal(LDT_P2, ZONE_VIENNA, OFFSET_P1, LDT_P2, OFFSET_P2); + // Very wrong offset in winter. + checkOfLocal(LDT_P1, ZONE_VIENNA, ZoneOffset.ofHours(-10), LDT_P1, OFFSET_P1); + + // Neither of those combinations exist, so they are interpreted as after the gap. + checkOfLocal(LDT_IN_GAP, ZONE_VIENNA, OFFSET_P1, LDT_IN_GAP.plusHours(1), OFFSET_P2); + checkOfLocal(LDT_IN_GAP, ZONE_VIENNA, OFFSET_P2, LDT_IN_GAP.plusHours(1), OFFSET_P2); + + // Both combinations exist and are valid, so they produce exactly the input. + checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, OFFSET_P1, LDT_IN_OVERLAP, OFFSET_P1); + checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, OFFSET_P2, LDT_IN_OVERLAP, OFFSET_P2); + + // Passing in null for preferredOffset will be biased to the offset before the transition. + checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, /* preferredOffset */ null, + LDT_IN_OVERLAP, OFFSET_P2); + // Passing in an invalid offset will be biased to the offset before the transition. + checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, ZoneOffset.ofHours(10), + LDT_IN_OVERLAP, OFFSET_P2); + } + + /** + * Assert that calling {@link ZonedDateTime#ofLocal(LocalDateTime, ZoneId, ZoneOffset)} with + * the first three parameters produces a sane result with the localDateTime, and offset equal + * to the last two. + */ + private static void checkOfLocal(LocalDateTime localDateTime, ZoneId zone, + ZoneOffset preferredOffset, LocalDateTime expectedDateTime, ZoneOffset expectedOffset) { + ZonedDateTime zonedDateTime = ZonedDateTime.ofLocal(localDateTime, zone, preferredOffset); + String message = String.format(" for ofLocal(%s, %s, %s) = %s, ", + localDateTime, zone, preferredOffset, zonedDateTime); + // Note that localDateTime doesn't necessarily equal zoneDateTime.toLocalDateTime(), + // specifically when offset is not a valid offset for zone at localDateTime (or ever). + assertEquals("zone" + message, zone, zonedDateTime.getZone()); + assertEquals("offset" + message, expectedOffset, zonedDateTime.getOffset()); + assertEquals("localDateTime" + message, expectedDateTime, zonedDateTime.toLocalDateTime()); + } + + @Test(expected = NullPointerException.class) + public void test_ofLocal_localDateTime_null() { + ZonedDateTime.ofLocal(null, ZONE_VIENNA, OFFSET_P1); + } + + @Test(expected = NullPointerException.class) + public void test_ofLocal_zone_null() { + ZonedDateTime.ofLocal(LDT_P1, null, OFFSET_P1); + } +} diff --git a/luni/src/test/java/libcore/java/time/chrono/ChronologyDisplayNameTest.java b/luni/src/test/java/libcore/java/time/chrono/ChronologyDisplayNameTest.java new file mode 100644 index 000000000..be9d4d610 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/chrono/ChronologyDisplayNameTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 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. + */ +package libcore.java.time.chrono; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import java.time.chrono.Chronology; +import java.time.chrono.Era; +import java.time.format.TextStyle; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test display names of chronologies and their eras. + * The primary reason for this test is to ensure that newly added chronologies have a display name + * and that their eras have a display name as well. + */ +@RunWith(Parameterized.class) +public class ChronologyDisplayNameTest { + @Parameterized.Parameters(name = "{0}") + public static List getChronologies() { + return new ArrayList<>(Chronology.getAvailableChronologies()); + } + + private final Chronology chronology; + + public ChronologyDisplayNameTest(Chronology chronology) { + this.chronology = chronology; + } + + @Test + public void testDisplayName() { + String displayName = chronology.getDisplayName(TextStyle.FULL, Locale.ENGLISH); + assertNotNull(displayName); + assertFalse("".equals(displayName)); + } + + @Test + public void testEras() { + List eras = chronology.eras(); + Set eraNames = new HashSet<>(); + for (Era era : eras) { + assertNotNull(era); + String displayName = era.getDisplayName(TextStyle.FULL, Locale.ENGLISH); + assertNotNull(displayName); + assertFalse("".equals(displayName)); + assertTrue("Era name for " + era + " not unique.", eraNames.add(displayName)); + } + } +} diff --git a/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java new file mode 100644 index 000000000..37fdd7c8b --- /dev/null +++ b/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.chrono; + +import org.junit.Test; +import java.time.chrono.AbstractChronology; +import java.time.chrono.ChronoLocalDate; +import java.time.chrono.Chronology; +import java.time.chrono.Era; +import java.time.chrono.IsoChronology; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.ValueRange; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +/** + * Additional tests for {@link Chronology}. + * + * @see tck.java.time.chrono.TCKChronology + */ +public class ChronologyTest { + + @Test + public void test_compareTo() { + Set chronologies = new LinkedHashSet<>(Chronology.getAvailableChronologies()); + chronologies.add(new DummyChronology("aaa", "z aaa")); + chronologies.add(new DummyChronology("zzz", "a zzz")); + + // Check for comparison of each chronology with each other (including itself). + for (Chronology c1 : chronologies) { + for (Chronology c2 : chronologies) { + assertComparesAccordingToId(c1, c2); + } + } + } + + private static void assertComparesAccordingToId(Chronology c1, Chronology c2) { + int chronologyResult = c1.compareTo(c2); + int idResult = c1.getId().compareTo(c2.getId()); + // note that this message is not strictly true: if two chronologies with the same id but + // different parameters exist, then they should return non-zero for compareTo(). That is not + // possible with any of the chronologies we currently ship (as of early 2017), though. + assertEquals("compareTo() must match getId().compareTo()", + (int) Math.signum(chronologyResult), (int) Math.signum(idResult)); + assertEquals(c1 + " and " + c2 + " compare as equal.", + chronologyResult == 0, c1.equals(c2)); + } + + @Test(expected = NullPointerException.class) + public void test_compareTo_null() { + IsoChronology.INSTANCE.compareTo(null); + } + + /** Dummy chronology that supports only returning an id and a type. */ + private static class DummyChronology extends AbstractChronology { + + private final String id; + + private final String type; + + public DummyChronology(String id, String type) { + this.id = id; + this.type = type; + } + + + @Override + public String getId() { + return id; + } + + @Override + public String getCalendarType() { + return type; + } + + @Override + public ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth) { + throw new UnsupportedOperationException(); + } + + @Override + public ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear) { + throw new UnsupportedOperationException(); + } + + @Override + public ChronoLocalDate dateEpochDay(long epochDay) { + throw new UnsupportedOperationException(); + } + + @Override + public ChronoLocalDate date(TemporalAccessor temporal) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLeapYear(long prolepticYear) { + throw new UnsupportedOperationException(); + } + + @Override + public int prolepticYear(Era era, int yearOfEra) { + throw new UnsupportedOperationException(); + } + + @Override + public Era eraOf(int eraValue) { + throw new UnsupportedOperationException(); + } + + @Override + public List eras() { + throw new UnsupportedOperationException(); + } + + @Override + public ValueRange range(ChronoField field) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java new file mode 100644 index 000000000..3a33e5b8d --- /dev/null +++ b/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.chrono; + +import org.junit.Test; +import java.time.chrono.HijrahChronology; +import java.time.chrono.HijrahDate; +import java.time.chrono.HijrahEra; +import java.time.temporal.ChronoField; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +/** + * Additional tests for {@link HijrahDate}. + * + * @see tck.java.time.chrono.TCKHijrahChronology + */ +public class HijrahChronologyTest { + @Test + public void test_HijrahDate_getEra() { + // HijrahChronology has only one valid era. + assertEquals(HijrahEra.AH, HijrahDate.of(1300, 1, 1).getEra()); + } + + @Test + public void test_HijrahDate_getLong() { + // 1300 is the first year in the HijrahChronology in the umalqura configuration. + HijrahDate date = HijrahDate.of(1300, 2, 5); + assertEquals(1300, date.getLong(ChronoField.YEAR_OF_ERA)); + assertEquals(1300, date.getLong(ChronoField.YEAR)); + assertEquals(2, date.getLong(ChronoField.MONTH_OF_YEAR)); + // Proleptic month starts with 0 for the first month of the proleptic year 0. + assertEquals(1300 * 12 + 2 - 1, date.getLong(ChronoField.PROLEPTIC_MONTH)); + assertEquals(5, date.getLong(ChronoField.DAY_OF_MONTH)); + // first month of the year 1300 has 30 days. + assertEquals(30 + 5, date.getLong(ChronoField.DAY_OF_YEAR)); + assertEquals(date.toEpochDay(), date.getLong(ChronoField.EPOCH_DAY)); + } + + @Test + public void test_HijrahDate_withVariant_same() { + // There is currently no way of creating an alternative HijrahChronology, so only this + // case and the null case are tested. + HijrahDate date1 = HijrahDate.now(); + HijrahDate date2 = date1.withVariant(HijrahChronology.INSTANCE); + assertSame(date1, date2); + } + + @Test(expected = NullPointerException.class) + public void test_HijrahDate_withVariant_null() { + HijrahDate.now().withVariant(null); + } +} diff --git a/luni/src/test/java/libcore/java/time/chrono/IsoChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/IsoChronologyTest.java new file mode 100644 index 000000000..2036f4495 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/chrono/IsoChronologyTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.chrono; + +import org.junit.Test; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.Year; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.chrono.IsoChronology; +import java.time.chrono.IsoEra; +import java.time.temporal.ChronoField; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * Additional tests for {@link IsoChronology}. + * + * @see tck.java.time.chrono.TCKIsoChronology + */ +public class IsoChronologyTest { + + @Test + public void test_dateYear() { + int[][] allValues = new int[][] { + // proleptic Year, dayOfYear, expectedYear, expectedMonth, expectedDayOfMonth + { 2017, 1, 2017, 1, 1 }, + { 1, 365, 1, 12, 31 }, + { 0, 32, 1, 2, 1 }, + { -100, 1, 101, 1, 1 }, + { 2000, 61, 2000, 3, 1 }, + { 2000, 366, 2000, 12, 31 }, + { Year.MAX_VALUE, 365, Year.MAX_VALUE, 12, 31 }, + { Year.MIN_VALUE, 365, -Year.MIN_VALUE + 1, 12, 31 }, + }; + + for (int[] values : allValues) { + LocalDate localDate = IsoChronology.INSTANCE.dateYearDay(values[0], values[1]); + IsoEra expectedEra = values[0] <= 0 ? IsoEra.BCE : IsoEra.CE; + assertEquals(expectedEra, localDate.getEra()); + assertEquals(values[0], localDate.getYear()); + assertEquals(values[1], localDate.getDayOfYear()); + assertEquals(values[2], localDate.get(ChronoField.YEAR_OF_ERA)); + assertEquals(values[3], localDate.getMonthValue()); + assertEquals(values[4], localDate.getDayOfMonth()); + } + } + + @Test + public void test_dateYear_invalidValues() { + int[][] invalidValues = new int[][] { + { Year.MAX_VALUE + 1, 1 }, + { Year.MIN_VALUE - 1, 1 }, + { Integer.MAX_VALUE, 1 }, + { Integer.MIN_VALUE, 1 }, + { 2001, 366 }, + { 2000, 367 }, + { 2017, 0 }, + { 2017, -1 }, + }; + + for (int[] values : invalidValues) { + try { + LocalDate localDate = IsoChronology.INSTANCE.dateYearDay(values[0], values[1]); + fail(values[0] + "/" + values[1] + " should have failed, but produced " + + localDate); + } catch (DateTimeException expected) { + } + } + } + + @Test + public void test_range() { + for (ChronoField field : ChronoField.values()) { + // IsoChronology ranges should by definition be equal to the default ranges. + assertEquals(field.range(), IsoChronology.INSTANCE.range(field)); + } + } + + @Test + public void test_zonedDateTime() { + ZonedDateTime zonedDateTime = ZonedDateTime + .of(/* year */ 2017, /* month */ 4, /* dayOfMonth */ 1, + /* hour */ 15, /* minute */ 14, /* second */ 13, /* nanoOfSecond */ 12, + ZoneId.of("Europe/London")); + + ZonedDateTime result = IsoChronology.INSTANCE + .zonedDateTime(zonedDateTime.toInstant(), zonedDateTime.getZone()); + assertEquals(LocalDate.of(2017, 4, 1), result.toLocalDate()); + assertEquals(LocalTime.of(15, 14, 13, 12), result.toLocalTime()); + assertEquals(ZoneOffset.ofHours(1), result.getOffset()); + } + + @Test(expected = NullPointerException.class) + public void test_zonedDateTime_nullInstant() { + IsoChronology.INSTANCE.zonedDateTime(null, ZoneOffset.UTC); + } + + @Test(expected = NullPointerException.class) + public void test_zonedDateTime_nullZone() { + IsoChronology.INSTANCE.zonedDateTime(Instant.EPOCH, null); + } +} diff --git a/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java new file mode 100644 index 000000000..3c1f0cf56 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.chrono; + +import org.junit.Test; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.chrono.ChronoZonedDateTime; +import java.time.chrono.JapaneseChronology; +import java.time.chrono.JapaneseDate; +import java.time.chrono.JapaneseEra; +import java.time.temporal.ChronoField; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +/** + * Additional tests for {@link JapaneseChronology} and {@link JapaneseDate}. + * + * @see tck.java.time.chrono.TCKJapaneseChronology + */ +public class JapaneseChronologyTest { + + @Test + public void test_zonedDateTime() { + ZonedDateTime zonedDateTime = ZonedDateTime + .of(2017, 4, 1, 15, 14, 13, 12, ZoneId.of("Europe/London")); + + ChronoZonedDateTime result = JapaneseChronology.INSTANCE + .zonedDateTime(zonedDateTime.toInstant(), zonedDateTime.getZone()); + assertEquals(JapaneseDate.of(JapaneseEra.HEISEI, 29, 4, 1), result.toLocalDate()); + assertEquals(LocalTime.of(15, 14, 13, 12), result.toLocalTime()); + assertEquals(ZoneOffset.ofHours(1), result.getOffset()); + } + + @Test(expected = NullPointerException.class) + public void test_zonedDateTime_nullInstant() { + JapaneseChronology.INSTANCE.zonedDateTime(null, ZoneOffset.UTC); + } + + @Test(expected = NullPointerException.class) + public void test_zonedDateTime_nullZone() { + JapaneseChronology.INSTANCE.zonedDateTime(Instant.EPOCH, null); + } + + @Test + public void test_JapaneseDate_getChronology() { + assertSame(JapaneseChronology.INSTANCE, JapaneseDate.now().getChronology()); + } + + @Test + public void test_JapaneseDate_getEra() { + // pick the first january of the second year of each era, except for Meiji, because the + // first supported year in JapaneseChronology is Meiji 6. + assertEquals(JapaneseEra.MEIJI, JapaneseDate.from(LocalDate.of(1873, 1, 1)).getEra()); + assertEquals(JapaneseEra.TAISHO, JapaneseDate.from(LocalDate.of(1913, 1, 1)).getEra()); + assertEquals(JapaneseEra.SHOWA, JapaneseDate.from(LocalDate.of(1927, 1, 1)).getEra()); + assertEquals(JapaneseEra.HEISEI, JapaneseDate.from(LocalDate.of(1990, 1, 1)).getEra()); + } + + @Test + public void test_JapaneseDate_isSupported_TemporalField() { + JapaneseDate date = JapaneseDate.now(); + // all date based fields, except for the aligned week ones are supported. + assertEquals(false, date.isSupported(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH)); + assertEquals(false, date.isSupported(ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR)); + assertEquals(false, date.isSupported(ChronoField.ALIGNED_WEEK_OF_MONTH)); + assertEquals(false, date.isSupported(ChronoField.ALIGNED_WEEK_OF_YEAR)); + assertEquals(false, date.isSupported(ChronoField.AMPM_OF_DAY)); + assertEquals(false, date.isSupported(ChronoField.CLOCK_HOUR_OF_AMPM)); + assertEquals(false, date.isSupported(ChronoField.CLOCK_HOUR_OF_DAY)); + assertEquals(true, date.isSupported(ChronoField.DAY_OF_MONTH)); + assertEquals(true, date.isSupported(ChronoField.DAY_OF_WEEK)); + assertEquals(true, date.isSupported(ChronoField.DAY_OF_YEAR)); + assertEquals(true, date.isSupported(ChronoField.EPOCH_DAY)); + assertEquals(true, date.isSupported(ChronoField.ERA)); + assertEquals(false, date.isSupported(ChronoField.HOUR_OF_AMPM)); + assertEquals(false, date.isSupported(ChronoField.HOUR_OF_DAY)); + assertEquals(false, date.isSupported(ChronoField.INSTANT_SECONDS)); + assertEquals(false, date.isSupported(ChronoField.MICRO_OF_DAY)); + assertEquals(false, date.isSupported(ChronoField.MICRO_OF_SECOND)); + assertEquals(false, date.isSupported(ChronoField.MILLI_OF_DAY)); + assertEquals(false, date.isSupported(ChronoField.MILLI_OF_SECOND)); + assertEquals(false, date.isSupported(ChronoField.MINUTE_OF_DAY)); + assertEquals(false, date.isSupported(ChronoField.MINUTE_OF_HOUR)); + assertEquals(true, date.isSupported(ChronoField.MONTH_OF_YEAR)); + assertEquals(false, date.isSupported(ChronoField.NANO_OF_DAY)); + assertEquals(false, date.isSupported(ChronoField.NANO_OF_SECOND)); + assertEquals(false, date.isSupported(ChronoField.OFFSET_SECONDS)); + assertEquals(true, date.isSupported(ChronoField.PROLEPTIC_MONTH)); + assertEquals(false, date.isSupported(ChronoField.SECOND_OF_DAY)); + assertEquals(false, date.isSupported(ChronoField.SECOND_OF_MINUTE)); + assertEquals(true, date.isSupported(ChronoField.YEAR)); + assertEquals(true, date.isSupported(ChronoField.YEAR_OF_ERA)); + } +} diff --git a/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java new file mode 100644 index 000000000..4d9e7bc76 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.chrono; + +import org.junit.Test; +import java.time.LocalDate; +import java.time.chrono.MinguoChronology; +import java.time.chrono.MinguoDate; +import java.time.chrono.MinguoEra; +import java.time.temporal.ChronoField; +import java.time.temporal.ValueRange; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +/** + * Additional tests for {@link MinguoChronology} and {@link MinguoDate}. + * + * @see tck.java.time.chrono.TCKMinguoChronology + */ +public class MinguoChronologyTest { + + // year 1 in Minguo calendar is 1912 in ISO calendar. + private static final int YEARS_BEHIND = 1911; + + @Test + public void test_range() { + for (ChronoField field : ChronoField.values()) { + ValueRange expected; + switch (field) { + case PROLEPTIC_MONTH: + // Proleptic month values are shifted by YEARS_BEHIND * 12. + expected = ValueRange.of( + ChronoField.PROLEPTIC_MONTH.range().getMinimum() - YEARS_BEHIND * 12L, + ChronoField.PROLEPTIC_MONTH.range().getMaximum() - YEARS_BEHIND * 12L); + break; + case YEAR_OF_ERA: + // range for era ROC is 1.. + // range for era before ROC is 1..<-yearRange.min + 1 + OFFSET> + expected = ValueRange.of(1, ChronoField.YEAR.range().getMaximum() - YEARS_BEHIND, + -ChronoField.YEAR.range().getMinimum() + 1 + YEARS_BEHIND); + break; + case YEAR: + // Proleptic year values are shifted by YEAR. + expected = ValueRange.of(ChronoField.YEAR.range().getMinimum() - YEARS_BEHIND, + ChronoField.YEAR.range().getMaximum() - YEARS_BEHIND); + break; + default: + // All other fields have the same ranges as ISO. + expected = field.range(); + break; + } + assertEquals("Range of " + field, expected, MinguoChronology.INSTANCE.range(field)); + } + } + + @Test + public void test_MinguoDate_getChronology() { + assertSame(MinguoChronology.INSTANCE, MinguoDate.now().getChronology()); + } + + @Test + public void test_MinguoDate_getEra() { + assertEquals(MinguoEra.BEFORE_ROC, MinguoDate.of(-1, 1, 1).getEra()); + assertEquals(MinguoEra.ROC, MinguoDate.of(1, 1, 1).getEra()); + } + + @Test + public void test_MinguoDate_range() { + MinguoDate dates[] = new MinguoDate[] { + MinguoDate.from(LocalDate.of(2000, 2, 1)), //February of a leap year + MinguoDate.from(LocalDate.of(2001, 2, 1)), //February of a non-leap year + MinguoDate.of(1, 2, 3), + MinguoDate.of(4, 5, 6), + MinguoDate.of(-7, 8, 9) + }; + + for (MinguoDate date : dates) { + // only these three ChronoFields and YEAR_OF_ERA (below) have date-dependent ranges. + assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_MONTH), + date.range(ChronoField.DAY_OF_MONTH)); + assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_YEAR), + date.range(ChronoField.DAY_OF_YEAR)); + assertEquals(LocalDate.from(date).range(ChronoField.ALIGNED_WEEK_OF_MONTH), + date.range(ChronoField.ALIGNED_WEEK_OF_MONTH)); + } + } + + @Test + public void test_MinguoDate_range_yeaOfEra() { + // YEAR_OF_ERA is the big difference to a LocalDate, all other ranges are the same. + assertEquals(ValueRange.of(1, ChronoField.YEAR.range().getMaximum() - YEARS_BEHIND), + MinguoDate.of(1, 1, 1).range(ChronoField.YEAR_OF_ERA)); + assertEquals(ValueRange.of(1, -ChronoField.YEAR.range().getMinimum() + 1 + YEARS_BEHIND), + MinguoDate.of(-1, 1, 1).range(ChronoField.YEAR_OF_ERA)); + } + + @Test + public void test_MinguoDate_getLong() { + MinguoDate date = MinguoDate.of(10, 2, 5); + assertEquals(10, date.getLong(ChronoField.YEAR_OF_ERA)); + assertEquals(10, date.getLong(ChronoField.YEAR)); + assertEquals(2, date.getLong(ChronoField.MONTH_OF_YEAR)); + assertEquals(10*12 + 2 - 1, date.getLong(ChronoField.PROLEPTIC_MONTH)); + assertEquals(5, date.getLong(ChronoField.DAY_OF_MONTH)); + assertEquals(31 + 5, date.getLong(ChronoField.DAY_OF_YEAR)); + assertEquals(date.toEpochDay(), date.getLong(ChronoField.EPOCH_DAY)); + } +} diff --git a/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistChronologyTest.java new file mode 100644 index 000000000..20e048ec6 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistChronologyTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.chrono; + +import org.junit.Test; +import java.time.LocalDate; +import java.time.chrono.ThaiBuddhistDate; +import java.time.chrono.ThaiBuddhistChronology; +import java.time.chrono.ThaiBuddhistEra; +import java.time.temporal.ChronoField; +import java.time.temporal.ValueRange; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +/** + * Additional tests for {@link ThaiBuddhistDate}. + * + * @see tck.java.time.chrono.TCKThaiBuddhistChronology + */ +public class ThaiBuddhistChronologyTest { + + // year 2543 in Thai Buddhist calendar is 2000 in ISO calendar. + private static final int YEARS_AHEAD = 543; + + @Test + public void test_range() { + for (ChronoField field : ChronoField.values()) { + ValueRange expected; + switch (field) { + case PROLEPTIC_MONTH: + // Proleptic month values are shifted by YEARS_AHEAD * 12. + expected = ValueRange.of( + ChronoField.PROLEPTIC_MONTH.range().getMinimum() + YEARS_AHEAD * 12L, + ChronoField.PROLEPTIC_MONTH.range().getMaximum() + YEARS_AHEAD * 12L); + break; + case YEAR_OF_ERA: + // range for era BE is 1.. + // range for era before BE is 1..<-yearRange.min + 1 + OFFSET> + expected = ValueRange + .of(1, -ChronoField.YEAR.range().getMinimum() + 1 - YEARS_AHEAD, + ChronoField.YEAR.range().getMaximum() + YEARS_AHEAD); + break; + case YEAR: + // Proleptic year values are shifted by YEAR. + expected = ValueRange.of(ChronoField.YEAR.range().getMinimum() + YEARS_AHEAD, + ChronoField.YEAR.range().getMaximum() + YEARS_AHEAD); + break; + default: + // All other fields have the same ranges as ISO. + expected = field.range(); + break; + } + assertEquals("Range of " + field, expected, + ThaiBuddhistChronology.INSTANCE.range(field)); + } + } + + @Test + public void test_ThaiBuddhistDate_getChronology() { + assertSame(ThaiBuddhistChronology.INSTANCE, ThaiBuddhistDate.now().getChronology()); + } + + @Test + public void test_ThaiBuddhistDate_getEra() { + assertEquals(ThaiBuddhistEra.BEFORE_BE, ThaiBuddhistDate.of(-1, 1, 1).getEra()); + assertEquals(ThaiBuddhistEra.BE, ThaiBuddhistDate.of(1, 1, 1).getEra()); + } + + @Test + public void test_ThaiBuddhistDate_range() { + ThaiBuddhistDate dates[] = new ThaiBuddhistDate[] { + ThaiBuddhistDate.from(LocalDate.of(2000, 2, 1)), //February of a leap year + ThaiBuddhistDate.from(LocalDate.of(2001, 2, 1)), //February of a non-leap year + ThaiBuddhistDate.of(1, 2, 3), + ThaiBuddhistDate.of(4, 5, 6), + ThaiBuddhistDate.of(-7, 8, 9) + }; + + for (ThaiBuddhistDate date : dates) { + // only these three ChronoFields and YEAR_OF_ERA (below) have date-dependent ranges. + assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_MONTH), + date.range(ChronoField.DAY_OF_MONTH)); + assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_YEAR), + date.range(ChronoField.DAY_OF_YEAR)); + assertEquals(LocalDate.from(date).range(ChronoField.ALIGNED_WEEK_OF_MONTH), + date.range(ChronoField.ALIGNED_WEEK_OF_MONTH)); + } + } + + @Test + public void test_ThaiBuddhistDate_range_yeaOfEra() { + // YEAR_OF_ERA is the big difference to a LocalDate, all other ranges are the same. + assertEquals(ValueRange.of(1, ChronoField.YEAR.range().getMaximum() + YEARS_AHEAD), + ThaiBuddhistDate.of(1, 1, 1).range(ChronoField.YEAR_OF_ERA)); + assertEquals(ValueRange.of(1, -ChronoField.YEAR.range().getMinimum() + 1 - YEARS_AHEAD), + ThaiBuddhistDate.of(-1, 1, 1).range(ChronoField.YEAR_OF_ERA)); + } + + @Test + public void test_ThaiBuddhistDate_getLong() { + ThaiBuddhistDate date = ThaiBuddhistDate.of(10, 2, 5); + assertEquals(10, date.getLong(ChronoField.YEAR_OF_ERA)); + assertEquals(10, date.getLong(ChronoField.YEAR)); + assertEquals(2, date.getLong(ChronoField.MONTH_OF_YEAR)); + assertEquals(10 * 12 + 2 - 1, date.getLong(ChronoField.PROLEPTIC_MONTH)); + assertEquals(5, date.getLong(ChronoField.DAY_OF_MONTH)); + assertEquals(31 + 5, date.getLong(ChronoField.DAY_OF_YEAR)); + assertEquals(date.toEpochDay(), date.getLong(ChronoField.EPOCH_DAY)); + } +} diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeFormatterBuilderTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterBuilderTest.java new file mode 100644 index 000000000..215f4837f --- /dev/null +++ b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterBuilderTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.format; + +import org.junit.Test; +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.Month; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.TemporalQueries; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * Additional tests for {@link DateTimeFormatterBuilder}. + * + * @see tck.java.time.format.TCKDateTimeFormatterBuilder + * @see test.java.time.format.TestDateTimeFormatterBuilder + */ +public class DateTimeFormatterBuilderTest { + + @Test + public void test_append_DateTimeFormatter() { + DateTimeFormatter formatter = new DateTimeFormatterBuilder() + .appendLiteral('<').append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('>') + .toFormatter(Locale.ROOT); + assertEquals("<2000-12-31>", formatter.format(LocalDate.of(2000, Month.DECEMBER, 31))); + } + + @Test + public void test_appendZoneRegionId_format() { + DateTimeFormatter formatter = + new DateTimeFormatterBuilder().appendZoneRegionId().toFormatter(); + + assertEquals("Europe/London", + formatter.format(ZonedDateTime.now(ZoneId.of("Europe/London")))); + assertEquals("UTC", + formatter.format(ZonedDateTime.now(ZoneId.of("UTC")))); + } + + @Test + public void test_appendZoneRegionId_format_offset() { + DateTimeFormatter formatter = + new DateTimeFormatterBuilder().appendZoneRegionId().toFormatter(); + + try { + formatter.format(OffsetDateTime.now(ZoneOffset.UTC)); + fail("Formatted ZoneOffset using appendZoneRegionId formatter"); + } catch (DateTimeException expected) { + } + } + + @Test + public void test_appendZoneRegionId_parse() { + DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendZoneRegionId() + .toFormatter(); + + assertEquals(ZoneId.of("Europe/London"), + formatter.parse("Europe/London").query(TemporalQueries.zoneId())); + assertEquals(ZoneId.of("UTC"), + formatter.parse("UTC").query(TemporalQueries.zoneId())); + assertEquals(ZoneId.of("GMT+1"), + formatter.parse("GMT+01:00").query(TemporalQueries.zoneId())); + // Note that the JavaDoc for appendZoneRegionId() suggests that this should return a + // ZoneOffset, but that documentation seems to be wrong (see http://b/35665981). + assertEquals(ZoneId.of("UTC+01:00"), + formatter.parse("UTC+01:00").query(TemporalQueries.zoneId())); + // Parsing a "bare metal" offset without prefix will return a ZoneOffset. + assertEquals(ZoneOffset.ofHours(1), + formatter.parse("+01:00").query(TemporalQueries.zoneId())); + } +} diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java new file mode 100644 index 000000000..0e57178f8 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.format; + +import org.junit.Test; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DecimalStyle; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +/** + * Additional tests for {@link DateTimeFormatter}. + * + * @see tck.java.time.format.TCKDateTimeFormatter + * @see test.java.time.format.TestDateTimeFormatter + */ +public class DateTimeFormatterTest { + + @Test + public void test_getDecimalStyle() { + Locale arLocale = Locale.forLanguageTag("ar"); + DateTimeFormatter[] formatters = new DateTimeFormatter[] { + DateTimeFormatter.ISO_DATE, + DateTimeFormatter.RFC_1123_DATE_TIME, + new DateTimeFormatterBuilder().toFormatter(), + new DateTimeFormatterBuilder().toFormatter(Locale.ROOT), + new DateTimeFormatterBuilder().toFormatter(Locale.ENGLISH), + new DateTimeFormatterBuilder().toFormatter(arLocale), + }; + + DecimalStyle arDecimalStyle = DecimalStyle.of(arLocale); + // Verify that the Locale ar returns a DecimalStyle other than STANDARD. + assertNotEquals(DecimalStyle.STANDARD, arDecimalStyle); + + for (DateTimeFormatter formatter : formatters) { + // All DateTimeFormatters should use the standard style, unless explicitly changed. + assertEquals(formatter.toString(), DecimalStyle.STANDARD, formatter.getDecimalStyle()); + + DateTimeFormatter arStyleFormatter = formatter.withDecimalStyle(arDecimalStyle); + assertEquals(arStyleFormatter.toString(), + arDecimalStyle, arStyleFormatter.getDecimalStyle()); + + // Verify that calling withDecimalStyle() doesn't modify the original formatter. + assertEquals(formatter.toString(), DecimalStyle.STANDARD, formatter.getDecimalStyle()); + } + } +} diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeParseExceptionTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeParseExceptionTest.java new file mode 100644 index 000000000..edcd782f6 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/format/DateTimeParseExceptionTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.format; + +import org.junit.Test; +import java.time.format.DateTimeParseException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +/** + * Tests for {@link DateTimeParseException}. + */ +public class DateTimeParseExceptionTest { + @Test + public void test_constructor_message_parsedData_errorIndex() { + DateTimeParseException ex = + new DateTimeParseException("message", new StringBuilder("parsedData"), 42); + assertEquals("message", ex.getMessage()); + assertEquals("parsedData", ex.getParsedString()); + assertEquals(42, ex.getErrorIndex()); + assertNull(ex.getCause()); + } + + @Test + public void test_constructor_message_parsedData_errorIndex_cause() { + Throwable cause = new Exception(); + DateTimeParseException ex = + new DateTimeParseException("message", new StringBuilder("parsedData"), 42, cause); + assertEquals("message", ex.getMessage()); + assertEquals("parsedData", ex.getParsedString()); + assertEquals(42, ex.getErrorIndex()); + assertSame(cause, ex.getCause()); + } + + +} diff --git a/luni/src/test/java/libcore/java/time/temporal/UnsupportedTemporalTypeExceptionTest.java b/luni/src/test/java/libcore/java/time/temporal/UnsupportedTemporalTypeExceptionTest.java new file mode 100644 index 000000000..fa155df34 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/temporal/UnsupportedTemporalTypeExceptionTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.temporal; + +import org.junit.Test; +import java.time.temporal.UnsupportedTemporalTypeException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +/** + * Tests for {@link UnsupportedTemporalTypeException}. + */ +public class UnsupportedTemporalTypeExceptionTest { + @Test + public void test_constructor_message() { + UnsupportedTemporalTypeException ex = new UnsupportedTemporalTypeException("message"); + assertEquals("message", ex.getMessage()); + assertNull(ex.getCause()); + } + + @Test + public void test_constructor_message_cause() { + Throwable cause = new Exception(); + UnsupportedTemporalTypeException ex = + new UnsupportedTemporalTypeException("message", cause); + assertEquals("message", ex.getMessage()); + assertSame(cause, ex.getCause()); + } + + +} diff --git a/luni/src/test/java/libcore/java/time/zone/IcuZoneRulesProviderTest.java b/luni/src/test/java/libcore/java/time/zone/IcuZoneRulesProviderTest.java new file mode 100644 index 000000000..c3c0f50f6 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/zone/IcuZoneRulesProviderTest.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2016 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. + */ +package libcore.java.time.zone; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import android.icu.util.BasicTimeZone; +import android.icu.util.TimeZone; +import android.icu.util.TimeZoneRule; +import android.icu.util.TimeZoneTransition; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; +import java.time.zone.ZoneOffsetTransition; +import java.time.zone.ZoneRules; +import java.time.zone.ZoneRulesProvider; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; + +/** + * Test the {@link java.time.zone.IcuZoneRulesProvider}. + * + * It is indirectly tested via static methods in {@link ZoneRulesProvider} as all the relevant + * methods are protected. This test verifies that the rules returned by that provider behave + * equivalently to the ICU rules from which they are created. + */ +@RunWith(Parameterized.class) +public class IcuZoneRulesProviderTest { + + @Parameterized.Parameters(name = "{0}") + public static Iterable getZoneIds() { + Set availableZoneIds = ZoneRulesProvider.getAvailableZoneIds(); + assertFalse("no zones returned", availableZoneIds.isEmpty()); + return availableZoneIds; + } + + private final String zoneId; + + public IcuZoneRulesProviderTest(final String zoneId) { + this.zoneId = zoneId; + } + + /** + * Verifies that ICU and java.time return the same transitions before and after a pre-selected + * set of instants in time. + */ + @Test + public void testTransitionsNearInstants() { + // An arbitrary set of instants at which to test the offsets in both implementations. + Instant[] instants = new Instant[] { + LocalDateTime.of(1900, Month.DECEMBER, 24, 12, 0).toInstant(ZoneOffset.UTC), + LocalDateTime.of(1970, Month.JANUARY, 1, 2, 3).toInstant(ZoneOffset.UTC), + LocalDateTime.of(1980, Month.FEBRUARY, 4, 5, 6).toInstant(ZoneOffset.UTC), + LocalDateTime.of(1990, Month.MARCH, 7, 8, 9).toInstant(ZoneOffset.UTC), + LocalDateTime.of(2000, Month.APRIL, 10, 11, 12).toInstant(ZoneOffset.UTC), + LocalDateTime.of(2016, Month.MAY, 13, 14, 15).toInstant(ZoneOffset.UTC), + LocalDateTime.of(2020, Month.JUNE, 16, 17, 18).toInstant(ZoneOffset.UTC), + LocalDateTime.of(2100, Month.JULY, 19, 20, 21).toInstant(ZoneOffset.UTC), + // yes, adding "now" makes the test time-dependent, but it also ensures that future + // updates don't break on the then-current date. + Instant.now() + }; + // Coincidentally this test verifies that all zones can be converted to ZoneRules and + // don't violate any of the assumptions of IcuZoneRulesProvider. + ZoneRules rules = ZoneRulesProvider.getRules(zoneId, false); + BasicTimeZone timeZone = (BasicTimeZone) TimeZone.getTimeZone(zoneId); + + int[] icuOffsets = new int[2]; + for (Instant instant : instants) { + ZoneOffset offset = rules.getOffset(instant); + Duration daylightSavings = rules.getDaylightSavings(instant); + timeZone.getOffset(instant.toEpochMilli(), false, icuOffsets); + + assertEquals("total offset for " + zoneId + " at " + instant, + icuOffsets[1] + icuOffsets[0], offset.getTotalSeconds() * 1000); + assertEquals("dst offset for " + zoneId + " at " + instant, + icuOffsets[1], daylightSavings.toMillis()); + + ZoneOffsetTransition jtTrans; + TimeZoneTransition icuTrans; + + jtTrans = rules.nextTransition(instant); + icuTrans = timeZone.getNextTransition(instant.toEpochMilli(), false); + while (isIcuOnlyTransition(icuTrans)) { + icuTrans = timeZone.getNextTransition(icuTrans.getTime(), false); + } + assertEquivalent(icuTrans, jtTrans); + + jtTrans = rules.previousTransition(instant); + icuTrans = timeZone.getPreviousTransition(instant.toEpochMilli(), false); + // Find previous "real" transition. + while (isIcuOnlyTransition(icuTrans)) { + icuTrans = timeZone.getPreviousTransition(icuTrans.getTime(), false); + } + assertEquivalent(icuTrans, jtTrans); + } + } + + /** + * Verifies that ICU and java.time rules return the same transitions between 1900 and 2100. + */ + @Test + public void testAllTransitions() { + final Instant start = LocalDateTime.of(1900, Month.JANUARY, 1, 12, 0) + .toInstant(ZoneOffset.UTC); + // Many timezones have ongoing DST changes, so they would generate transitions endlessly. + // Pick a far-future end date to stop comparing in that case. + final Instant end = LocalDateTime.of(2100, Month.DECEMBER, 31, 12, 0) + .toInstant(ZoneOffset.UTC); + + ZoneRules rules = ZoneRulesProvider.getRules(zoneId, false); + BasicTimeZone timeZone = (BasicTimeZone) TimeZone.getTimeZone(zoneId); + + Instant instant = start; + while (instant.isBefore(end)) { + ZoneOffsetTransition jtTrans; + TimeZoneTransition icuTrans; + + jtTrans = rules.nextTransition(instant); + icuTrans = timeZone.getNextTransition(instant.toEpochMilli(), false); + while (isIcuOnlyTransition(icuTrans)) { + icuTrans = timeZone.getNextTransition(icuTrans.getTime(), false); + } + assertEquivalent(icuTrans, jtTrans); + if (jtTrans == null) { + break; + } + instant = jtTrans.getInstant(); + } + } + + /** + * Returns {@code true} iff this transition will only be returned by ICU code. + * ICU reports "no-op" transitions where the raw offset and the dst savings + * change by the same absolute value in opposite directions, java.time doesn't + * return them, so find the next "real" transition. + */ + private static boolean isIcuOnlyTransition(TimeZoneTransition transition) { + if (transition == null) { + return false; + } + return transition.getFrom().getRawOffset() + transition.getFrom().getDSTSavings() + == transition.getTo().getRawOffset() + transition.getTo().getDSTSavings(); + } + + /** + * Asserts that the ICU {@link TimeZoneTransition} is equivalent to the java.time {@link + * ZoneOffsetTransition}. + */ + private static void assertEquivalent( + TimeZoneTransition icuTransition, ZoneOffsetTransition jtTransition) { + if (icuTransition == null) { + assertNull(jtTransition); + return; + } + assertEquals("time of transition", + Instant.ofEpochMilli(icuTransition.getTime()), jtTransition.getInstant()); + TimeZoneRule from = icuTransition.getFrom(); + TimeZoneRule to = icuTransition.getTo(); + assertEquals("offset before", + (from.getDSTSavings() + from.getRawOffset()) / 1000, + jtTransition.getOffsetBefore().getTotalSeconds()); + assertEquals("offset after", + (to.getDSTSavings() + to.getRawOffset()) / 1000, + jtTransition.getOffsetAfter().getTotalSeconds()); + } +} diff --git a/luni/src/test/java/libcore/java/time/zone/ZoneOffsetTransitionTest.java b/luni/src/test/java/libcore/java/time/zone/ZoneOffsetTransitionTest.java new file mode 100644 index 000000000..6094afb7f --- /dev/null +++ b/luni/src/test/java/libcore/java/time/zone/ZoneOffsetTransitionTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.zone; + +import org.junit.Test; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.zone.ZoneOffsetTransition; + +import static org.junit.Assert.assertEquals; + +/** + * Additional tests for {@link ZoneOffsetTransition}. + * + * @see tck.java.time.zone.TCKZoneOffsetTransition + */ +public class ZoneOffsetTransitionTest { + + @Test + public void test_toEpochSeconds() { + LocalDateTime time = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0); + ZoneOffset offsetP1 = ZoneOffset.ofHours(1); + ZoneOffset offsetP2 = ZoneOffset.ofHours(2); + ZoneOffsetTransition transition = ZoneOffsetTransition.of(time, + /* offsetBefore */ offsetP1, /* offsetAfter */ offsetP2); + // toEpochSeconds must match the toEpochSeconds of the original time at the "offset before". + assertEquals( + OffsetDateTime.of(time, offsetP1).toEpochSecond(), + transition.toEpochSecond()); + + } + +} diff --git a/luni/src/test/java/libcore/java/time/zone/ZoneRulesExceptionTest.java b/luni/src/test/java/libcore/java/time/zone/ZoneRulesExceptionTest.java new file mode 100644 index 000000000..40051756c --- /dev/null +++ b/luni/src/test/java/libcore/java/time/zone/ZoneRulesExceptionTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.zone; + +import org.junit.Test; +import java.time.zone.ZoneRulesException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +/** + * Tests for {@link ZoneRulesException}. + */ +public class ZoneRulesExceptionTest { + + @Test + public void test_constructor_message() { + ZoneRulesException ex = new ZoneRulesException("message"); + assertEquals("message", ex.getMessage()); + assertNull(ex.getCause()); + } + + @Test + public void test_constructor_message_cause() { + Throwable cause = new Exception(); + ZoneRulesException ex = new ZoneRulesException("message", cause); + assertEquals("message", ex.getMessage()); + assertSame(cause, ex.getCause()); + } +} diff --git a/luni/src/test/java/libcore/java/time/zone/ZoneRulesTest.java b/luni/src/test/java/libcore/java/time/zone/ZoneRulesTest.java new file mode 100644 index 000000000..54b5bba75 --- /dev/null +++ b/luni/src/test/java/libcore/java/time/zone/ZoneRulesTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 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. + */ +package libcore.java.time.zone; + +import org.junit.Test; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; +import java.time.zone.ZoneRules; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * Additional tests for {@link ZoneRules}. + * + * @see tck.java.time.zone.TCKZoneRules + */ +public class ZoneRulesTest { + + @Test + public void test_of_ZoneOffset() { + ZoneOffset offset = ZoneOffset.MIN; + ZoneRules zoneRules = ZoneRules.of(offset); + + assertEquals(Collections.emptyList(), zoneRules.getTransitionRules()); + assertEquals(Collections.emptyList(), zoneRules.getTransitions()); + assertNull(zoneRules.nextTransition(Instant.MIN)); + + // Check various offsets at a bunch of instants, as they should be constant. + Instant[] instants = new Instant[] { + LocalDateTime.MIN.toInstant(offset), + Instant.EPOCH, + LocalDateTime.of(2000, Month.JANUARY, 1, 1, 1).toInstant(ZoneOffset.UTC), + Instant.now(), + LocalDateTime.MAX.toInstant(offset), + }; + + for (Instant instant : instants) { + assertEquals(Duration.ZERO, zoneRules.getDaylightSavings(instant)); + assertEquals(offset, zoneRules.getOffset(instant)); + assertEquals(offset, zoneRules.getStandardOffset(instant)); + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, offset); + assertNull(zoneRules.getTransition(localDateTime)); + assertEquals(Collections.singletonList(offset), + zoneRules.getValidOffsets(localDateTime)); + } + } + + @Test(expected = NullPointerException.class) + public void test_of_ZoneOffset_null() { + ZoneRules.of(null); + } +} diff --git a/luni/src/test/java/libcore/java/util/AbstractCollectionTest.java b/luni/src/test/java/libcore/java/util/AbstractCollectionTest.java index 2a9e5efce..4f6e170d9 100644 --- a/luni/src/test/java/libcore/java/util/AbstractCollectionTest.java +++ b/luni/src/test/java/libcore/java/util/AbstractCollectionTest.java @@ -18,6 +18,8 @@ import java.io.Serializable; import java.util.AbstractCollection; +import java.util.Collections; +import java.util.Iterator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.ConcurrentHashMap; import junit.framework.TestCase; @@ -55,4 +57,31 @@ public void test_toArray() throws Exception { reader.join(); mutator.join(); } + + // http://b/31052838 + public void test_empty_removeAll_null() { + try { + new EmptyCollection().removeAll(null); + fail("Should have thrown"); + } catch (NullPointerException expected) { + } + } + + // http://b/31052838 + public void test_empty_retainAll_null() { + try { + new EmptyCollection().retainAll(null); + fail("Should have thrown"); + } catch (NullPointerException expected) { + } + } + + /** + * An AbstractCollection that does not override removeAll() / retainAll(). + */ + private static class EmptyCollection extends AbstractCollection { + @Override public Iterator iterator() { return Collections.emptySet().iterator(); } + @Override public int size() { return 0; } + } + } diff --git a/luni/src/test/java/libcore/java/util/AbstractResourceLeakageDetectorTestCase.java b/luni/src/test/java/libcore/java/util/AbstractResourceLeakageDetectorTestCase.java deleted file mode 100644 index 5ea67d396..000000000 --- a/luni/src/test/java/libcore/java/util/AbstractResourceLeakageDetectorTestCase.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2014 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. - */ -package libcore.java.util; - -import junit.framework.TestCase; - -/** - * Ensures that resources used within a test are cleaned up; will detect problems with tests and - * also with runtime. - */ -public abstract class AbstractResourceLeakageDetectorTestCase extends TestCase { - /** - * The leakage detector. - */ - private ResourceLeakageDetector detector; - - @Override - protected void setUp() throws Exception { - detector = ResourceLeakageDetector.newDetector(); - } - - @Override - protected void tearDown() throws Exception { - // If available check for resource leakage. At this point it is impossible to determine - // whether the test has thrown an exception. If it has then the exception thrown by this - // could hide that test failure; it largely depends on the test runner. - if (detector != null) { - detector.checkForLeaks(); - } - } -} diff --git a/luni/src/test/java/libcore/java/util/Base64Test.java b/luni/src/test/java/libcore/java/util/Base64Test.java new file mode 100644 index 000000000..30aa177c6 --- /dev/null +++ b/luni/src/test/java/libcore/java/util/Base64Test.java @@ -0,0 +1,1138 @@ +/* + * Copyright (C) 2016 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. + * + */ +package libcore.java.util; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Base64.Decoder; +import java.util.Base64.Encoder; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import static java.nio.charset.StandardCharsets.US_ASCII; +import static java.util.Arrays.copyOfRange; + +public class Base64Test extends TestCase { + + /** + * The base 64 alphabet from RFC 4648 Table 1. + */ + private static final Set TABLE_1 = + Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList( + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + ))); + + /** + * The "URL and Filename safe" Base 64 Alphabet from RFC 4648 Table 2. + */ + private static final Set TABLE_2 = + Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList( + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' + ))); + + public void testAlphabet_plain() { + checkAlphabet(TABLE_1, "", Base64.getEncoder()); + } + + public void testAlphabet_mime() { + checkAlphabet(TABLE_1, "\r\n", Base64.getMimeEncoder()); + } + + public void testAlphabet_url() { + checkAlphabet(TABLE_2, "", Base64.getUrlEncoder()); + } + + private static void checkAlphabet(Set expectedAlphabet, String lineSeparator, + Encoder encoder) { + assertEquals("Base64 alphabet size must be 64 characters", 64, expectedAlphabet.size()); + byte[] bytes = new byte[256]; + for (int i = 0; i < 256; i++) { + bytes[i] = (byte) i; + } + Set actualAlphabet = new HashSet<>(); + + byte[] encodedBytes = encoder.encode(bytes); + // ignore the padding + int endIndex = encodedBytes.length; + while (endIndex > 0 && encodedBytes[endIndex - 1] == '=') { + endIndex--; + } + for (byte b : Arrays.copyOfRange(encodedBytes, 0, endIndex)) { + char c = (char) b; + actualAlphabet.add(c); + } + for (char c : lineSeparator.toCharArray()) { + assertTrue(actualAlphabet.remove(c)); + } + assertEquals(expectedAlphabet, actualAlphabet); + } + + /** + * Checks decoding of bytes containing a value outside of the allowed + * {@link #TABLE_1 "basic" alphabet}. + */ + public void testDecoder_extraChars_basic() throws Exception { + Decoder basicDecoder = Base64.getDecoder(); // uses Table 1 + // Check failure cases common to both RFC4648 Table 1 and Table 2 decoding. + checkDecoder_extraChars_common(basicDecoder); + + // Tests characters that are part of RFC4848 Table 2 but not Table 1. + assertDecodeThrowsIAe(basicDecoder, "_aGVsbG8sIHdvcmx"); + assertDecodeThrowsIAe(basicDecoder, "aGV_sbG8sIHdvcmx"); + assertDecodeThrowsIAe(basicDecoder, "aGVsbG8sIHdvcmx_"); + } + + /** + * Checks decoding of bytes containing a value outside of the allowed + * {@link #TABLE_2 url alphabet}. + */ + public void testDecoder_extraChars_url() throws Exception { + Decoder urlDecoder = Base64.getUrlDecoder(); // uses Table 2 + // Check failure cases common to both RFC4648 table 1 and table 2 decoding. + checkDecoder_extraChars_common(urlDecoder); + + // Tests characters that are part of RFC4848 Table 1 but not Table 2. + assertDecodeThrowsIAe(urlDecoder, "/aGVsbG8sIHdvcmx"); + assertDecodeThrowsIAe(urlDecoder, "aGV/sbG8sIHdvcmx"); + assertDecodeThrowsIAe(urlDecoder, "aGVsbG8sIHdvcmx/"); + } + + /** + * Checks characters that are bad both in RFC4648 {@link #TABLE_1} and + * in {@link #TABLE_2} based decoding. + */ + private static void checkDecoder_extraChars_common(Decoder decoder) throws Exception { + // Characters outside alphabet before padding. + assertDecodeThrowsIAe(decoder, " aGVsbG8sIHdvcmx"); + assertDecodeThrowsIAe(decoder, "aGV sbG8sIHdvcmx"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmx "); + assertDecodeThrowsIAe(decoder, "*aGVsbG8sIHdvcmx"); + assertDecodeThrowsIAe(decoder, "aGV*sbG8sIHdvcmx"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmx*"); + assertDecodeThrowsIAe(decoder, "\r\naGVsbG8sIHdvcmx"); + assertDecodeThrowsIAe(decoder, "aGV\r\nsbG8sIHdvcmx"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmx\r\n"); + assertDecodeThrowsIAe(decoder, "\naGVsbG8sIHdvcmx"); + assertDecodeThrowsIAe(decoder, "aGV\nsbG8sIHdvcmx"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmx\n"); + + // padding 0 + assertEquals("hello, world", decodeToAscii(decoder, "aGVsbG8sIHdvcmxk")); + // Extra padding + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxk="); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxk=="); + // Characters outside alphabet intermixed with (too much) padding. + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxk ="); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxk = = "); + + // padding 1 + assertEquals("hello, world?!", decodeToAscii(decoder, "aGVsbG8sIHdvcmxkPyE=")); + // Missing padding + assertEquals("hello, world?!", decodeToAscii(decoder, "aGVsbG8sIHdvcmxkPyE")); + // Characters outside alphabet before padding. + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE ="); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE*="); + // Trailing characters, otherwise valid. + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE= "); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE=*"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE=X"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE=XY"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE=XYZ"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE=XYZA"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE=\n"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE=\r\n"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE= "); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE=="); + // Characters outside alphabet intermixed with (too much) padding. + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE =="); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkPyE = = "); + + // padding 2 + assertEquals("hello, world.", decodeToAscii(decoder, "aGVsbG8sIHdvcmxkLg==")); + // Missing padding + assertEquals("hello, world.", decodeToAscii(decoder, "aGVsbG8sIHdvcmxkLg")); + // Partially missing padding + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg="); + // Characters outside alphabet before padding. + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg =="); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg*=="); + // Trailing characters, otherwise valid. + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg== "); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg==*"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg==X"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg==XY"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg==XYZ"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg==XYZA"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg==\n"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg==\r\n"); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg== "); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg==="); + // Characters outside alphabet inside padding. + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg= ="); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg=*="); + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg=\r\n="); + // Characters inside alphabet inside padding. + assertDecodeThrowsIAe(decoder, "aGVsbG8sIHdvcmxkLg=X="); + } + + public void testDecoder_extraChars_mime() throws Exception { + Decoder mimeDecoder = Base64.getMimeDecoder(); + + // Characters outside alphabet before padding. + assertEquals("hello, world", decodeToAscii(mimeDecoder, " aGVsbG8sIHdvcmxk")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGV sbG8sIHdvcmxk")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxk ")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "_aGVsbG8sIHdvcmxk")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGV_sbG8sIHdvcmxk")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxk_")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "*aGVsbG8sIHdvcmxk")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGV*sbG8sIHdvcmxk")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxk*")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "\r\naGVsbG8sIHdvcmxk")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGV\r\nsbG8sIHdvcmxk")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxk\r\n")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "\naGVsbG8sIHdvcmxk")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGV\nsbG8sIHdvcmxk")); + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxk\n")); + + // padding 0 + assertEquals("hello, world", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxk")); + // Extra padding + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxk="); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxk=="); + // Characters outside alphabet intermixed with (too much) padding. + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxk ="); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxk = = "); + + // padding 1 + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE=")); + // Missing padding + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE")); + // Characters outside alphabet before padding. + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE =")); + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE*=")); + // Trailing characters, otherwise valid. + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE= ")); + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE=*")); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkPyE=X"); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkPyE=XY"); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkPyE=XYZ"); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkPyE=XYZA"); + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE=\n")); + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE=\r\n")); + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE= ")); + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE==")); + // Characters outside alphabet intermixed with (too much) padding. + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE ==")); + assertEquals("hello, world?!", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkPyE = = ")); + + // padding 2 + assertEquals("hello, world.", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkLg==")); + // Missing padding + assertEquals("hello, world.", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkLg")); + // Partially missing padding + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkLg="); + // Characters outside alphabet before padding. + assertEquals("hello, world.", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkLg ==")); + assertEquals("hello, world.", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkLg*==")); + // Trailing characters, otherwise valid. + assertEquals("hello, world.", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkLg== ")); + assertEquals("hello, world.", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkLg==*")); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkLg==X"); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkLg==XY"); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkLg==XYZ"); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkLg==XYZA"); + assertEquals("hello, world.", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkLg==\n")); + assertEquals("hello, world.", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkLg==\r\n")); + assertEquals("hello, world.", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkLg== ")); + assertEquals("hello, world.", decodeToAscii(mimeDecoder, "aGVsbG8sIHdvcmxkLg===")); + + // Characters outside alphabet inside padding are not allowed by the MIME decoder. + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkLg= ="); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkLg=*="); + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkLg=\r\n="); + + // Characters inside alphabet inside padding. + assertDecodeThrowsIAe(mimeDecoder, "aGVsbG8sIHdvcmxkLg=X="); + } + + public void testDecoder_nonPrintableBytes_basic() throws Exception { + checkDecoder_nonPrintableBytes_table1(Base64.getDecoder()); + } + + public void testDecoder_nonPrintableBytes_mime() throws Exception { + checkDecoder_nonPrintableBytes_table1(Base64.getMimeDecoder()); + } + + /** + * Check decoding sample non-ASCII byte[] values from a {@link #TABLE_1} + * encoded String. + */ + private static void checkDecoder_nonPrintableBytes_table1(Decoder decoder) throws Exception { + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 0, decoder.decode("")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 1, decoder.decode("/w==")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 2, decoder.decode("/+4=")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 3, decoder.decode("/+7d")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 4, decoder.decode("/+7dzA==")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 5, decoder.decode("/+7dzLs=")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 6, decoder.decode("/+7dzLuq")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 7, decoder.decode("/+7dzLuqmQ==")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 8, decoder.decode("/+7dzLuqmYg=")); + } + + /** + * Check decoding sample non-ASCII byte[] values from a {@link #TABLE_2} + * (url safe) encoded String. + */ + public void testDecoder_nonPrintableBytes_url() throws Exception { + Decoder decoder = Base64.getUrlDecoder(); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 0, decoder.decode("")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 1, decoder.decode("_w==")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 2, decoder.decode("_-4=")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 3, decoder.decode("_-7d")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 4, decoder.decode("_-7dzA==")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 5, decoder.decode("_-7dzLs=")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 6, decoder.decode("_-7dzLuq")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 7, decoder.decode("_-7dzLuqmQ==")); + assertArrayPrefixEquals(SAMPLE_NON_ASCII_BYTES, 8, decoder.decode("_-7dzLuqmYg=")); + } + + private static final byte[] SAMPLE_NON_ASCII_BYTES = { (byte) 0xff, (byte) 0xee, (byte) 0xdd, + (byte) 0xcc, (byte) 0xbb, (byte) 0xaa, + (byte) 0x99, (byte) 0x88, (byte) 0x77 }; + + public void testDecoder_closedStream() { + try { + closedDecodeStream().available(); + fail("Should have thrown"); + } catch (IOException expected) { + } + try { + closedDecodeStream().read(); + fail("Should have thrown"); + } catch (IOException expected) { + } + try { + closedDecodeStream().read(new byte[23]); + fail("Should have thrown"); + } catch (IOException expected) { + } + + try { + closedDecodeStream().read(new byte[23], 0, 1); + fail("Should have thrown"); + } catch (IOException expected) { + } + } + + private static InputStream closedDecodeStream() { + InputStream result = Base64.getDecoder().wrap(new ByteArrayInputStream(new byte[0])); + try { + result.close(); + } catch (IOException e) { + fail(e.getMessage()); + } + return result; + } + + /** + * Tests {@link Decoder#decode(byte[], byte[])} for correctness as well as + * for consistency with other methods tested elsewhere. + */ + public void testDecoder_decodeArrayToArray() { + Decoder decoder = Base64.getDecoder(); + + // Empty input + assertEquals(0, decoder.decode(new byte[0], new byte[0])); + + // Test data for non-empty input + String inputString = "YWJjZWZnaGk="; + byte[] input = inputString.getBytes(US_ASCII); + String expectedString = "abcefghi"; + byte[] decodedBytes = expectedString.getBytes(US_ASCII); + // check test data consistency with other methods that are tested elsewhere + assertRoundTrip(Base64.getEncoder(), decoder, inputString, decodedBytes); + + // Non-empty input: output array too short + byte[] tooShort = new byte[decodedBytes.length - 1]; + try { + decoder.decode(input, tooShort); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Non-empty input: output array longer than required + byte[] tooLong = new byte[decodedBytes.length + 1]; + int tooLongBytesDecoded = decoder.decode(input, tooLong); + assertEquals(decodedBytes.length, tooLongBytesDecoded); + assertEquals(0, tooLong[tooLong.length - 1]); + assertArrayPrefixEquals(tooLong, decodedBytes.length, decodedBytes); + + // Non-empty input: output array has exact minimum required size + byte[] justRight = new byte[decodedBytes.length]; + int justRightBytesDecoded = decoder.decode(input, justRight); + assertEquals(decodedBytes.length, justRightBytesDecoded); + assertArrayEquals(decodedBytes, justRight); + + } + + public void testDecoder_decodeByteBuffer() { + Decoder decoder = Base64.getDecoder(); + + byte[] emptyByteArray = new byte[0]; + ByteBuffer emptyByteBuffer = ByteBuffer.wrap(emptyByteArray); + ByteBuffer emptyDecodedBuffer = decoder.decode(emptyByteBuffer); + assertEquals(emptyByteBuffer, emptyDecodedBuffer); + assertNotSame(emptyByteArray, emptyDecodedBuffer); + + // Test the two types of byte buffer. + String inputString = "YWJjZWZnaGk="; + byte[] input = inputString.getBytes(US_ASCII); + String expectedString = "abcefghi"; + byte[] expectedBytes = expectedString.getBytes(US_ASCII); + + ByteBuffer inputBuffer = ByteBuffer.allocate(input.length); + inputBuffer.put(input); + inputBuffer.position(0); + checkDecoder_decodeByteBuffer(decoder, inputBuffer, expectedBytes); + + inputBuffer = ByteBuffer.allocateDirect(input.length); + inputBuffer.put(input); + inputBuffer.position(0); + checkDecoder_decodeByteBuffer(decoder, inputBuffer, expectedBytes); + } + + private static void checkDecoder_decodeByteBuffer( + Decoder decoder, ByteBuffer inputBuffer, byte[] expectedBytes) { + assertEquals(0, inputBuffer.position()); + assertEquals(inputBuffer.remaining(), inputBuffer.limit()); + int inputLength = inputBuffer.remaining(); + + ByteBuffer decodedBuffer = decoder.decode(inputBuffer); + + assertEquals(inputLength, inputBuffer.position()); + assertEquals(0, inputBuffer.remaining()); + assertEquals(inputLength, inputBuffer.limit()); + assertEquals(0, decodedBuffer.position()); + assertEquals(expectedBytes.length, decodedBuffer.remaining()); + assertEquals(expectedBytes.length, decodedBuffer.limit()); + } + + public void testDecoder_decodeByteBuffer_invalidData() { + Decoder decoder = Base64.getDecoder(); + + // Test the two types of byte buffer. + String inputString = "AAAA AAAA"; + byte[] input = inputString.getBytes(US_ASCII); + + ByteBuffer inputBuffer = ByteBuffer.allocate(input.length); + inputBuffer.put(input); + inputBuffer.position(0); + checkDecoder_decodeByteBuffer_invalidData(decoder, inputBuffer); + + inputBuffer = ByteBuffer.allocateDirect(input.length); + inputBuffer.put(input); + inputBuffer.position(0); + checkDecoder_decodeByteBuffer_invalidData(decoder, inputBuffer); + } + + private static void checkDecoder_decodeByteBuffer_invalidData( + Decoder decoder, ByteBuffer inputBuffer) { + assertEquals(0, inputBuffer.position()); + assertEquals(inputBuffer.remaining(), inputBuffer.limit()); + int limit = inputBuffer.limit(); + + try { + decoder.decode(inputBuffer); + fail(); + } catch (IllegalArgumentException expected) { + } + + assertEquals(0, inputBuffer.position()); + assertEquals(limit, inputBuffer.remaining()); + assertEquals(limit, inputBuffer.limit()); + } + + public void testDecoder_nullArgs() { + checkDecoder_nullArgs(Base64.getDecoder()); + checkDecoder_nullArgs(Base64.getMimeDecoder()); + checkDecoder_nullArgs(Base64.getUrlDecoder()); + } + + private static void checkDecoder_nullArgs(Decoder decoder) { + assertThrowsNpe(() -> decoder.decode((byte[]) null)); + assertThrowsNpe(() -> decoder.decode((String) null)); + assertThrowsNpe(() -> decoder.decode(null, null)); + assertThrowsNpe(() -> decoder.decode((ByteBuffer) null)); + assertThrowsNpe(() -> decoder.wrap(null)); + } + + public void testEncoder_nullArgs() { + checkEncoder_nullArgs(Base64.getEncoder()); + checkEncoder_nullArgs(Base64.getMimeEncoder()); + checkEncoder_nullArgs(Base64.getUrlEncoder()); + checkEncoder_nullArgs(Base64.getMimeEncoder(20, new byte[] { '*' })); + checkEncoder_nullArgs(Base64.getEncoder().withoutPadding()); + checkEncoder_nullArgs(Base64.getMimeEncoder().withoutPadding()); + checkEncoder_nullArgs(Base64.getUrlEncoder().withoutPadding()); + checkEncoder_nullArgs(Base64.getMimeEncoder(20, new byte[] { '*' }).withoutPadding()); + + } + + private static void checkEncoder_nullArgs(Encoder encoder) { + assertThrowsNpe(() -> encoder.encode((byte[]) null)); + assertThrowsNpe(() -> encoder.encodeToString(null)); + assertThrowsNpe(() -> encoder.encode(null, null)); + assertThrowsNpe(() -> encoder.encode((ByteBuffer) null)); + assertThrowsNpe(() -> encoder.wrap(null)); + } + + public void testEncoder_nonPrintableBytes() throws Exception { + Encoder encoder = Base64.getUrlEncoder(); + assertEquals("", encoder.encodeToString(copyOfRange(SAMPLE_NON_ASCII_BYTES, 0, 0))); + assertEquals("_w==", encoder.encodeToString(copyOfRange(SAMPLE_NON_ASCII_BYTES, 0, 1))); + assertEquals("_-4=", encoder.encodeToString(copyOfRange(SAMPLE_NON_ASCII_BYTES, 0, 2))); + assertEquals("_-7d", encoder.encodeToString(copyOfRange(SAMPLE_NON_ASCII_BYTES, 0, 3))); + assertEquals("_-7dzA==", encoder.encodeToString(copyOfRange(SAMPLE_NON_ASCII_BYTES, 0, 4))); + assertEquals("_-7dzLs=", encoder.encodeToString(copyOfRange(SAMPLE_NON_ASCII_BYTES, 0, 5))); + assertEquals("_-7dzLuq", encoder.encodeToString(copyOfRange(SAMPLE_NON_ASCII_BYTES, 0, 6))); + assertEquals("_-7dzLuqmQ==", encoder.encodeToString(copyOfRange(SAMPLE_NON_ASCII_BYTES, 0, 7))); + assertEquals("_-7dzLuqmYg=", encoder.encodeToString(copyOfRange(SAMPLE_NON_ASCII_BYTES, 0, 8))); + } + + public void testEncoder_lineLength() throws Exception { + String in_56 = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd"; + String in_57 = in_56 + "e"; + String in_58 = in_56 + "ef"; + String in_59 = in_56 + "efg"; + String in_60 = in_56 + "efgh"; + String in_61 = in_56 + "efghi"; + + String prefix = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5emFi"; + String out_56 = prefix + "Y2Q="; + String out_57 = prefix + "Y2Rl"; + String out_58 = prefix + "Y2Rl\r\nZg=="; + String out_59 = prefix + "Y2Rl\r\nZmc="; + String out_60 = prefix + "Y2Rl\r\nZmdo"; + String out_61 = prefix + "Y2Rl\r\nZmdoaQ=="; + + Encoder encoder = Base64.getMimeEncoder(); + Decoder decoder = Base64.getMimeDecoder(); + assertEquals("", encodeFromAscii(encoder, decoder, "")); + assertEquals(out_56, encodeFromAscii(encoder, decoder, in_56)); + assertEquals(out_57, encodeFromAscii(encoder, decoder, in_57)); + assertEquals(out_58, encodeFromAscii(encoder, decoder, in_58)); + assertEquals(out_59, encodeFromAscii(encoder, decoder, in_59)); + assertEquals(out_60, encodeFromAscii(encoder, decoder, in_60)); + assertEquals(out_61, encodeFromAscii(encoder, decoder, in_61)); + + encoder = Base64.getUrlEncoder(); + decoder = Base64.getUrlDecoder(); + assertEquals(out_56.replaceAll("\r\n", ""), encodeFromAscii(encoder, decoder, in_56)); + assertEquals(out_57.replaceAll("\r\n", ""), encodeFromAscii(encoder, decoder, in_57)); + assertEquals(out_58.replaceAll("\r\n", ""), encodeFromAscii(encoder, decoder, in_58)); + assertEquals(out_59.replaceAll("\r\n", ""), encodeFromAscii(encoder, decoder, in_59)); + assertEquals(out_60.replaceAll("\r\n", ""), encodeFromAscii(encoder, decoder, in_60)); + assertEquals(out_61.replaceAll("\r\n", ""), encodeFromAscii(encoder, decoder, in_61)); + } + + public void testGetMimeEncoder_invalidLineSeparator() { + byte[] invalidLineSeparator = { 'A' }; + try { + Base64.getMimeEncoder(20, invalidLineSeparator); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + Base64.getMimeEncoder(0, invalidLineSeparator); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + Base64.getMimeEncoder(20, null); + fail(); + } catch (NullPointerException expected) { + } + + try { + Base64.getMimeEncoder(0, null); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testEncoder_closedStream() { + try { + closedEncodeStream().write(100); + fail("Should have thrown"); + } catch (IOException expected) { + } + try { + closedEncodeStream().write(new byte[100]); + fail("Should have thrown"); + } catch (IOException expected) { + } + + try { + closedEncodeStream().write(new byte[100], 0, 1); + fail("Should have thrown"); + } catch (IOException expected) { + } + } + + private static OutputStream closedEncodeStream() { + OutputStream result = Base64.getEncoder().wrap(new ByteArrayOutputStream()); + try { + result.close(); + } catch (IOException e) { + fail(e.getMessage()); + } + return result; + } + + + /** + * Tests {@link Decoder#decode(byte[], byte[])} for correctness. + */ + public void testEncoder_encodeArrayToArray() { + Encoder encoder = Base64.getEncoder(); + + // Empty input + assertEquals(0, encoder.encode(new byte[0], new byte[0])); + + // Test data for non-empty input + byte[] input = "abcefghi".getBytes(US_ASCII); + String expectedString = "YWJjZWZnaGk="; + byte[] encodedBytes = expectedString.getBytes(US_ASCII); + + // Non-empty input: output array too short + byte[] tooShort = new byte[encodedBytes.length - 1]; + try { + encoder.encode(input, tooShort); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Non-empty input: output array longer than required + byte[] tooLong = new byte[encodedBytes.length + 1]; + int tooLongBytesEncoded = encoder.encode(input, tooLong); + assertEquals(encodedBytes.length, tooLongBytesEncoded); + assertEquals(0, tooLong[tooLong.length - 1]); + assertArrayPrefixEquals(tooLong, encodedBytes.length, encodedBytes); + + // Non-empty input: output array has exact minimum required size + byte[] justRight = new byte[encodedBytes.length]; + int justRightBytesEncoded = encoder.encode(input, justRight); + assertEquals(encodedBytes.length, justRightBytesEncoded); + assertArrayEquals(encodedBytes, justRight); + } + + public void testEncoder_encodeByteBuffer() { + Encoder encoder = Base64.getEncoder(); + + byte[] emptyByteArray = new byte[0]; + ByteBuffer emptyByteBuffer = ByteBuffer.wrap(emptyByteArray); + ByteBuffer emptyEncodedBuffer = encoder.encode(emptyByteBuffer); + assertEquals(emptyByteBuffer, emptyEncodedBuffer); + assertNotSame(emptyByteArray, emptyEncodedBuffer); + + // Test the two types of byte buffer. + byte[] input = "abcefghi".getBytes(US_ASCII); + String expectedString = "YWJjZWZnaGk="; + byte[] expectedBytes = expectedString.getBytes(US_ASCII); + + ByteBuffer inputBuffer = ByteBuffer.allocate(input.length); + inputBuffer.put(input); + inputBuffer.position(0); + testEncoder_encodeByteBuffer(encoder, inputBuffer, expectedBytes); + + inputBuffer = ByteBuffer.allocateDirect(input.length); + inputBuffer.put(input); + inputBuffer.position(0); + testEncoder_encodeByteBuffer(encoder, inputBuffer, expectedBytes); + } + + private static void testEncoder_encodeByteBuffer( + Encoder encoder, ByteBuffer inputBuffer, byte[] expectedBytes) { + assertEquals(0, inputBuffer.position()); + assertEquals(inputBuffer.remaining(), inputBuffer.limit()); + int inputLength = inputBuffer.remaining(); + + ByteBuffer encodedBuffer = encoder.encode(inputBuffer); + + assertEquals(inputLength, inputBuffer.position()); + assertEquals(0, inputBuffer.remaining()); + assertEquals(inputLength, inputBuffer.limit()); + assertEquals(0, encodedBuffer.position()); + assertEquals(expectedBytes.length, encodedBuffer.remaining()); + assertEquals(expectedBytes.length, encodedBuffer.limit()); + } + + /** + * Checks that all encoders/decoders map {@code new byte[0]} to "" and vice versa. + */ + public void testRoundTrip_empty() { + checkRoundTrip_empty(Base64.getEncoder(), Base64.getDecoder()); + checkRoundTrip_empty(Base64.getMimeEncoder(), Base64.getMimeDecoder()); + byte[] sep = new byte[] { '\r', '\n' }; + checkRoundTrip_empty(Base64.getMimeEncoder(-1, sep), Base64.getMimeDecoder()); + checkRoundTrip_empty(Base64.getMimeEncoder(20, new byte[0]), Base64.getMimeDecoder()); + checkRoundTrip_empty(Base64.getMimeEncoder(23, sep), Base64.getMimeDecoder()); + checkRoundTrip_empty(Base64.getMimeEncoder(76, sep), Base64.getMimeDecoder()); + checkRoundTrip_empty(Base64.getUrlEncoder(), Base64.getUrlDecoder()); + } + + private static void checkRoundTrip_empty(Encoder encoder, Decoder decoder) { + assertRoundTrip(encoder, decoder, "", new byte[0]); + } + + /** + * Encoding of byte values 0..255 using the non-URL alphabet. + */ + private static final String ALL_BYTE_VALUES_ENCODED = + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4" + + "OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3Bx" + + "cnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmq" + + "q6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj" + + "5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="; + + public void testRoundTrip_allBytes_plain() { + checkRoundTrip_allBytes_singleLine(Base64.getEncoder(), Base64.getDecoder()); + } + + /** + * Checks that if the lineSeparator is empty or the line length is {@code <= 3} + * or larger than the data to be encoded, a single line is returned. + */ + public void testRoundTrip_allBytes_mime_singleLine() { + Decoder decoder = Base64.getMimeDecoder(); + checkRoundTrip_allBytes_singleLine(Base64.getMimeEncoder(76, new byte[0]), decoder); + + // Line lengths <= 3 mean no wrapping; the separator is ignored in that case. + byte[] separator = new byte[] { '*' }; + checkRoundTrip_allBytes_singleLine(Base64.getMimeEncoder(Integer.MIN_VALUE, separator), + decoder); + checkRoundTrip_allBytes_singleLine(Base64.getMimeEncoder(-1, separator), decoder); + checkRoundTrip_allBytes_singleLine(Base64.getMimeEncoder(0, separator), decoder); + checkRoundTrip_allBytes_singleLine(Base64.getMimeEncoder(1, separator), decoder); + checkRoundTrip_allBytes_singleLine(Base64.getMimeEncoder(2, separator), decoder); + checkRoundTrip_allBytes_singleLine(Base64.getMimeEncoder(3, separator), decoder); + + // output fits into the permitted line length + checkRoundTrip_allBytes_singleLine(Base64.getMimeEncoder( + ALL_BYTE_VALUES_ENCODED.length(), separator), decoder); + checkRoundTrip_allBytes_singleLine(Base64.getMimeEncoder(Integer.MAX_VALUE, separator), + decoder); + } + + /** + * Checks round-trip encoding/decoding for a few simple examples that + * should work the same across three Encoder/Decoder pairs: This is + * because they only use characters that are in both RFC 4648 Table 1 + * and Table 2, and are short enough to fit into a single line. + */ + public void testRoundTrip_simple_basic() throws Exception { + // uses Table 1, never adds linebreaks + checkRoundTrip_simple(Base64.getEncoder(), Base64.getDecoder()); + // uses Table 1, allows 76 chars in a line + checkRoundTrip_simple(Base64.getMimeEncoder(), Base64.getMimeDecoder()); + // uses Table 2, never adds linebreaks + checkRoundTrip_simple(Base64.getUrlEncoder(), Base64.getUrlDecoder()); + } + + private static void checkRoundTrip_simple(Encoder encoder, Decoder decoder) throws Exception { + assertRoundTrip(encoder, decoder, "YQ==", "a".getBytes(US_ASCII)); + assertRoundTrip(encoder, decoder, "YWI=", "ab".getBytes(US_ASCII)); + assertRoundTrip(encoder, decoder, "YWJj", "abc".getBytes(US_ASCII)); + assertRoundTrip(encoder, decoder, "YWJjZA==", "abcd".getBytes(US_ASCII)); + } + + /** check a range of possible line lengths */ + public void testRoundTrip_allBytes_mime_lineLength() { + Decoder decoder = Base64.getMimeDecoder(); + byte[] separator = new byte[] { '*' }; + checkRoundTrip_allBytes(Base64.getMimeEncoder(4, separator), decoder, + wrapLines("*", ALL_BYTE_VALUES_ENCODED, 4)); + checkRoundTrip_allBytes(Base64.getMimeEncoder(8, separator), decoder, + wrapLines("*", ALL_BYTE_VALUES_ENCODED, 8)); + checkRoundTrip_allBytes(Base64.getMimeEncoder(20, separator), decoder, + wrapLines("*", ALL_BYTE_VALUES_ENCODED, 20)); + checkRoundTrip_allBytes(Base64.getMimeEncoder(100, separator), decoder, + wrapLines("*", ALL_BYTE_VALUES_ENCODED, 100)); + checkRoundTrip_allBytes(Base64.getMimeEncoder(Integer.MAX_VALUE & ~3, separator), decoder, + wrapLines("*", ALL_BYTE_VALUES_ENCODED, Integer.MAX_VALUE & ~3)); + } + + public void testRoundTrip_allBytes_mime_lineLength_defaultsTo76Chars() { + checkRoundTrip_allBytes(Base64.getMimeEncoder(), Base64.getMimeDecoder(), + wrapLines("\r\n", ALL_BYTE_VALUES_ENCODED, 76)); + } + + /** + * checks that the specified line length is rounded down to the nearest multiple of 4. + */ + public void testRoundTrip_allBytes_mime_lineLength_isRoundedDown() { + Decoder decoder = Base64.getMimeDecoder(); + byte[] separator = new byte[] { '\r', '\n' }; + checkRoundTrip_allBytes(Base64.getMimeEncoder(60, separator), decoder, + wrapLines("\r\n", ALL_BYTE_VALUES_ENCODED, 60)); + checkRoundTrip_allBytes(Base64.getMimeEncoder(63, separator), decoder, + wrapLines("\r\n", ALL_BYTE_VALUES_ENCODED, 60)); + checkRoundTrip_allBytes(Base64.getMimeEncoder(10, separator), decoder, + wrapLines("\r\n", ALL_BYTE_VALUES_ENCODED, 8)); + } + + public void testRoundTrip_allBytes_url() { + String encodedUrl = ALL_BYTE_VALUES_ENCODED.replace('+', '-').replace('/', '_'); + checkRoundTrip_allBytes(Base64.getUrlEncoder(), Base64.getUrlDecoder(), encodedUrl); + } + + /** + * Checks round-trip encoding/decoding of all byte values 0..255 for + * the case where the Encoder doesn't add any linebreaks. + */ + private static void checkRoundTrip_allBytes_singleLine(Encoder encoder, Decoder decoder) { + checkRoundTrip_allBytes(encoder, decoder, ALL_BYTE_VALUES_ENCODED); + } + + /** + * Checks that byte values 0..255, in order, are encoded to exactly + * the given String (including any linebreaks, if present) and that + * that String can be decoded back to the same byte values. + * + * @param encoded the expected encoded representation of the (unsigned) + * byte values 0..255, in order. + */ + private static void checkRoundTrip_allBytes(Encoder encoder, Decoder decoder, String encoded) { + byte[] bytes = new byte[256]; + for (int i = 0; i < 256; i++) { + bytes[i] = (byte) i; + } + assertRoundTrip(encoder, decoder, encoded, bytes); + } + + public void testRoundTrip_variousSizes_plain() { + checkRoundTrip_variousSizes(Base64.getEncoder(), Base64.getDecoder()); + } + + public void testRoundTrip_variousSizes_mime() { + checkRoundTrip_variousSizes(Base64.getMimeEncoder(), Base64.getMimeDecoder()); + } + + public void testRoundTrip_variousSizes_url() { + checkRoundTrip_variousSizes(Base64.getUrlEncoder(), Base64.getUrlDecoder()); + } + + /** + * Checks that various-sized inputs survive a round trip. + */ + private static void checkRoundTrip_variousSizes(Encoder encoder, Decoder decoder) { + Random random = new Random(7654321); + for (int numBytes : new int [] { 0, 1, 2, 75, 76, 77, 80, 100, 1234 }) { + byte[] bytes = new byte[numBytes]; + random.nextBytes(bytes); + byte[] result = decoder.decode(encoder.encode(bytes)); + assertArrayEquals(bytes, result); + } + } + + public void testRoundtrip_wrap_basic() throws Exception { + Encoder encoder = Base64.getEncoder(); + Decoder decoder = Base64.getDecoder(); + checkRoundTrip_wrapInputStream(encoder, decoder); + } + + public void testRoundtrip_wrap_mime() throws Exception { + Encoder encoder = Base64.getMimeEncoder(); + Decoder decoder = Base64.getMimeDecoder(); + checkRoundTrip_wrapInputStream(encoder, decoder); + } + + public void testRoundTrip_wrap_url() throws Exception { + Encoder encoder = Base64.getUrlEncoder(); + Decoder decoder = Base64.getUrlDecoder(); + checkRoundTrip_wrapInputStream(encoder, decoder); + } + + /** + * Checks that the {@link Decoder#wrap(InputStream) wrapping} an + * InputStream of encoded data yields the plain data that was + * previously {@link Encoder#encode(byte[]) encoded}. + */ + private static void checkRoundTrip_wrapInputStream(Encoder encoder, Decoder decoder) + throws IOException { + Random random = new Random(32176L); + int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 }; + + // Test input needs to be at least 2048 bytes to fill up the + // read buffer of Base64InputStream. + byte[] plain = new byte[4567]; + random.nextBytes(plain); + byte[] encoded = encoder.encode(plain); + byte[] actual = new byte[plain.length * 2]; + int b; + + // ----- test decoding ("encoded" -> "plain") ----- + + // read as much as it will give us in one chunk + ByteArrayInputStream bais = new ByteArrayInputStream(encoded); + InputStream b64is = decoder.wrap(bais); + int ap = 0; + while ((b = b64is.read(actual, ap, actual.length - ap)) != -1) { + ap += b; + } + assertArrayPrefixEquals(actual, ap, plain); + + // read individual bytes + bais = new ByteArrayInputStream(encoded); + b64is = decoder.wrap(bais); + ap = 0; + while ((b = b64is.read()) != -1) { + actual[ap++] = (byte) b; + } + assertArrayPrefixEquals(actual, ap, plain); + + // mix reads of variously-sized arrays with one-byte reads + bais = new ByteArrayInputStream(encoded); + b64is = decoder.wrap(bais); + ap = 0; + while (true) { + int l = writeLengths[random.nextInt(writeLengths.length)]; + if (l >= 0) { + b = b64is.read(actual, ap, l); + if (b == -1) { + break; + } + ap += b; + } else { + for (int i = 0; i < -l; ++i) { + if ((b = b64is.read()) == -1) { + break; + } + actual[ap++] = (byte) b; + } + } + } + assertArrayPrefixEquals(actual, ap, plain); + } + + public void testDecoder_wrap_singleByteReads() throws IOException { + InputStream in = Base64.getDecoder().wrap(new ByteArrayInputStream("/v8=".getBytes())); + assertEquals(254, in.read()); + assertEquals(255, in.read()); + assertEquals(-1, in.read()); + } + + public void testEncoder_withoutPadding() { + byte[] bytes = new byte[] { (byte) 0xFE, (byte) 0xFF }; + assertEquals("/v8=", Base64.getEncoder().encodeToString(bytes)); + assertEquals("/v8", Base64.getEncoder().withoutPadding().encodeToString(bytes)); + + assertEquals("/v8=", Base64.getMimeEncoder().encodeToString(bytes)); + assertEquals("/v8", Base64.getMimeEncoder().withoutPadding().encodeToString(bytes)); + + assertEquals("_v8=", Base64.getUrlEncoder().encodeToString(bytes)); + assertEquals("_v8", Base64.getUrlEncoder().withoutPadding().encodeToString(bytes)); + } + + public void testEncoder_wrap_plain() throws Exception { + checkWrapOutputStreamConsistentWithEncode(Base64.getEncoder()); + } + + public void testEncoder_wrap_url() throws Exception { + checkWrapOutputStreamConsistentWithEncode(Base64.getUrlEncoder()); + } + + public void testEncoder_wrap_mime() throws Exception { + checkWrapOutputStreamConsistentWithEncode(Base64.getMimeEncoder()); + } + + /** A way of writing bytes to an OutputStream. */ + interface WriteStrategy { + void write(byte[] bytes, OutputStream out) throws IOException; + } + + private static void checkWrapOutputStreamConsistentWithEncode(Encoder encoder) + throws Exception { + final Random random = new Random(32176L); + + // one large write(byte[]) of the whole input + WriteStrategy allAtOnce = (bytes, out) -> out.write(bytes); + checkWrapOutputStreamConsistentWithEncode(encoder, allAtOnce); + + // many calls to write(int) + WriteStrategy byteWise = (bytes, out) -> { + for (byte b : bytes) { + out.write(b); + } + }; + checkWrapOutputStreamConsistentWithEncode(encoder, byteWise); + + // intermixed sequences of write(int) with + // write(byte[],int,int) of various lengths. + WriteStrategy mixed = (bytes, out) -> { + int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 }; + int p = 0; + while (p < bytes.length) { + int l = writeLengths[random.nextInt(writeLengths.length)]; + l = Math.min(l, bytes.length - p); + if (l >= 0) { + out.write(bytes, p, l); + p += l; + } else { + l = Math.min(-l, bytes.length - p); + for (int i = 0; i < l; ++i) { + out.write(bytes[p + i]); + } + p += l; + } + } + }; + checkWrapOutputStreamConsistentWithEncode(encoder, mixed); + } + + /** + * Checks that writing to a wrap()ping OutputStream produces the same + * output on the wrapped stream as {@link Encoder#encode(byte[])}. + */ + private static void checkWrapOutputStreamConsistentWithEncode(Encoder encoder, + WriteStrategy writeStrategy) throws IOException { + Random random = new Random(32176L); + // Test input needs to be at least 1024 bytes to test filling + // up the write(int) buffer of Base64OutputStream. + byte[] plain = new byte[1234]; + random.nextBytes(plain); + byte[] encodeResult = encoder.encode(plain); + ByteArrayOutputStream wrappedOutputStream = new ByteArrayOutputStream(); + try (OutputStream plainOutputStream = encoder.wrap(wrappedOutputStream)) { + writeStrategy.write(plain, plainOutputStream); + } + assertArrayEquals(encodeResult, wrappedOutputStream.toByteArray()); + } + + /** Decodes a string, returning the resulting bytes interpreted as an ASCII String. */ + private static String decodeToAscii(Decoder decoder, String encoded) throws Exception { + byte[] plain = decoder.decode(encoded); + return new String(plain, US_ASCII); + } + + /** + * Checks round-trip encoding/decoding of {@code plain}. + * + * @param plain an ASCII String + * @return the Base64-encoded value of the ASCII codepoints from {@code plain} + */ + private static String encodeFromAscii(Encoder encoder, Decoder decoder, String plain) + throws Exception { + String encoded = encoder.encodeToString(plain.getBytes(US_ASCII)); + String decoded = decodeToAscii(decoder, encoded); + assertEquals(plain, decoded); + return encoded; + } + + /** + * Rewraps {@code s} by inserting {@lineSeparator} every {@code lineLength} characters, + * but not at the end. + */ + private static String wrapLines(String lineSeparator, String s, int lineLength) { + return String.join(lineSeparator, breakLines(s, lineLength)); + } + + /** + * Splits {@code s} into a list of substrings, each except possibly the last one + * exactly {@code lineLength} characters long. + */ + private static List breakLines(String longString, int lineLength) { + List lines = new ArrayList<>(); + for (int pos = 0; pos < longString.length(); pos += lineLength) { + lines.add(longString.substring(pos, Math.min(longString.length(), pos + lineLength))); + } + return lines; + } + + /** Assert that decoding the specific String throws IllegalArgumentException. */ + private static void assertDecodeThrowsIAe(Decoder decoder, String invalidEncoded) + throws Exception { + try { + decoder.decode(invalidEncoded); + fail("should have failed to decode"); + } catch (IllegalArgumentException e) { + } + } + + /** + * Asserts that the given String decodes to the bytes, and that the bytes encode + * to the given String. + */ + private static void assertRoundTrip(Encoder encoder, Decoder decoder, String encoded, + byte[] bytes) { + assertEquals(encoded, encoder.encodeToString(bytes)); + assertArrayEquals(bytes, decoder.decode(encoded)); + } + + /** Asserts that actual equals the first len bytes of expected. */ + private static void assertArrayPrefixEquals(byte[] expected, int len, byte[] actual) { + assertArrayEquals(copyOfRange(expected, 0, len), actual); + } + + /** Checks array contents. */ + private static void assertArrayEquals(byte[] expected, byte[] actual) { + if (!Arrays.equals(expected, actual)) { + fail("Expected " + hexString(expected) + ", got " + hexString(actual)); + } + } + + private static String hexString(byte[] bytes) { + StringBuilder sb = new StringBuilder("0x"); + for (byte b : bytes) { + sb.append(Integer.toHexString(b & 0xff)); + } + return sb.toString(); + } + + private static void assertThrowsNpe(Runnable runnable) { + try { + runnable.run(); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException expected) { + } + } + +} diff --git a/luni/src/test/java/libcore/java/util/BitSetTest.java b/luni/src/test/java/libcore/java/util/BitSetTest.java index c4e8ad3da..41d4e5327 100644 --- a/luni/src/test/java/libcore/java/util/BitSetTest.java +++ b/luni/src/test/java/libcore/java/util/BitSetTest.java @@ -34,6 +34,22 @@ public void test_toString() throws Exception { assertEquals("{2, 4, 10}", bs.toString()); } + // b/31234459 + public void test_toString_highestPossibleBitSet() { + // 2^28 bytes for the bits in the BitSet, plus extra bytes for everything else + int bytesRequired = (1 << 28) + (1 << 27); + if (Runtime.getRuntime().maxMemory() < bytesRequired) { + return; + } + try { + BitSet bitSet = new BitSet(); + bitSet.set(Integer.MAX_VALUE); + assertEquals("{2147483647}", bitSet.toString()); + } catch (OutOfMemoryError e) { + // ignore + } + } + private static void assertBitSet(BitSet bs, long[] longs, String s) { for (int i = 0; i < 64 * longs.length; ++i) { assertEquals(bs.toString(), ((longs[i / 64] & (1L << (i % 64))) != 0), bs.get(i)); diff --git a/luni/src/test/java/libcore/java/util/CalendarBuilderTest.java b/luni/src/test/java/libcore/java/util/CalendarBuilderTest.java new file mode 100644 index 000000000..5e4ac9638 --- /dev/null +++ b/luni/src/test/java/libcore/java/util/CalendarBuilderTest.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2016 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. + */ +package libcore.java.util; + +import org.junit.Before; +import org.junit.Test; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * Tests {@link java.util.Calendar.Builder}. + */ +public class CalendarBuilderTest { + + + @Test + public void test_default_values() { + Calendar.Builder builder = new Calendar.Builder(); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + assertEquals(expected, builder.build()); + } + + @Test + public void test_setCalendarType_iso8601() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setCalendarType("iso8601"); + // ISO 8601 represents a gregorian calendar with a specific configuration + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + expected.setGregorianChange(new Date(Long.MIN_VALUE)); + expected.setFirstDayOfWeek(Calendar.MONDAY); + expected.setMinimalDaysInFirstWeek(4); + assertEquals(expected, builder.build()); + } + + @Test + public void test_setCalendarType_invalid() { + Calendar.Builder builder = new Calendar.Builder(); + try { + builder.setCalendarType(null); + fail("Should have thrown NPE"); + } catch (NullPointerException expected) {} + + for (String unsupported : new String[] { "buddhist", "japanese", "notACalendarType" }) { + try { + // not supported + builder.setCalendarType(unsupported); + fail("Unsupported calendar type " + unsupported + " should have thrown."); + } catch (IllegalArgumentException expected) {} + } + } + + @Test + public void test_setCalendarType_reset() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setCalendarType("gregorian"); + try { + builder.setCalendarType("iso8601"); + fail("Should not accept second setCalendarType() call"); + } catch (IllegalStateException expected) {} + } + + @Test + public void test_setDate() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setDate(2000, Calendar.FEBRUARY, 3); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + expected.set(2000, Calendar.FEBRUARY, 3); + assertEquals(expected, builder.build()); + } + + @Test + public void test_setTimeOfDay() { + Calendar.Builder builder = new Calendar.Builder(); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + builder.setTimeOfDay(10, 11, 12); + expected.set(1970, Calendar.JANUARY, 1, 10, 11, 12); + assertEquals(expected, builder.build()); + builder.setTimeOfDay(10, 11, 12, 13); + expected.set(Calendar.MILLISECOND, 13); + assertEquals(expected, builder.build()); + } + + @Test + public void test_setWeekDate() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setWeekDate(1, 2000, Calendar.TUESDAY); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + expected.setWeekDate(1, 2000, Calendar.TUESDAY); + assertEquals(expected, builder.build()); + } + + @Test + public void test_setLenient() { + Calendar.Builder builder = new Calendar.Builder(); + builder.set(Calendar.HOUR_OF_DAY, 25); + builder.setLenient(false); + try { + builder.build(); + fail("Should have failed to build."); + } catch (IllegalArgumentException expected) {} + builder.setLenient(true); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + expected.setLenient(true); + expected.set(Calendar.HOUR_OF_DAY, 25); + assertEquals(expected, builder.build()); + } + + @Test + public void test_setLocale() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setLocale(Locale.GERMANY); + GregorianCalendar expected = new GregorianCalendar(Locale.GERMANY); + expected.clear(); + assertEquals(expected, builder.build()); + } + + @Test + public void test_setLocale_thTH() { + // See http://b/35138741 + Calendar.Builder builder = new Calendar.Builder(); + Locale th = new Locale("th", "TH"); + builder.setLocale(th); + GregorianCalendar expected = new GregorianCalendar(th); + expected.clear(); + assertEquals(expected, builder.build()); + } + + @Test + public void test_set() { + Calendar.Builder builder = new Calendar.Builder(); + builder.set(Calendar.YEAR, 2000); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + expected.set(Calendar.YEAR, 2000); + assertEquals(expected, builder.build()); + } + + @Test(expected = IllegalArgumentException.class) + public void test_set_negative_field() { + new Calendar.Builder().set(-1, 1); + } + + @Test(expected = IllegalArgumentException.class) + public void test_set_field_too_high() { + new Calendar.Builder().set(Calendar.FIELD_COUNT, 1); + } + + @Test + public void test_set_after_setInstant() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setInstant(0L); + try { + builder.set(Calendar.YEAR, 2000); + fail("Setting a field after setInstant should fail."); + } catch (IllegalStateException expected) {} + } + + @Test + public void test_setFields() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setFields(Calendar.YEAR, 2000, Calendar.MONTH, Calendar.FEBRUARY); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + expected.set(Calendar.YEAR, 2000); + expected.set(Calendar.MONTH, Calendar.FEBRUARY); + assertEquals(expected, builder.build()); + + // field values can be re-set and order of fields matter + builder.setFields(Calendar.DAY_OF_WEEK_IN_MONTH, 1, + Calendar.DAY_OF_MONTH, 20, // this will effectively be ignored + Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY); + expected.set(Calendar.DAY_OF_WEEK_IN_MONTH, 1); + expected.set(Calendar.DAY_OF_MONTH, 20); + expected.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY); + assertEquals(expected, builder.build()); + // 20th February 2000 would have been a Sunday, but we set the DOW last. + assertEquals(Calendar.WEDNESDAY, builder.build().get(Calendar.DAY_OF_WEEK)); + } + + @Test(expected = NullPointerException.class) + public void test_setFields_null() { + new Calendar.Builder().setFields(null); + } + + @Test(expected = IllegalArgumentException.class) + public void test_setFields_oddNumberOfArguments() { + new Calendar.Builder().setFields(Calendar.YEAR); + } + + @Test + public void test_setFields_after_setInstant() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setInstant(0L); + try { + builder.setFields(Calendar.YEAR, 2000); + fail("Setting a field after setInstant should fail."); + } catch (IllegalStateException expected) {} + } + + @Test + public void test_setInstant() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setInstant(Long.MIN_VALUE); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + expected.setTimeInMillis(Long.MIN_VALUE); + assertEquals(expected, builder.build()); + } + + @Test + public void test_setInstant_after_set() { + Calendar.Builder builder = new Calendar.Builder(); + builder.set(Calendar.YEAR, 2000); + try { + builder.setInstant(0L); + fail("Setting the instant after setting a field should fail."); + } catch (IllegalStateException expected) {} + } + + @Test + public void test_setInstant_Date() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setInstant(new Date(Long.MAX_VALUE)); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + expected.setTimeInMillis(Long.MAX_VALUE); + assertEquals(expected, builder.build()); + } + + @Test(expected = NullPointerException.class) + public void test_setInstant_Date_null() { + new Calendar.Builder().setInstant(null); + } + + @Test + public void test_setTimeZone() { + TimeZone london = TimeZone.getTimeZone("Europe/London"); + Calendar.Builder builder = new Calendar.Builder(); + builder.setTimeZone(london); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + expected.setTimeZone(london); + assertEquals(expected, builder.build()); + } + + @Test(expected = NullPointerException.class) + public void test_setTimeZone_null() { + new Calendar.Builder().setTimeZone(null); + } + + @Test + public void test_setWeekDefinition() { + Calendar.Builder builder = new Calendar.Builder(); + builder.setWeekDefinition(Calendar.TUESDAY, 7); + GregorianCalendar expected = new GregorianCalendar(); + expected.clear(); + expected.setFirstDayOfWeek(Calendar.TUESDAY); + expected.setMinimalDaysInFirstWeek(7); + assertEquals(expected, builder.build()); + } + + @Test(expected = IllegalArgumentException.class) + public void test_setWeekDefinition_invalid_first_dow() { + new Calendar.Builder().setWeekDefinition(-1, 1); + } + + @Test(expected = IllegalArgumentException.class) + public void test_setWeekDefinition_invalid_minimum_days() { + new Calendar.Builder().setWeekDefinition(Calendar.WEDNESDAY, 8); + } + +} diff --git a/luni/src/test/java/libcore/java/util/CalendarTest.java b/luni/src/test/java/libcore/java/util/CalendarTest.java index e1775a98a..7d3b1242f 100644 --- a/luni/src/test/java/libcore/java/util/CalendarTest.java +++ b/luni/src/test/java/libcore/java/util/CalendarTest.java @@ -16,10 +16,15 @@ package libcore.java.util; +import java.time.Instant; +import java.util.Arrays; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; +import java.util.HashSet; import java.util.Locale; +import java.util.Set; import java.util.TimeZone; import libcore.util.SerializationTester; @@ -157,6 +162,18 @@ private void testSetSelfConsistent(TimeZone timeZone, int year, int month, int d assertEquals(minute, calendar.get(Calendar.MINUTE)); } + public void testToInstant() { + TimeZone timeZone = TimeZone.getTimeZone("Europe/Paris"); + Calendar calendar = new GregorianCalendar(timeZone); + calendar.clear(); + calendar.set(2007, Calendar.DECEMBER, 3, 10, 15, 30); + Instant instant = calendar.toInstant(); + assertEquals(calendar.getTime().toInstant(), instant); + assertEquals(Instant.ofEpochMilli(calendar.getTimeInMillis()), instant); + // GMT is one hour earlier than Europe/Paris, hence hour of day is 9 rather than 10 + assertEquals(instant, Instant.parse("2007-12-03T09:15:30Z")); + } + // http://b/5179775 public void testCalendarSerialization() { String s = "aced00057372001b6a6176612e7574696c2e477265676f7269616e43616c656e6461728f3dd7d6e" @@ -314,6 +331,13 @@ public void testSetHourOfDayInEuropeLondon() { assertEquals(1,calendar.get(Calendar.HOUR_OF_DAY)); } + public void testGetAvailableCalendarTypes() { + // Guards against unintentional change; for intentional changes, + // update this test. + Set expected = Collections.singleton("gregory"); + assertEquals(expected, Calendar.getAvailableCalendarTypes()); + } + public void testGetWeekYear() { try { new FakeCalendar().getWeekYear(); diff --git a/luni/src/test/java/libcore/java/util/CalendarWeekOfMonthTest.java b/luni/src/test/java/libcore/java/util/CalendarWeekOfMonthTest.java new file mode 100644 index 000000000..66cd0e8c7 --- /dev/null +++ b/luni/src/test/java/libcore/java/util/CalendarWeekOfMonthTest.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2016 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. + */ +package libcore.java.util; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.junit.Assert.assertEquals; + +/** + * Test that Calendar.get(WEEK_OF_MONTH) works as expected. + */ +@RunWith(Parameterized.class) +public class CalendarWeekOfMonthTest { + + private final long timeInMillis; + + private final String date; + + private final int firstDayOfWeek; + + private final int minimalDaysInFirstWeek; + + private final int expectedWeekOfMonth; + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][] { + // MinimalDaysInFirstWeek = 4, FirstDayOfWeek MONDAY + { 1462107600000L, "01 May 2016", Calendar.MONDAY, 4, 0 }, + { 1462194000000L, "02 May 2016", Calendar.MONDAY, 4, 1 }, + { 1462798800000L, "09 May 2016", Calendar.MONDAY, 4, 2 }, + { 1463403600000L, "16 May 2016", Calendar.MONDAY, 4, 3 }, + { 1464008400000L, "23 May 2016", Calendar.MONDAY, 4, 4 }, + { 1464613200000L, "30 May 2016", Calendar.MONDAY, 4, 5 }, + { 1464786000000L, "01 Jun 2016", Calendar.MONDAY, 4, 1 }, + { 1465218000000L, "06 Jun 2016", Calendar.MONDAY, 4, 2 }, + { 1465822800000L, "13 Jun 2016", Calendar.MONDAY, 4, 3 }, + { 1466427600000L, "20 Jun 2016", Calendar.MONDAY, 4, 4 }, + { 1467032400000L, "27 Jun 2016", Calendar.MONDAY, 4, 5 }, + { 1467378000000L, "01 Jul 2016", Calendar.MONDAY, 4, 0 }, + { 1467637200000L, "04 Jul 2016", Calendar.MONDAY, 4, 1 }, + { 1468242000000L, "11 Jul 2016", Calendar.MONDAY, 4, 2 }, + { 1468846800000L, "18 Jul 2016", Calendar.MONDAY, 4, 3 }, + { 1469451600000L, "25 Jul 2016", Calendar.MONDAY, 4, 4 }, + { 1470056400000L, "01 Aug 2016", Calendar.MONDAY, 4, 1 }, + { 1470661200000L, "08 Aug 2016", Calendar.MONDAY, 4, 2 }, + { 1471266000000L, "15 Aug 2016", Calendar.MONDAY, 4, 3 }, + { 1471870800000L, "22 Aug 2016", Calendar.MONDAY, 4, 4 }, + { 1472475600000L, "29 Aug 2016", Calendar.MONDAY, 4, 5 }, + { 1472734800000L, "01 Sep 2016", Calendar.MONDAY, 4, 1 }, + { 1473080400000L, "05 Sep 2016", Calendar.MONDAY, 4, 2 }, + { 1473685200000L, "12 Sep 2016", Calendar.MONDAY, 4, 3 }, + { 1474290000000L, "19 Sep 2016", Calendar.MONDAY, 4, 4 }, + { 1474894800000L, "26 Sep 2016", Calendar.MONDAY, 4, 5 }, + { 1475326800000L, "01 Oct 2016", Calendar.MONDAY, 4, 0 }, + { 1475499600000L, "03 Oct 2016", Calendar.MONDAY, 4, 1 }, + { 1476104400000L, "10 Oct 2016", Calendar.MONDAY, 4, 2 }, + { 1476709200000L, "17 Oct 2016", Calendar.MONDAY, 4, 3 }, + { 1477314000000L, "24 Oct 2016", Calendar.MONDAY, 4, 4 }, + { 1477918800000L, "31 Oct 2016", Calendar.MONDAY, 4, 5 }, + { 1478005200000L, "01 Nov 2016", Calendar.MONDAY, 4, 1 }, + { 1478523600000L, "07 Nov 2016", Calendar.MONDAY, 4, 2 }, + { 1479128400000L, "14 Nov 2016", Calendar.MONDAY, 4, 3 }, + { 1479733200000L, "21 Nov 2016", Calendar.MONDAY, 4, 4 }, + { 1480338000000L, "28 Nov 2016", Calendar.MONDAY, 4, 5 }, + + // MinimalDaysInFirstWeek = 1, FirstDayOfWeek MONDAY + { 1462107600000L, "01 May 2016", Calendar.MONDAY, 1, 1 }, + { 1462194000000L, "02 May 2016", Calendar.MONDAY, 1, 2 }, + { 1462798800000L, "09 May 2016", Calendar.MONDAY, 1, 3 }, + { 1463403600000L, "16 May 2016", Calendar.MONDAY, 1, 4 }, + { 1464008400000L, "23 May 2016", Calendar.MONDAY, 1, 5 }, + { 1464613200000L, "30 May 2016", Calendar.MONDAY, 1, 6 }, + { 1464786000000L, "01 Jun 2016", Calendar.MONDAY, 1, 1 }, + { 1465218000000L, "06 Jun 2016", Calendar.MONDAY, 1, 2 }, + { 1465822800000L, "13 Jun 2016", Calendar.MONDAY, 1, 3 }, + { 1466427600000L, "20 Jun 2016", Calendar.MONDAY, 1, 4 }, + { 1467032400000L, "27 Jun 2016", Calendar.MONDAY, 1, 5 }, + { 1467378000000L, "01 Jul 2016", Calendar.MONDAY, 1, 1 }, + { 1467637200000L, "04 Jul 2016", Calendar.MONDAY, 1, 2 }, + { 1468242000000L, "11 Jul 2016", Calendar.MONDAY, 1, 3 }, + { 1468846800000L, "18 Jul 2016", Calendar.MONDAY, 1, 4 }, + { 1469451600000L, "25 Jul 2016", Calendar.MONDAY, 1, 5 }, + { 1470056400000L, "01 Aug 2016", Calendar.MONDAY, 1, 1 }, + { 1470661200000L, "08 Aug 2016", Calendar.MONDAY, 1, 2 }, + { 1471266000000L, "15 Aug 2016", Calendar.MONDAY, 1, 3 }, + { 1471870800000L, "22 Aug 2016", Calendar.MONDAY, 1, 4 }, + { 1472475600000L, "29 Aug 2016", Calendar.MONDAY, 1, 5 }, + { 1472734800000L, "01 Sep 2016", Calendar.MONDAY, 1, 1 }, + { 1473080400000L, "05 Sep 2016", Calendar.MONDAY, 1, 2 }, + { 1473685200000L, "12 Sep 2016", Calendar.MONDAY, 1, 3 }, + { 1474290000000L, "19 Sep 2016", Calendar.MONDAY, 1, 4 }, + { 1474894800000L, "26 Sep 2016", Calendar.MONDAY, 1, 5 }, + { 1475326800000L, "01 Oct 2016", Calendar.MONDAY, 1, 1 }, + { 1475499600000L, "03 Oct 2016", Calendar.MONDAY, 1, 2 }, + { 1476104400000L, "10 Oct 2016", Calendar.MONDAY, 1, 3 }, + { 1476709200000L, "17 Oct 2016", Calendar.MONDAY, 1, 4 }, + { 1477314000000L, "24 Oct 2016", Calendar.MONDAY, 1, 5 }, + { 1477918800000L, "31 Oct 2016", Calendar.MONDAY, 1, 6 }, + { 1478005200000L, "01 Nov 2016", Calendar.MONDAY, 1, 1 }, + { 1478523600000L, "07 Nov 2016", Calendar.MONDAY, 1, 2 }, + { 1479128400000L, "14 Nov 2016", Calendar.MONDAY, 1, 3 }, + { 1479733200000L, "21 Nov 2016", Calendar.MONDAY, 1, 4 }, + { 1480338000000L, "28 Nov 2016", Calendar.MONDAY, 1, 5 }, + + // MinimalDaysInFirstWeek = 4, FirstDayOfWeek SUNDAY + { 1462107600000L, "01 May 2016", Calendar.SUNDAY, 4, 1 }, + { 1462712400000L, "08 May 2016", Calendar.SUNDAY, 4, 2 }, + { 1463317200000L, "15 May 2016", Calendar.SUNDAY, 4, 3 }, + { 1463922000000L, "22 May 2016", Calendar.SUNDAY, 4, 4 }, + { 1464526800000L, "29 May 2016", Calendar.SUNDAY, 4, 5 }, + { 1464786000000L, "01 Jun 2016", Calendar.SUNDAY, 4, 1 }, + { 1465131600000L, "05 Jun 2016", Calendar.SUNDAY, 4, 2 }, + { 1465736400000L, "12 Jun 2016", Calendar.SUNDAY, 4, 3 }, + { 1466341200000L, "19 Jun 2016", Calendar.SUNDAY, 4, 4 }, + { 1466946000000L, "26 Jun 2016", Calendar.SUNDAY, 4, 5 }, + { 1467378000000L, "01 Jul 2016", Calendar.SUNDAY, 4, 0 }, + { 1467550800000L, "03 Jul 2016", Calendar.SUNDAY, 4, 1 }, + { 1468155600000L, "10 Jul 2016", Calendar.SUNDAY, 4, 2 }, + { 1468760400000L, "17 Jul 2016", Calendar.SUNDAY, 4, 3 }, + { 1469365200000L, "24 Jul 2016", Calendar.SUNDAY, 4, 4 }, + { 1469970000000L, "31 Jul 2016", Calendar.SUNDAY, 4, 5 }, + { 1470056400000L, "01 Aug 2016", Calendar.SUNDAY, 4, 1 }, + { 1470574800000L, "07 Aug 2016", Calendar.SUNDAY, 4, 2 }, + { 1471179600000L, "14 Aug 2016", Calendar.SUNDAY, 4, 3 }, + { 1471784400000L, "21 Aug 2016", Calendar.SUNDAY, 4, 4 }, + { 1472389200000L, "28 Aug 2016", Calendar.SUNDAY, 4, 5 }, + { 1472734800000L, "01 Sep 2016", Calendar.SUNDAY, 4, 0 }, + { 1472994000000L, "04 Sep 2016", Calendar.SUNDAY, 4, 1 }, + { 1473598800000L, "11 Sep 2016", Calendar.SUNDAY, 4, 2 }, + { 1474203600000L, "18 Sep 2016", Calendar.SUNDAY, 4, 3 }, + { 1474808400000L, "25 Sep 2016", Calendar.SUNDAY, 4, 4 }, + { 1475326800000L, "01 Oct 2016", Calendar.SUNDAY, 4, 0 }, + { 1475413200000L, "02 Oct 2016", Calendar.SUNDAY, 4, 1 }, + { 1476018000000L, "09 Oct 2016", Calendar.SUNDAY, 4, 2 }, + { 1476622800000L, "16 Oct 2016", Calendar.SUNDAY, 4, 3 }, + { 1477227600000L, "23 Oct 2016", Calendar.SUNDAY, 4, 4 }, + { 1477832400000L, "30 Oct 2016", Calendar.SUNDAY, 4, 5 }, + { 1478005200000L, "01 Nov 2016", Calendar.SUNDAY, 4, 1 }, + { 1478437200000L, "06 Nov 2016", Calendar.SUNDAY, 4, 2 }, + { 1479042000000L, "13 Nov 2016", Calendar.SUNDAY, 4, 3 }, + { 1479646800000L, "20 Nov 2016", Calendar.SUNDAY, 4, 4 }, + { 1480251600000L, "27 Nov 2016", Calendar.SUNDAY, 4, 5 }, + + // MinimalDaysInFirstWeek = 1, FirstDayOfWeek SUNDAY + { 1462107600000L, "01 May 2016", Calendar.SUNDAY, 1, 1 }, + { 1462712400000L, "08 May 2016", Calendar.SUNDAY, 1, 2 }, + { 1463317200000L, "15 May 2016", Calendar.SUNDAY, 1, 3 }, + { 1463922000000L, "22 May 2016", Calendar.SUNDAY, 1, 4 }, + { 1464526800000L, "29 May 2016", Calendar.SUNDAY, 1, 5 }, + { 1464786000000L, "01 Jun 2016", Calendar.SUNDAY, 1, 1 }, + { 1465131600000L, "05 Jun 2016", Calendar.SUNDAY, 1, 2 }, + { 1465736400000L, "12 Jun 2016", Calendar.SUNDAY, 1, 3 }, + { 1466341200000L, "19 Jun 2016", Calendar.SUNDAY, 1, 4 }, + { 1466946000000L, "26 Jun 2016", Calendar.SUNDAY, 1, 5 }, + { 1467378000000L, "01 Jul 2016", Calendar.SUNDAY, 1, 1 }, + { 1467550800000L, "03 Jul 2016", Calendar.SUNDAY, 1, 2 }, + { 1468155600000L, "10 Jul 2016", Calendar.SUNDAY, 1, 3 }, + { 1468760400000L, "17 Jul 2016", Calendar.SUNDAY, 1, 4 }, + { 1469365200000L, "24 Jul 2016", Calendar.SUNDAY, 1, 5 }, + { 1469970000000L, "31 Jul 2016", Calendar.SUNDAY, 1, 6 }, + { 1470056400000L, "01 Aug 2016", Calendar.SUNDAY, 1, 1 }, + { 1470574800000L, "07 Aug 2016", Calendar.SUNDAY, 1, 2 }, + { 1471179600000L, "14 Aug 2016", Calendar.SUNDAY, 1, 3 }, + { 1471784400000L, "21 Aug 2016", Calendar.SUNDAY, 1, 4 }, + { 1472389200000L, "28 Aug 2016", Calendar.SUNDAY, 1, 5 }, + { 1472734800000L, "01 Sep 2016", Calendar.SUNDAY, 1, 1 }, + { 1472994000000L, "04 Sep 2016", Calendar.SUNDAY, 1, 2 }, + { 1473598800000L, "11 Sep 2016", Calendar.SUNDAY, 1, 3 }, + { 1474203600000L, "18 Sep 2016", Calendar.SUNDAY, 1, 4 }, + { 1474808400000L, "25 Sep 2016", Calendar.SUNDAY, 1, 5 }, + { 1475326800000L, "01 Oct 2016", Calendar.SUNDAY, 1, 1 }, + { 1475413200000L, "02 Oct 2016", Calendar.SUNDAY, 1, 2 }, + { 1476018000000L, "09 Oct 2016", Calendar.SUNDAY, 1, 3 }, + { 1476622800000L, "16 Oct 2016", Calendar.SUNDAY, 1, 4 }, + { 1477227600000L, "23 Oct 2016", Calendar.SUNDAY, 1, 5 }, + { 1477832400000L, "30 Oct 2016", Calendar.SUNDAY, 1, 6 }, + { 1478005200000L, "01 Nov 2016", Calendar.SUNDAY, 1, 1 }, + { 1478437200000L, "06 Nov 2016", Calendar.SUNDAY, 1, 2 }, + { 1479042000000L, "13 Nov 2016", Calendar.SUNDAY, 1, 3 }, + { 1479646800000L, "20 Nov 2016", Calendar.SUNDAY, 1, 4 }, + { 1480251600000L, "27 Nov 2016", Calendar.SUNDAY, 1, 5 }, + + }); + } + + public CalendarWeekOfMonthTest(long timeInMillis, String date, int firstDayOfWeek, + int minimalDaysInFirstWeek, int expectedWeekOfMonth) { + this.timeInMillis = timeInMillis; + this.date = date; + this.firstDayOfWeek = firstDayOfWeek; + this.minimalDaysInFirstWeek = minimalDaysInFirstWeek; + this.expectedWeekOfMonth = expectedWeekOfMonth; + } + + @Test + public void test() { + Calendar calendar = new GregorianCalendar( + TimeZone.getTimeZone("America/Los_Angeles"), Locale.US); + calendar.setFirstDayOfWeek(firstDayOfWeek); + calendar.setMinimalDaysInFirstWeek(minimalDaysInFirstWeek); + calendar.setTimeInMillis(timeInMillis); + + assertEquals(toString(), expectedWeekOfMonth, calendar.get(Calendar.WEEK_OF_MONTH)); + } + + @Override + public String toString() { + return "CalendarWeekOfMonthTest{" + "timeInMillis=" + timeInMillis + + ", date='" + date + '\'' + ", firstDayOfWeek=" + firstDayOfWeek + + ", minimalDaysInFirstWeek=" + minimalDaysInFirstWeek + + ", expectedWeekOfMonth=" + expectedWeekOfMonth + '}'; + } +} diff --git a/luni/src/test/java/libcore/java/util/CollectionsTest.java b/luni/src/test/java/libcore/java/util/CollectionsTest.java index f3284c094..d09cb83ef 100644 --- a/luni/src/test/java/libcore/java/util/CollectionsTest.java +++ b/luni/src/test/java/libcore/java/util/CollectionsTest.java @@ -17,19 +17,51 @@ package libcore.java.util; import java.io.Serializable; +import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.ConcurrentModificationException; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; import java.util.Spliterator; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.LinkedBlockingDeque; +import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import libcore.util.Objects; + +import dalvik.system.VMRuntime; + +import static java.util.Collections.checkedNavigableMap; +import static java.util.Collections.checkedQueue; +import static java.util.Collections.synchronizedNavigableMap; +import static java.util.Collections.unmodifiableNavigableMap; +import static java.util.Spliterator.DISTINCT; +import static java.util.Spliterator.ORDERED; +import static java.util.Spliterator.SIZED; +import static java.util.Spliterator.SUBSIZED; +import static libcore.java.util.SpliteratorTester.assertHasCharacteristics; + public final class CollectionsTest extends TestCase { private static final Object NOT_A_STRING = new Object(); @@ -104,36 +136,108 @@ private void testEmptyListIterator(ListIterator i) { } } - public static final class ArrayListInheritor extends ArrayList { - public ArrayListInheritor(int capacity) { - super(capacity); + static final class ArrayListInheritor extends ArrayList { + private int numSortCalls = 0; + public ArrayListInheritor(Collection initialElements) { + super(initialElements); + } + + @Override + public void sort(Comparator c) { + super.sort(c); + numSortCalls++; + } + + public int numSortCalls() { + return numSortCalls; } } - public void testSort_leavesModcountUnmodified() { - // This tests the fast path for ArrayLists where we can get away without - // a copy. - ArrayList list = new ArrayList(16); - list.add("coven"); - list.add("asylum"); - list.add("murder house"); - list.add("freak show"); + /** + * Tests that when targetSdk {@code <= 25}, Collections.sort() does not delegate + * to List.sort(). + */ + public void testSort_nougatOrEarlier_doesNotDelegateToListSort() { + runOnTargetSdk(25, () -> { // Nougat MR1 / MR2 + ArrayListInheritor list = new ArrayListInheritor<>( + Arrays.asList("a", "c", "b")); + assertEquals(0, list.numSortCalls()); + Collections.sort(list); + assertEquals(0, list.numSortCalls()); + }); + } + + public void testSort_postNougat_delegatesToListSort() { + runOnTargetSdkAtLeast(26, () -> { + ArrayListInheritor list = new ArrayListInheritor<>( + Arrays.asList("a", "c", "b")); + assertEquals(0, list.numSortCalls()); + Collections.sort(list); + assertEquals(1, list.numSortCalls()); + }); + } + + public void testSort_modcountUnmodifiedForLinkedList() { + runOnTargetSdkAtLeast(26, () -> { + LinkedList list = new LinkedList<>(Arrays.asList( + "red", "green", "blue", "violet")); + Iterator it = list.iterator(); + it.next(); + Collections.sort(list); + it.next(); // does not throw ConcurrentModificationException + }); + } + + public void testSort_modcountModifiedForArrayListAndSubclasses() { + runOnTargetSdkAtLeast(26, () -> { + List testData = Arrays.asList("red", "green", "blue", "violet"); + + ArrayList list = new ArrayList<>(testData); + Iterator it = list.iterator(); + it.next(); + Collections.sort(list); + try { + it.next(); + fail(); + } catch (ConcurrentModificationException expected) { + } - Iterator it = list.iterator(); - it.next(); - Collections.sort(list); - it.next(); + list = new ArrayListInheritor<>(testData); + it = list.iterator(); + it.next(); + Collections.sort(list); + try { + it.next(); + fail(); + } catch (ConcurrentModificationException expected) { + } + }); + } - list = new ArrayListInheritor(16); - list.add("apples"); - list.add("oranges"); - list.add("pineapples"); - list.add("bacon"); + /** + * Runs the given runnable on this thread with the targetSdkVersion temporarily set + * to the specified value, unless the current value is already higher. + */ + private static void runOnTargetSdkAtLeast(int minimumTargetSdkForTest, Runnable runnable) { + int targetSdkForTest = Math.max(minimumTargetSdkForTest, + VMRuntime.getRuntime().getTargetSdkVersion()); + runOnTargetSdk(targetSdkForTest, runnable); + } - it = list.iterator(); - it.next(); - Collections.sort(list); - it.next(); + /** + * Runs the given runnable on this thread with the targetSdkVersion temporarily set + * to the specified value. This helps test behavior that depends on an API level + * other than the current one (e.g. between releases). + */ + private static void runOnTargetSdk(int targetSdkForTest, Runnable runnable) { + VMRuntime runtime = VMRuntime.getRuntime(); + int targetSdk = runtime.getTargetSdkVersion(); + try { + runtime.setTargetSdkVersion(targetSdkForTest); + runnable.run(); + } finally { + runtime.setTargetSdkVersion(targetSdk); + } } /** @@ -211,22 +315,180 @@ public void testSingletonSpliterator() { assertEquals(false, sp.tryAdvance(value -> fail())); } + public void test_checkedNavigableMap_replaceAll() { + NavigableMap map = checkedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2)), + String.class, Integer.class); + map.replaceAll((k, v) -> 5 * v); + assertEquals( + createMap("key3", 15, "key1", 5, "key4", 20, "key2", 10), + map); + } + + public void test_checkedNavigableMap_putIfAbsent() { + NavigableMap map = + checkedNavigableMap(new TreeMap<>(), Integer.class, Double.class); + MapDefaultMethodTester.test_putIfAbsent(map, + false /* acceptsNullKey */, true /* acceptsNullValue */); + } + + public void test_checkedNavigableMap_remove() { + NavigableMap map = + checkedNavigableMap(new TreeMap<>(), Integer.class, Double.class); + MapDefaultMethodTester.test_remove(map, + false /* acceptsNullKey */, true /* acceptsNullValue */); + } + + public void test_checkedNavigableMap_replace$K$V() { + NavigableMap map = + checkedNavigableMap(new TreeMap<>(), Integer.class, Double.class); + MapDefaultMethodTester.test_replace$K$V$V(map, + false /* acceptsNullKey */, true /* acceptsNullValue */); + } + + public void test_checkedNavigableMap_replace$K$V$V() { + NavigableMap map = + checkedNavigableMap(new TreeMap<>(), Integer.class, Double.class); + MapDefaultMethodTester.test_replace$K$V$V(map, + false /* acceptsNullKey */, true /* acceptsNullValue */); + } + + public void test_checkedNavigableMap_computeIfAbsent() { + NavigableMap map = + checkedNavigableMap(new TreeMap<>(), Integer.class, Double.class); + MapDefaultMethodTester.test_computeIfAbsent(map, + false /* acceptsNullKey */, true /* acceptsNullValue */); + } + + public void test_checkedNavigableMap_computeIfPresent() { + NavigableMap map = + checkedNavigableMap(new TreeMap<>(), Integer.class, Double.class); + MapDefaultMethodTester.test_computeIfPresent(map, false /* acceptsNullKey */); + } + + public void test_checkedNavigableMap_compute() { + NavigableMap map = + checkedNavigableMap(new TreeMap<>(), Integer.class, Double.class); + MapDefaultMethodTester.test_compute(map, false /* acceptsNullKey */); + } + + public void test_checkedNavigableMap_merge() { + NavigableMap map = + checkedNavigableMap(new TreeMap<>(), Integer.class, Double.class); + MapDefaultMethodTester.test_merge(map, false /* acceptsNullKey */); + } + + public void test_checkedNavigableMap_navigableKeySet() { + NavigableMap map = checkedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2)), + String.class, Integer.class); + check_navigableSet( + map.navigableKeySet(), + Arrays.asList("key1", "key2", "key3", "key4") /* expectedElementsInOrder */, + "absent" /* absentElement */); + } + + public void test_checkedNavigableMap_values() { + NavigableMap map = checkedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2)), + String.class, Integer.class); + check_orderedCollection(map.values(), Arrays.asList(1, 2, 3, 4) /* expectedElementsInOrder */); + } + + public void test_checkedNavigableMap_isChecked() { + NavigableMap delegate = new TreeMap<>(); + delegate.put("present", 1); + delegate.put("another key", 2); + check_navigableMap_isChecked( + checkedNavigableMap(delegate, String.class, Integer.class), + "present", 1, "aaa absent", "zzz absent", 42); + } + + public void test_checkedNavigableSet() { + NavigableSet set = Collections.checkedNavigableSet(new TreeSet<>(), String.class); + check_navigableSet(set, Arrays.asList(), "absent element"); + + set.add("element 1"); + set.add("element 2"); + List elementsInOrder = Arrays.asList("element 1", "element 2"); + check_navigableSet(set, elementsInOrder, "absent element"); + + assertEquals(set, new HashSet<>(elementsInOrder)); + assertEquals(new HashSet<>(elementsInOrder), set); + assertEquals(2, set.size()); + assertTrue(set.contains("element 1")); + assertTrue(set.contains("element 2")); + assertFalse(set.contains("absent element")); + } + + public void test_checkedNavigableSet_isChecked() { + NavigableSet set = Collections.checkedNavigableSet(new TreeSet<>(), String.class); + assertThrowsCce(() -> { set.add(new Object()); }); + assertThrowsCce(() -> { set.addAll(Arrays.asList(new Object())); }); + } + + public void test_checkedQueue() { + Queue queue = checkedQueue(new LinkedBlockingDeque<>(2), CharSequence.class); + assertQueueEmpty(queue); + // Demonstrate that any implementation of CharSequence works by using two + // different ones (StringBuilder and String) as values. + StringBuilder firstElement = new StringBuilder("first element"); + assertTrue(queue.add(firstElement)); + assertFalse(queue.isEmpty()); + assertTrue(queue.add("second element")); + assertEquals(2, queue.size()); + + assertFalse(queue.offer("third element")); // queue is at capacity + try { + queue.add("third element"); + fail(); + } catch (IllegalStateException expected) { + } + assertThrowsCce(() -> { queue.add(new Object()); }); // fails the type check + assertEquals(2, queue.size()); // size is unchanged + + // element() and peek() don't remove the first element + assertSame(firstElement, queue.element()); + assertSame(firstElement, queue.peek()); + + assertSame(firstElement, queue.poll()); + assertSame("second element", queue.poll()); + assertQueueEmpty(queue); + + assertThrowsCce(() -> { queue.add(new Object()); }); // fails the type check + } + + /** + * Asserts properties that should hold for any empty queue. + */ + private static void assertQueueEmpty(Queue queue) { + assertTrue(queue.isEmpty()); + assertEquals(0, queue.size()); + assertNull(queue.peek()); + try { + queue.element(); + fail(); + } catch (NoSuchElementException expected) { + } + assertNull(queue.poll()); + } + public void test_unmodifiableMap_getOrDefault() { - HashMap hashMap = new HashMap<>(); - hashMap.put(2, 12.0); - hashMap.put(3, null); - Map m = Collections.unmodifiableMap(hashMap); + Map delegate = new HashMap<>(); + delegate.put(2, 12.0); + delegate.put(3, null); + Map m = Collections.unmodifiableMap(delegate); assertEquals(-1.0, m.getOrDefault(1, -1.0)); assertEquals(12.0, m.getOrDefault(2, -1.0)); assertEquals(null, m.getOrDefault(3, -1.0)); } public void test_unmodifiableMap_forEach() { - Map hashMap = new HashMap<>(); + Map delegate = new HashMap<>(); Map replica = new HashMap<>(); - hashMap.put(1, 10.0); - hashMap.put(2, 20.0); - Collections.unmodifiableMap(hashMap).forEach(replica::put); + delegate.put(1, 10.0); + delegate.put(2, 20.0); + Collections.unmodifiableMap(delegate).forEach(replica::put); assertEquals(10.0, replica.get(1)); assertEquals(20.0, replica.get(2)); assertEquals(2, replica.size()); @@ -240,7 +502,7 @@ public void test_unmodifiableMap_putIfAbsent() { } // For existing key - HashMap m = new HashMap<>(); + Map m = new HashMap<>(); m.put(1, 5.0); try { Collections.unmodifiableMap(m).putIfAbsent(1, 5.0); @@ -257,7 +519,7 @@ public void test_unmodifiableMap_remove() { } // For existing key - HashMap m = new HashMap<>(); + Map m = new HashMap<>(); m.put(1, 5.0); try { Collections.unmodifiableMap(m).remove(1, 5.0); @@ -274,7 +536,7 @@ public void test_unmodifiableMap_remove() { } // For existing key - HashMap m = new HashMap<>(); + Map m = new HashMap<>(); m.put(1, 5.0); try { Collections.unmodifiableMap(m).replace(1, 5.0, 1.0); @@ -291,7 +553,7 @@ public void test_unmodifiableMap_remove() { } // For existing key - HashMap m = new HashMap<>(); + Map m = new HashMap<>(); m.put(1, 5.0); try { Collections.unmodifiableMap(m).replace(1, 5.0); @@ -308,7 +570,7 @@ public void test_unmodifiableMap_computeIfAbsent() { } // For existing key - HashMap m = new HashMap<>(); + Map m = new HashMap<>(); m.put(1, 5.0); try { Collections.unmodifiableMap(m).computeIfAbsent(1, k -> 1.0); @@ -325,7 +587,7 @@ public void test_unmodifiableMap_computeIfPresent() { } // For existing key - HashMap m = new HashMap<>(); + Map m = new HashMap<>(); m.put(1, 5.0); try { Collections.unmodifiableMap(m).computeIfPresent(1, (k, v) -> 1.0); @@ -342,7 +604,7 @@ public void test_unmodifiableMap_compute() { } // For existing key - HashMap m = new HashMap<>(); + Map m = new HashMap<>(); m.put(1, 5.0); try { Collections.unmodifiableMap(m).compute(1, (k, v) -> 1.0); @@ -359,7 +621,7 @@ public void test_unmodifiableMap_merge() { } // For existing key - HashMap m = new HashMap<>(); + Map m = new HashMap<>(); m.put(1, 5.0); try { Collections.unmodifiableMap(m).merge(1, 2.0, (k, v) -> 1.0); @@ -368,6 +630,601 @@ public void test_unmodifiableMap_merge() { } } + public void test_unmodifiableNavigableMap_empty() { + NavigableMap map = unmodifiableNavigableMap(new TreeMap<>()); + + check_unmodifiableNavigableMap_defaultMethods(map, + Arrays.asList(), + Arrays.asList(), + "absent key", -1 /* absentValue */); + + check_unmodifiableNavigableMap_collectionViews(map, + Arrays.asList(), + Arrays.asList(), + "absent key"); + } + + public void test_unmodifiableNavigableMap_nonEmpty() { + NavigableMap map = unmodifiableNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2))); + + check_unmodifiableNavigableMap_defaultMethods(map, + Arrays.asList("key1", "key2", "key3", "key4"), + Arrays.asList(1, 2, 3, 4), + "absent key", -1 /* absentValue */); + + check_unmodifiableNavigableMap_collectionViews(map, + Arrays.asList("key1", "key2", "key3", "key4"), + Arrays.asList(1, 2, 3, 4), + "absent key"); + } + + public void test_unmodifiableNavigableSet_empty() { + NavigableSet set = Collections.unmodifiableNavigableSet(new TreeSet<>()); + check_unmodifiableSet(set, "absent element"); + check_navigableSet(set, new ArrayList<>(), "absent element"); + } + + public void test_unmodifiableNavigableSet_nonEmpty() { + NavigableSet delegate = new TreeSet<>(); + NavigableSet set = Collections.unmodifiableNavigableSet(delegate); + delegate.add("pear"); + delegate.add("banana"); + delegate.add("apple"); + delegate.add("melon"); + + check_unmodifiableNavigableSet(set, + Arrays.asList("apple", "banana", "melon", "pear"), + "absent element"); + + assertEquals("pear", set.ceiling("nonexistent")); + assertEquals("melon", set.floor("nonexistent")); + } + + public void test_synchronizedNavigableMap_replaceAll() { + NavigableMap map = synchronizedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2))); + map.replaceAll((k, v) -> 5 * v); + assertEquals(map, createMap("key3", 15, "key1", 5, "key4", 20, "key2", 10)); + } + + public void test_synchronizedNavigableMap_putIfAbsent() { + MapDefaultMethodTester.test_putIfAbsent( + Collections.synchronizedNavigableMap(new TreeMap<>()), + false /* acceptsNullKey */, true /* acceptsNullValue */); + } + + public void test_synchronizedNavigableMap_remove() { + MapDefaultMethodTester.test_remove( + Collections.synchronizedNavigableMap(new TreeMap<>()), + false /* acceptsNullKey */, true /* acceptsNullValue */); + } + + public void test_synchronizedNavigableMap_replace$K$V$V() { + MapDefaultMethodTester.test_replace$K$V$V( + Collections.synchronizedNavigableMap(new TreeMap<>()), + false /* acceptsNullKey */, true /* acceptsNullValue */); + } + + public void test_synchronizedNavigableMap_replace$K$V() { + MapDefaultMethodTester.test_replace$K$V( + Collections.synchronizedNavigableMap(new TreeMap<>()), + false /* acceptsNullKey */, true /* acceptsNullValue */); + } + + public void test_synchronizedNavigableMap_computeIfAbsent() { + MapDefaultMethodTester.test_computeIfAbsent( + Collections.synchronizedNavigableMap(new TreeMap<>()), + false /* acceptsNullKey */, true /* acceptsNullValue */); + } + + public void test_synchronizedNavigableMap_computeIfPresent() { + MapDefaultMethodTester.test_computeIfPresent( + Collections.synchronizedNavigableMap(new TreeMap<>()), + false /* acceptsNullKey */); + } + + public void test_synchronizedNavigableMap_compute() { + MapDefaultMethodTester.test_compute( + Collections.synchronizedNavigableMap(new TreeMap<>()), + false /* acceptsNullKey */); + } + + public void test_synchronizedNavigableMap_merge() { + MapDefaultMethodTester.test_merge( + Collections.synchronizedNavigableMap(new TreeMap<>()), + false /* acceptsNullKey */); + } + + public void test_synchronizedNavigableMap_keySet() { + NavigableMap map = synchronizedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2))); + // Note: keySet() returns a Collections$UnmodifiableSet (not instanceof NavigableSet) + Set set = map.keySet(); + check_orderedSet(set, Arrays.asList("key1", "key2", "key3", "key4")); + } + + public void test_synchronizedNavigableMap_navigableKeySet() { + NavigableMap map = synchronizedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2))); + NavigableSet set = map.navigableKeySet(); + check_navigableSet(set, Arrays.asList("key1", "key2", "key3", "key4"), "absent element"); + } + + public void test_synchronizedNavigableMap_descendingMap_descendingKeySet() { + NavigableMap map = synchronizedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2))); + NavigableSet set = map.descendingMap().descendingKeySet(); + check_navigableSet(set, Arrays.asList("key1", "key2", "key3", "key4"), "absent element"); + } + + public void test_synchronizedNavigableMap_descendingKeySet() { + NavigableMap map = synchronizedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2))); + NavigableSet set = map.descendingKeySet(); + check_navigableSet(set, Arrays.asList("key4", "key3", "key2", "key1"), "absent element"); + } + + public void test_synchronizedNavigableMap_descendingMap_keySet() { + NavigableMap map = synchronizedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2))); + // Note: keySet() returns a Collections$UnmodifiableSet (not instanceof NavigableSet) + Set set = map.descendingMap().keySet(); + check_orderedSet(set, Arrays.asList("key4", "key3", "key2", "key1")); + } + + public void test_synchronizedNavigableMap_descendingMap_navigableKeySet() { + NavigableMap map = synchronizedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2))); + NavigableSet set = map.descendingMap().navigableKeySet(); + check_navigableSet(set, Arrays.asList("key4", "key3", "key2", "key1"), "absent element"); + } + + public void test_synchronizedNavigableMap_values() { + NavigableMap map = synchronizedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2))); + Collection values = map.values(); + check_orderedCollection(values, Arrays.asList(1, 2, 3, 4)); + } + + public void test_synchronizedNavigableMap_descendingMap_values() { + NavigableMap map = synchronizedNavigableMap( + new TreeMap<>(createMap("key3", 3, "key1", 1, "key4", 4, "key2", 2))); + Collection values = map.descendingMap().values(); + check_orderedCollection(values, Arrays.asList(4, 3, 2, 1)); + } + + public void test_synchronizedNavigableSet_empty() { + NavigableSet set = Collections.synchronizedNavigableSet(new TreeSet<>()); + check_navigableSet(set, new ArrayList<>(), "absent element"); + } + + public void test_synchronizedNavigableSet_nonEmpty() { + List elements = Arrays.asList("apple", "banana", "melon", "pear"); + NavigableSet set = Collections.synchronizedNavigableSet(new TreeSet<>(elements)); + check_navigableSet(set, elements, "absent element"); + } + + private static void check_unmodifiableNavigableMap_defaultMethods(NavigableMap map, + List keysInOrder, List valuesInOrder, K absentKey, V absentValue) { + check_unmodifiableOrderedMap_defaultMethods(map, keysInOrder, valuesInOrder, + absentKey, absentValue); + + List reverseKeys = reverseCopyOf(keysInOrder); + List reverseValues = reverseCopyOf(valuesInOrder); + + check_unmodifiableOrderedMap_defaultMethods(map.descendingMap(), reverseKeys, + reverseValues, absentKey, absentValue); + + int numEntries = keysInOrder.size(); + for (int i = 0; i < numEntries; i++) { + K key = keysInOrder.get(i); + V value = valuesInOrder.get(i); + + check_unmodifiableOrderedMap_defaultMethods( + map.headMap(key), + keysInOrder.subList(0, i), + valuesInOrder.subList(0, i), + absentKey, + absentValue); + check_unmodifiableOrderedMap_defaultMethods( + map.headMap(key, false /* inclusive */), + keysInOrder.subList(0, i), + valuesInOrder.subList(0, i), + absentKey, + absentValue); + check_unmodifiableOrderedMap_defaultMethods( + map.headMap(key, true /* inclusive */), + keysInOrder.subList(0, i + 1), + valuesInOrder.subList(0, i + 1), + absentKey, + absentValue); + K lowerKey = map.lowerKey(key); + if (lowerKey != null) { + // headMap inclusive of lowerKey is same as exclusive of key + check_unmodifiableOrderedMap_defaultMethods( + map.headMap(lowerKey, true /* inclusive */), + keysInOrder.subList(0, i), + valuesInOrder.subList(0, i), + absentKey, + absentValue); + } + + check_unmodifiableOrderedMap_defaultMethods( + map.tailMap(key), + keysInOrder.subList(i, numEntries), + valuesInOrder.subList(i, numEntries), + absentKey, + absentValue); + check_unmodifiableOrderedMap_defaultMethods( + map.tailMap(key, true /* inclusive */), + keysInOrder.subList(i, numEntries), + valuesInOrder.subList(i, numEntries), + absentKey, + absentValue); + check_unmodifiableOrderedMap_defaultMethods( + map.tailMap(key, false /* inclusive */), + keysInOrder.subList(i + 1, numEntries), + valuesInOrder.subList(i + 1, numEntries), + absentKey, + absentValue); + K higherKey = map.higherKey(key); + if (higherKey != null) { + // headMap inclusive of higherKey is same as exclusive of key + check_unmodifiableOrderedMap_defaultMethods( + map.tailMap(higherKey, true /* inclusive */), + keysInOrder.subList(i + 1, numEntries), + valuesInOrder.subList(i + 1, numEntries), + absentKey, + absentValue); + } + + int headSize = map.headMap(absentKey).size(); + check_unmodifiableOrderedMap_defaultMethods( + map.headMap(absentKey, true /* inclusive */), + keysInOrder.subList(0, headSize), + valuesInOrder.subList(0, headSize), + absentKey, + absentValue); + check_unmodifiableOrderedMap_defaultMethods( + map.tailMap(absentKey, true /* inclusive */), + keysInOrder.subList(headSize, numEntries), + valuesInOrder.subList(headSize, numEntries), + absentKey, + absentValue); + + assertEquals(key, map.floorKey(key)); + assertEquals(key, map.ceilingKey(key)); + assertEquals(new AbstractMap.SimpleEntry<>(key, value), map.floorEntry(key)); + assertEquals(new AbstractMap.SimpleEntry<>(key, value), map.ceilingEntry(key)); + } + + K floor = map.floorKey(absentKey); + K ceiling = map.ceilingKey(absentKey); + if (numEntries == 0) { + assertNull(floor); + assertNull(ceiling); + } else { + assertFalse(Objects.equal(floor, ceiling)); + assertTrue(floor != null || ceiling != null); + assertEquals(ceiling, floor == null ? map.firstKey() : map.higherKey(floor)); + assertEquals(floor, ceiling == null ? map.lastKey() : map.lowerKey(ceiling)); + } + } + + /** + * Tests Map's default methods (getOrDefault, forEach, ...) on the given Map. + * + * @param keysInOrder the expected keys in the map, in iteration order + * @param valuesInOrder the expected values in the map, in iteration order + * @param absentKey a key that does not occur in the map + * @param absentValue a value that does not occur in the map + */ + private static void check_unmodifiableOrderedMap_defaultMethods(Map map, + List keysInOrder, List valuesInOrder, K absentKey, V absentValue) { + if (keysInOrder.size() != valuesInOrder.size()) { + throw new IllegalArgumentException(); + } + Map mapCopy = new LinkedHashMap(map); + + // getOrDefault + int numEntries = keysInOrder.size(); + for (int i = 0; i < numEntries; i++) { + assertEquals(valuesInOrder.get(i), map.getOrDefault(keysInOrder.get(i), null)); + } + + // forEach + List keysCopy = new ArrayList<>(); + List valuesCopy = new ArrayList<>(); + map.forEach((k, v) -> { + keysCopy.add(k); + valuesCopy.add(v); + }); + assertEquals(keysInOrder, keysCopy); + assertEquals(valuesInOrder, valuesCopy); + + assertThrowsUoe(() -> { map.putIfAbsent(absentKey, absentValue); }); + assertThrowsUoe(() -> { map.remove(absentKey); }); + assertThrowsUoe(() -> { map.replace(absentKey, absentValue, absentValue); }); + assertThrowsUoe(() -> { map.replace(absentKey, absentValue); }); + assertThrowsUoe(() -> { map.computeIfAbsent(absentKey, k -> absentValue); }); + assertThrowsUoe(() -> { map.computeIfPresent(absentKey, (k, v) -> absentValue); }); + assertThrowsUoe(() -> { map.compute(absentKey, (k, v) -> absentValue); }); + assertThrowsUoe(() -> { map.merge(absentKey, absentValue, (k, v) -> absentValue); }); + + if (numEntries > 0) { + K sampleKey = keysInOrder.get(0); + V sampleValue = valuesInOrder.get(0); + assertThrowsUoe(() -> { map.putIfAbsent(sampleKey, absentValue); }); + assertThrowsUoe(() -> { map.remove(sampleKey); }); + assertThrowsUoe(() -> { map.replace(sampleKey, sampleValue, absentValue); }); + assertThrowsUoe(() -> { map.replace(sampleKey, absentValue); }); + assertThrowsUoe(() -> { map.computeIfAbsent(sampleKey, k -> absentValue); }); + assertThrowsUoe(() -> { map.computeIfPresent(sampleKey, (k, v) -> absentValue); }); + assertThrowsUoe(() -> { map.compute(sampleKey, (k, v) -> absentValue); }); + assertThrowsUoe(() -> { map.merge(sampleKey, sampleValue, (k, v) -> absentValue); }); + } + + // Check that map is unchanged + assertEquals(mapCopy, map); + } + + /** + * Tests the various {@code Collection} views of the given Map for contents/ + * iteration order consistent with the given expectations. + */ + private static void check_unmodifiableNavigableMap_collectionViews( + NavigableMap map, List keysInOrder, List valuesInOrder, K absentKey) { + List reverseKeys = reverseCopyOf(keysInOrder); + + // keySet + check_unmodifiableSet(map.keySet(), absentKey); + check_orderedSet(map.keySet(), keysInOrder); + + // navigableKeySet + check_unmodifiableNavigableSet(map.navigableKeySet(), keysInOrder, absentKey); + + // descendingMap -> descendingKeySet + check_unmodifiableNavigableSet( + map.descendingMap().descendingKeySet(), keysInOrder, absentKey); + + // descendingKeySet + check_unmodifiableNavigableSet(map.descendingKeySet(), reverseKeys, absentKey); + + // descendingMap -> keySet + check_unmodifiableSet(map.descendingMap().keySet(), absentKey); + check_orderedSet(map.descendingMap().keySet(), reverseKeys); + + // descendingMap -> navigableKeySet + check_unmodifiableNavigableSet( + map.descendingMap().navigableKeySet(), reverseKeys, absentKey); + + // values + check_unmodifiableOrderedCollection(map.values(), valuesInOrder); + check_orderedCollection(map.values(), valuesInOrder); + + // descendingValues + check_unmodifiableOrderedCollection(map.descendingMap().values(), reverseCopyOf(valuesInOrder)); + check_orderedCollection(map.descendingMap().values(), reverseCopyOf(valuesInOrder)); + } + + /** + * @param absentKeyHead absent key smaller than {@code presentKey}, under the Map's ordering + * @param absentKeyTail absent key larger than {@code presentKey}, under the Map's ordering + */ + private static void check_navigableMap_isChecked(NavigableMap map, + K presentKey, V presentValue, K absentKeyHead, K absentKeyTail, V absentValue) { + check_map_isChecked(map, + presentKey, presentValue, absentKeyHead, absentValue); + check_map_isChecked(map.descendingMap(), + presentKey, presentValue, absentKeyHead, absentValue); + + // Need to pass correct absent key since the Map might check for + // range inclusion before checking the type of a value + check_map_isChecked(map.headMap(presentKey, true /* inclusive */), + presentKey, presentValue, absentKeyHead, absentValue); + check_map_isChecked(map.tailMap(presentKey, true /* inclusive */), + presentKey, presentValue, absentKeyTail, absentValue); + } + + /** + * Asserts that the given map is checked (rejects keys/values of type Object). + * + * @param map a checked Map that contains the entry (presentKey, preventValue), does not + * contain key absentKey or value absentValue, and rejects keys/types of type Object. + */ + private static void check_map_isChecked(Map map, + K presentKey, V presentValue, K absentKey, V absentValue) { + Map copyOfMap = new HashMap(map); + assertEquals(map.get(presentKey), presentValue); + assertFalse(map.containsKey(absentKey)); + assertFalse(map.values().contains(absentValue)); + + assertThrowsCce(() -> { map.replaceAll((k, v) -> new Object()); }); + + assertThrowsCce(() -> { map.putIfAbsent(presentKey, new Object()); }); + assertThrowsCce(() -> { map.putIfAbsent(absentKey, new Object()); }); + assertThrowsCce(() -> { map.putIfAbsent(new Object(), presentValue); }); + + assertThrowsCce(() -> { map.remove(new Object()); }); + + assertThrowsCce(() -> { map.replace(new Object(), presentValue); }); + assertThrowsCce(() -> { map.replace(presentKey, new Object()); }); + + assertThrowsCce(() -> { map.replace(new Object(), presentValue, absentValue); }); + // doesn't throw, but has no effect since oldValue doesn't match + assertFalse(map.replace(presentKey, new Object(), absentValue)); + assertThrowsCce(() -> { map.replace(presentKey, presentValue, new Object()); }); + + assertThrowsCce(() -> { map.computeIfAbsent(new Object(), k -> presentValue); }); + // doesn't throw, but has no effect since presentKey is present + assertEquals(presentValue, map.computeIfAbsent(presentKey, k -> new Object())); + assertThrowsCce(() -> { map.computeIfAbsent(absentKey, k -> new Object()); }); + + assertThrowsCce(() -> { map.computeIfPresent(new Object(), (k, v) -> presentValue); }); + assertThrowsCce(() -> { map.computeIfPresent(presentKey, (k, v) -> new Object()); }); + // doesn't throw, but has no effect since absentKey is absent + assertNull(map.computeIfPresent(absentKey, (k, v) -> new Object())); + + assertThrowsCce(() -> { map.compute(new Object(), (k, v) -> presentValue); }); + assertThrowsCce(() -> { map.compute(presentKey, (k, v) -> new Object()); }); + assertThrowsCce(() -> { map.compute(absentKey, (k, v) -> new Object()); }); + + assertThrowsCce(() -> { map.merge(new Object(), presentValue, (v1, v2) -> presentValue); }); + assertThrowsCce(() -> { map.merge(presentKey, presentValue, (v1, v2) -> new Object()); }); + + // doesn't throw, puts (absentKey, absentValue) into the map + map.merge(absentKey, absentValue, (v1, v2) -> new Object()); + assertEquals(absentValue, map.remove(absentKey)); // restore previous state + + assertThrowsCce(() -> { map.put(new Object(), absentValue); }); + assertThrowsCce(() -> { map.put(absentKey, new Object()); }); + assertThrowsCce(() -> { map.put(new Object(), presentValue); }); + assertThrowsCce(() -> { map.put(presentKey, new Object()); }); + + assertEquals("map should be unchanged", copyOfMap, map); + } + + private static void check_unmodifiableNavigableSet(NavigableSet set, + List expectedElementsInOrder, K absentElement) { + check_unmodifiableSet(set, absentElement); + check_unmodifiableSet(set.descendingSet(), absentElement); + check_navigableSet(set, expectedElementsInOrder, absentElement); + if (!expectedElementsInOrder.isEmpty()) { + K sampleElement = expectedElementsInOrder.get(expectedElementsInOrder.size() / 2); + check_unmodifiableSet(set.headSet(sampleElement), absentElement); + check_unmodifiableSet(set.tailSet(sampleElement), absentElement); + } + } + + private static void check_navigableSet(NavigableSet set, + List expectedElementsInOrder, K absentElement) { + check_orderedSet(set, expectedElementsInOrder); + check_set(set, absentElement); + + int numElements = set.size(); + List reverseOrder = new ArrayList<>(expectedElementsInOrder); + Collections.reverse(reverseOrder); + check_orderedSet(set.descendingSet(), reverseOrder); + + for (int i = 0; i < numElements; i++) { + K element = expectedElementsInOrder.get(i); + check_orderedSet( + set.headSet(element), + expectedElementsInOrder.subList(0, i)); + check_orderedSet( + set.headSet(element, false /* inclusive */), + expectedElementsInOrder.subList(0, i)); + check_orderedSet( + set.headSet(element, true /* inclusive */), + expectedElementsInOrder.subList(0, i + 1)); + + check_orderedSet( + set.tailSet(element), + expectedElementsInOrder.subList(i, numElements)); + check_orderedSet( + set.tailSet(element, true /* inclusive */), + expectedElementsInOrder.subList(i, numElements)); + check_orderedSet( + set.tailSet(element, false /* inclusive */), + expectedElementsInOrder.subList(i + 1, numElements)); + + assertEquals(element, set.floor(element)); + assertEquals(element, set.ceiling(element)); + } + + K floor = set.floor(absentElement); + K ceiling = set.ceiling(absentElement); + if (numElements == 0) { + assertNull(floor); + assertNull(ceiling); + } else { + assertFalse(Objects.equal(floor, ceiling)); + assertTrue(floor != null || ceiling != null); + } + } + + /** + * Checks a Set that may or may not be instanceof SortedSet / NavigableSet + * for adherence to a specified iteration order. + */ + private static void check_orderedSet( + Set set, List expectedElementsInOrder) { + assertEquals(expectedElementsInOrder, new ArrayList<>(set)); + Set copy = new HashSet<>(expectedElementsInOrder); + assertEquals(copy, set); + assertEquals(copy.hashCode(), set.hashCode()); + + int numElements = set.size(); + assertEquals(expectedElementsInOrder.size(), numElements); + Spliterator spliterator = set.spliterator(); + SpliteratorTester.runBasicIterationTests(spliterator, expectedElementsInOrder); + if (spliterator.hasCharacteristics(SIZED)) { + SpliteratorTester.runSizedTests(set, numElements); + } + if (spliterator.hasCharacteristics(SUBSIZED)) { + SpliteratorTester.runSubSizedTests(set, numElements); + } + assertHasCharacteristics(ORDERED | DISTINCT, spliterator); + SpliteratorTester.runOrderedTests(set); + } + + private static void check_unmodifiableSet(Set set, K absentElement) { + assertThrowsUoe(() -> { set.remove(null); } ); + assertThrowsUoe(set::clear); + assertThrowsUoe(() -> { set.add(null); } ); + if (set.isEmpty()) { + assertEquals(0, set.size()); + } else { + assertTrue(set.size() > 0); + Iterator iterator = set.iterator(); + K firstElement = iterator.next(); + assertThrowsUoe(() -> { set.remove(firstElement); } ); + assertThrowsUoe(iterator::remove); + } + SpliteratorTester.runDistinctTests(set); + + check_set(set, absentElement); + } + + private static void check_set(Set set, K absentElement) { + // some basic properties that must hold for all sets (regardless of whether + // they're ordered, strict, support null, ...): + if (!set.isEmpty()) { + K sampleElement = set.iterator().next(); + assertTrue(set.contains(sampleElement)); + } + assertFalse(set.contains(absentElement)); + } + + private static void check_unmodifiableOrderedCollection( + Collection values, List elementsInOrder) { + assertThrowsUoe(() -> { values.remove(null); } ); + assertThrowsUoe(values::clear); + assertThrowsUoe(() -> { values.add(null); } ); + + Iterator iterator = values.iterator(); + if (!elementsInOrder.isEmpty()) { + iterator.next(); + assertThrowsUoe(iterator::remove); + assertThrowsUoe(() -> { values.remove(elementsInOrder.get(0)); }); + } + check_orderedCollection(values, elementsInOrder); + } + + private static void check_orderedCollection( + Collection collection, List elementsInOrder) { + Spliterator spliterator = collection.spliterator(); + SpliteratorTester.runBasicIterationTests(spliterator, elementsInOrder); + if (spliterator.hasCharacteristics(SIZED)) { + SpliteratorTester.runSizedTests(collection, elementsInOrder.size()); + } + if (spliterator.hasCharacteristics(SUBSIZED)) { + SpliteratorTester.runSubSizedTests(collection, elementsInOrder.size()); + } + SpliteratorTester.runOrderedTests(collection); + } + public void test_EmptyMap_getOrDefault() { Map m = Collections.emptyMap(); assertEquals(-1.0, m.getOrDefault(1, -1.0)); @@ -553,6 +1410,50 @@ public void test_EmptyList_sort() { Collections.emptyList().sort((k1, k2) -> 1); } + public void test_emptyNavigableMap() { + NavigableMap map = Collections.emptyNavigableMap(); + check_unmodifiableNavigableMap_defaultMethods( + map, + new ArrayList<>() /* keysInOrder */, + new ArrayList<>() /* valuesInOrder */, + "absent key" /* absentKey */, + -1 /* absentValue */ + ); + check_unmodifiableNavigableMap_collectionViews( + map, + new ArrayList<>() /* keysInOrder */, + new ArrayList<>() /* valuesInOrder */, + "absent key" /* absentKey */); + } + + public void test_emptyNavigableSet() { + NavigableSet set = Collections.emptyNavigableSet(); + check_unmodifiableNavigableSet(set, new ArrayList<>() /* expectedElementsInOrder */, + "absent element"); + check_navigableSet(set, new ArrayList<>() /* expectedElementsInOrder */, "absent element"); + } + + public void test_emptySortedMap() { + SortedMap map = Collections.emptySortedMap(); + + check_unmodifiableOrderedMap_defaultMethods( + map, + new ArrayList<>() /* keysInOrder */, + new ArrayList<>() /* valuesInOrder */, + "absent key" /* absentKey */, + -1 /* absentValue */); + check_unmodifiableSet(map.keySet(), "absent element"); + check_orderedSet(map.keySet(), new ArrayList<>() /* expectedElementsInOrder */); + check_unmodifiableSet(map.entrySet(), new AbstractMap.SimpleEntry<>("absent element", 42)); + check_orderedCollection(map.values(), new ArrayList<>() /* expectedValuesInOrder */); + } + + public void test_emptySortedSet() { + SortedSet set = Collections.emptySortedSet(); + check_unmodifiableSet(set, "absent element"); + check_orderedSet(set, new ArrayList<>() /* expectedElementsInOrder */); + } + public void test_unmodifiableList_replaceAll() { try { Collections.unmodifiableList(new ArrayList<>()).replaceAll(k -> 1); @@ -629,12 +1530,14 @@ public void test_CheckedMap_putIfAbsent() { checkedMap2.putIfAbsent(1, A_STRING); try { checkedMap2.putIfAbsent(1, NOT_A_STRING); + fail(); } catch (ClassCastException expected) {} // When key is absent checkedMap2.clear(); try { checkedMap2.putIfAbsent(1, NOT_A_STRING); + fail(); } catch (ClassCastException expected) {} } @@ -657,6 +1560,7 @@ public void test_CheckedMap_remove() { try { checkedMap2.replace(1, NOT_A_STRING); + fail(); } catch (ClassCastException expected) {} } @@ -672,6 +1576,7 @@ public void test_CheckedMap_remove() { try { checkedMap2.replace(1, 1, NOT_A_STRING); + fail(); } catch (ClassCastException expected) {} } @@ -685,15 +1590,16 @@ public void test_CheckedMap_computeIfAbsent() { Map checkedMap2 = Collections.checkedMap(new HashMap<>(), Integer.class, String.class); checkedMap2.put(1, A_STRING); - // When key is present - try { - checkedMap2.computeIfAbsent(1, k -> NOT_A_STRING); - } catch (ClassCastException expected) {} + // When key is present, function should not be invoked + assertSame(A_STRING, checkedMap2.computeIfAbsent(1, k -> { + throw new AssertionFailedError("key present: function should not be invoked"); + })); - // When key is absent + // When key is absent, computed value's type should be checked checkedMap2.clear(); try { checkedMap2.computeIfAbsent(1, k -> NOT_A_STRING); + fail(); } catch (ClassCastException expected) {} } @@ -709,6 +1615,7 @@ public void test_CheckedMap_computeIfPresent() { try { checkedMap2.computeIfPresent(1, (k, v) -> NOT_A_STRING); + fail(); } catch (ClassCastException expected) {} } @@ -721,6 +1628,7 @@ public void test_CheckedMap_compute() { checkedMap2.put(1, A_STRING); try { checkedMap2.compute(1, (k, v) -> NOT_A_STRING); + fail(); } catch (ClassCastException expected) {} } @@ -736,6 +1644,40 @@ public void test_CheckedMap_merge() { try { checkedMap2.merge(1, A_STRING, (v1, v2) -> NOT_A_STRING); + fail(); } catch (ClassCastException expected) {} } + + private static Map createMap(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + Map result = new HashMap<>(); + result.put(k1, v1); + result.put(k2, v2); + result.put(k3, v3); + result.put(k4, v4); + return result; + } + + private static void assertThrowsUoe(Runnable runnable) { + try { + runnable.run(); + fail(); + } catch (UnsupportedOperationException expected) { + } + } + + private static void assertThrowsCce(Runnable runnable) { + try { + runnable.run(); + fail(); + } catch (ClassCastException expected) { + } + } + + private static List reverseCopyOf(List list) { + List result = new LinkedList<>(); + for (T element : list) { + result.add(0, element); + } + return result; + } } diff --git a/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java b/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java index 74d847398..397108cfd 100644 --- a/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java +++ b/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java @@ -22,7 +22,8 @@ public class ConcurrentHashMapTest extends junit.framework.TestCase { public void test_getOrDefault() { MapDefaultMethodTester.test_getOrDefault(new ConcurrentHashMap<>(), - false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/); + false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/, + true /*getAcceptsAnyObject*/); } public void test_forEach() { diff --git a/luni/src/test/java/libcore/java/util/CurrencyTest.java b/luni/src/test/java/libcore/java/util/CurrencyTest.java index 58c958af0..552ad9e9b 100644 --- a/luni/src/test/java/libcore/java/util/CurrencyTest.java +++ b/luni/src/test/java/libcore/java/util/CurrencyTest.java @@ -35,6 +35,29 @@ public void test_getSymbol_fallback() throws Exception { assertEquals("AED", Currency.getInstance("AED").getSymbol(Locale.CANADA)); } + public void test_getSymbol_locale() { + Currency currency = Currency.getInstance("DEM"); + assertEquals("DEM", currency.getSymbol(Locale.FRANCE)); + assertEquals("DM", currency.getSymbol(Locale.GERMANY)); + assertEquals("DEM", currency.getSymbol(Locale.US)); + } + + /** + * Checks that the no-argument version of {@link Currency#getSymbol()} uses the + * default DISPLAY locale as opposed to the default locale or the default FORMAT + * locale. + */ + public void test_getSymbol_noLocaleArgument() { + Currency currency = Currency.getInstance("DEM"); + Locales locales = Locales.getAndSetDefaultForTest(Locale.US, Locale.GERMANY, Locale.FRANCE); + try { + // getAndSetDefaultForTest(uncategorizedLocale, displayLocale, formatLocale) + assertEquals("DM", currency.getSymbol()); + } finally { + locales.setAsDefault(); + } + } + // Regression test to ensure that Currency.getInstance(String) throws if // given an invalid ISO currency code. public void test_getInstance_illegal_currency_code() throws Exception { @@ -56,11 +79,44 @@ public void testGetAvailableCurrencies() throws Exception { assertTrue(all.toString(), all.contains(Currency.getInstance("USD"))); } - public void test_getDisplayName() throws Exception { - assertEquals("Swiss Franc", Currency.getInstance("CHF").getDisplayName(Locale.US)); - assertEquals("Schweizer Franken", Currency.getInstance("CHF").getDisplayName(new Locale("de", "CH"))); - assertEquals("franc suisse", Currency.getInstance("CHF").getDisplayName(new Locale("fr", "CH"))); - assertEquals("franco svizzero", Currency.getInstance("CHF").getDisplayName(new Locale("it", "CH"))); + public void test_getDisplayName_locale_chf() throws Exception { + Currency currency = Currency.getInstance("CHF"); + assertEquals("Swiss Franc", currency.getDisplayName(Locale.US)); + assertEquals("Schweizer Franken", currency.getDisplayName(new Locale("de", "CH"))); + assertEquals("franc suisse", currency.getDisplayName(new Locale("fr", "CH"))); + assertEquals("franco svizzero", currency.getDisplayName(new Locale("it", "CH"))); + } + + public void test_getDisplayName_locale_dem() throws Exception { + Currency currency = Currency.getInstance("DEM"); + assertEquals("Deutsche Mark", currency.getDisplayName(Locale.GERMANY)); + assertEquals("German Mark", currency.getDisplayName(Locale.US)); + assertEquals("mark allemand", currency.getDisplayName(Locale.FRANCE)); + } + + public void test_getDisplayName_null() { + Currency currency = Currency.getInstance("CHF"); + try { + currency.getDisplayName(null); + fail(); + } catch (NullPointerException expected) { + } + } + + /** + * Checks that the no-argument version of {@link Currency#getDisplayName()} uses + * the default DISPLAY locale, as opposed to the default locale or the default + * FORMAT locale. + */ + public void test_getDisplayName_noLocaleArgument() { + Currency currency = Currency.getInstance("DEM"); + // getAndSetDefaultForTest(uncategorizedLocale, displayLocale, formatLocale) + Locales locales = Locales.getAndSetDefaultForTest(Locale.US, Locale.GERMANY, Locale.FRANCE); + try { + assertEquals("Deutsche Mark", currency.getDisplayName()); + } finally { + locales.setAsDefault(); + } } public void test_getDefaultFractionDigits() throws Exception { @@ -108,4 +164,5 @@ public void test_getNumericCode() throws Exception { assertEquals(999, Currency.getInstance("XXX").getNumericCode()); assertEquals(0, Currency.getInstance("XFU").getNumericCode()); } + } diff --git a/luni/src/test/java/libcore/java/util/DateTest.java b/luni/src/test/java/libcore/java/util/DateTest.java index d807007a4..df86a3884 100644 --- a/luni/src/test/java/libcore/java/util/DateTest.java +++ b/luni/src/test/java/libcore/java/util/DateTest.java @@ -16,6 +16,7 @@ package libcore.java.util; +import java.time.Instant; import java.util.Calendar; import java.util.Date; import java.util.Locale; @@ -77,6 +78,98 @@ public void test_parse_timezones() { assertEquals( Date.parse("Wed, 06 Jan 2016 11:55:59 GMT+05:00"), Date.parse("Wed, 06 Jan 2016 11:55:59 GMT+05")); + } + + /** + * Test that conversion between Date and Instant works when the + * Instant is based on a millisecond value (and thus can be + * represented as a Date). + */ + public void test_convertFromAndToInstant_milliseconds() { + check_convertFromAndToInstant_milliseconds(Long.MIN_VALUE); + check_convertFromAndToInstant_milliseconds(Long.MAX_VALUE); + + check_convertFromAndToInstant_milliseconds(-1); + check_convertFromAndToInstant_milliseconds(0); + check_convertFromAndToInstant_milliseconds(123456789); + } + + private static void check_convertFromAndToInstant_milliseconds(long millis) { + assertEquals(new Date(millis), Date.from(Instant.ofEpochMilli(millis))); + assertEquals(new Date(millis).toInstant(), Instant.ofEpochMilli(millis)); + } + + /** + * Checks the minimum/maximum Instant values (based on seconds and + * nanos) that can be converted to a Date, i.e. that can be converted + * to milliseconds without overflowing a long. Note that the rounding + * is such that the lower bound is exactly Long.MIN_VALUE msec whereas + * the upper bound is 999,999 nanos beyond Long.MAX_VALUE msec. This + * makes some sense in that the magnitude of the upper/lower bound + * nanos differ only by 1, just like the magnitude of Long.MIN_VALUE / + * MAX_VALUE differ only by 1. + */ + public void test_convertFromInstant_secondsAndNanos() { + // Documentation for how the below bounds relate to long boundaries for milliseconds + assertEquals(-808, Long.MIN_VALUE % 1000); + assertEquals(807, Long.MAX_VALUE % 1000); + + // Lower bound + long minSecond = Long.MIN_VALUE / 1000; + Date.from(Instant.ofEpochSecond(minSecond)); + // This instant exactly corresponds to Long.MIN_VALUE msec because + // Long.MIN_VALUE % 1000 == -808 == (-1000 + 192) + Date.from(Instant.ofEpochSecond(minSecond - 1, 192000000)); + assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(minSecond - 1, 0)); + assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(minSecond - 1, 191999999)); + + // Upper bound + long maxSecond = Long.MAX_VALUE / 1000; + Date.from(Instant.ofEpochSecond(maxSecond, 0)); + // This Instant is 999,999 nanos beyond Long.MAX_VALUE msec because + // (Long.MAX_VALUE % 1000) == 807 + Date.from(Instant.ofEpochSecond(maxSecond, 807999999)); + assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(maxSecond + 1, 0)); + assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(maxSecond, 808000000)); + } + + private static void assertArithmeticOverflowDateFrom(Instant instant) { + try { + Date.from(instant); + fail(instant + " should not have been convertible to Date"); + } catch (IllegalArgumentException expected) { + } + } + + /** + * Checks conversion between long, Date and Instant. + */ + public void test_convertToInstantAndBack() { + check_convertToInstantAndBack(0); + check_convertToInstantAndBack(-1); + check_convertToInstantAndBack( 999999999); + check_convertToInstantAndBack(1000000000); + check_convertToInstantAndBack(1000000001); + check_convertToInstantAndBack(1000000002); + check_convertToInstantAndBack(1000000499); + check_convertToInstantAndBack(1000000500); + check_convertToInstantAndBack(1000000999); + check_convertToInstantAndBack(1000001000); + check_convertToInstantAndBack(Long.MIN_VALUE + 808); // minimum ofEpochMilli argument + check_convertToInstantAndBack(Long.MAX_VALUE); + check_convertToInstantAndBack(System.currentTimeMillis()); + check_convertToInstantAndBack(Date.parse("Wed, 06 Jan 2016 11:55:59 GMT+0500")); + } + + private static void check_convertToInstantAndBack(long millis) { + Date date = new Date(millis); + Instant instant = date.toInstant(); + assertEquals(date, Date.from(instant)); + + assertEquals(instant, Instant.ofEpochMilli(millis)); + assertEquals("Millis should be a millions of nanos", 0, instant.getNano() % 1000000); + assertEquals(millis, date.getTime()); + assertEquals(millis, instant.toEpochMilli()); } } diff --git a/luni/src/test/java/libcore/java/util/FormatterTest.java b/luni/src/test/java/libcore/java/util/FormatterTest.java index e87fee562..d3bd313fb 100644 --- a/luni/src/test/java/libcore/java/util/FormatterTest.java +++ b/luni/src/test/java/libcore/java/util/FormatterTest.java @@ -17,7 +17,11 @@ package libcore.java.util; import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; import java.util.Calendar; +import java.util.Formatter; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; @@ -183,4 +187,24 @@ private static void checkFormat(String expected, String pattern, int hour) { assertEquals(expected, String.format(Locale.US, "%t" + pattern, c)); assertEquals(expected, String.format(Locale.US, "%T" + pattern, c)); } + + // http://b/33245708: Some locales have a group separator != '\0' but a default decimal format + // pattern without grouping (e.g. a group size of zero). This would throw divide by zero when + // working out where to place the separator. + public void testGroupingSizeZero() { + Locale localeWithoutGrouping = new Locale("en", "US", "POSIX"); + DecimalFormat decimalFormat = + (DecimalFormat) NumberFormat.getInstance(localeWithoutGrouping); + + // Confirm the locale is still a good example: it has a group separator, but no grouping in + // the default decimal format. + assertEquals(0, decimalFormat.getGroupingSize()); + assertFalse(decimalFormat.isGroupingUsed()); + DecimalFormatSymbols symbols = decimalFormat.getDecimalFormatSymbols(); + assertTrue(symbols.getGroupingSeparator() != '\0'); + + Formatter formatter = new Formatter(localeWithoutGrouping); + formatter.format("%,d", 123456789); + // No exception expected + } } diff --git a/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java b/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java index 3fa4dabd1..3345ed527 100644 --- a/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java +++ b/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java @@ -16,6 +16,10 @@ package libcore.java.util; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -283,6 +287,43 @@ public void test_getWeeksInWeekYear() { assertEquals(52, cal.getWeeksInWeekYear()); } + public void test_fromZonedDateTime() { + ZonedDateTime zdt = ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]"); + GregorianCalendar calendar = GregorianCalendar.from(zdt); + TimeZone timeZone = TimeZone.getTimeZone("Europe/Paris"); + assertEquals(timeZone, calendar.getTimeZone()); + assertEquals(2007, calendar.get(Calendar.YEAR)); + assertEquals(Calendar.DECEMBER, calendar.get(Calendar.MONTH)); + assertEquals(3, calendar.get(Calendar.DAY_OF_MONTH)); + assertEquals(10, calendar.get(Calendar.HOUR_OF_DAY)); + assertEquals(15, calendar.get(Calendar.MINUTE)); + assertEquals(3600 * 1000, calendar.getTimeZone().getRawOffset()); // in milliseconds + } + + public void test_fromZonedDateTime_invalidValues() { + ZoneId gmt = ZoneId.of("GMT"); + ZonedDateTime[] invalidValues = { + ZonedDateTime.of(LocalDateTime.MAX, gmt), + ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.MAX_VALUE).plusMillis(1), gmt), + ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.MIN_VALUE).minusMillis(1), gmt), + ZonedDateTime.of(LocalDateTime.MAX, gmt) }; + for (ZonedDateTime invalidValue : invalidValues) { + try { + GregorianCalendar.from(invalidValue); + fail("GregorianCalendar.from() should have failed with " + invalidValue); + } catch (IllegalArgumentException expected) {} + } + } + + public void test_toZonedDateTime() { + TimeZone timeZone = TimeZone.getTimeZone("Europe/Paris"); + GregorianCalendar calendar = new GregorianCalendar(timeZone); + calendar.set(2007, Calendar.DECEMBER, 3, 10, 15, 30); + calendar.set(Calendar.MILLISECOND, 0); + ZonedDateTime zdt = calendar.toZonedDateTime(); + assertEquals(ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]"), zdt); + } + private long getDstLosAngeles2014(TimeZone timeZone) { GregorianCalendar cal = new GregorianCalendar(timeZone, Locale.ENGLISH); cal.set(Calendar.MILLISECOND, 0); diff --git a/luni/src/test/java/libcore/java/util/HashMapTest.java b/luni/src/test/java/libcore/java/util/HashMapTest.java index 12bf59bec..a5ee1f6dd 100644 --- a/luni/src/test/java/libcore/java/util/HashMapTest.java +++ b/luni/src/test/java/libcore/java/util/HashMapTest.java @@ -16,14 +16,25 @@ package libcore.java.util; +import org.mockito.InOrder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.Spliterator; +import java.util.TreeMap; public class HashMapTest extends junit.framework.TestCase { public void test_getOrDefault() { MapDefaultMethodTester.test_getOrDefault(new HashMap<>(), true /*acceptsNullKey*/, - true /*acceptsNullValue*/); + true /*acceptsNullValue*/, true /*getAcceptsAnyObject*/); } public void test_forEach() { @@ -70,6 +81,148 @@ public void test_merge() { .test_merge(new HashMap<>(), true /*acceptsNullKey*/); } + public void test_spliterator_keySet() { + Map m = new HashMap<>(); + m.put("a", 1); + m.put("b", 2); + m.put("c", 3); + m.put("d", 4); + m.put("e", 5); + m.put("f", 6); + m.put("g", 7); + m.put("h", 8); + m.put("i", 9); + m.put("j", 10); + ArrayList expectedKeys = new ArrayList<>( + Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")); + Set keys = m.keySet(); + SpliteratorTester.runBasicIterationTests(keys.spliterator(), expectedKeys); + SpliteratorTester.runBasicSplitTests(keys, expectedKeys); + SpliteratorTester.testSpliteratorNPE(keys.spliterator()); + SpliteratorTester.runSizedTests(keys.spliterator(), 10); + assertEquals(Spliterator.DISTINCT | Spliterator.SIZED, + keys.spliterator().characteristics()); + SpliteratorTester.assertSupportsTrySplit(keys); + } + + public void test_spliterator_values() { + Map m = new HashMap<>(); + m.put("a", 1); + m.put("b", 2); + m.put("c", 3); + m.put("d", 4); + m.put("e", 5); + m.put("f", 6); + m.put("g", 7); + m.put("h", 8); + m.put("i", 9); + m.put("j", 10); + ArrayList expectedValues = new ArrayList<>( + Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + ); + Collection values = m.values(); + SpliteratorTester.runBasicIterationTests(values.spliterator(), expectedValues); + SpliteratorTester.runBasicSplitTests(values, expectedValues); + SpliteratorTester.testSpliteratorNPE(values.spliterator()); + SpliteratorTester.runSizedTests(values, 10); + assertEquals(Spliterator.SIZED, values.spliterator().characteristics()); + SpliteratorTester.assertSupportsTrySplit(values); + } + + public void test_spliterator_entrySet() { + MapDefaultMethodTester.test_entrySet_spliterator_unordered(new HashMap<>()); + + Map m = new HashMap<>(Collections.singletonMap("key", 42)); + assertEquals(Spliterator.DISTINCT | Spliterator.SIZED, + m.entrySet().spliterator().characteristics()); + } + + /** + * Checks that {@code HashMap.entrySet().spliterator().trySplit()} + * estimates half of the parents' estimate (rounded down, which + * can be an underestimate) but is not itself SIZED. + * + * These assertions are still stronger than what the documentation + * guarantees since un-SIZED Spliterators' size estimates may be off by + * an arbitrary amount. + */ + public void test_entrySet_subsizeEstimates() { + Map m = new HashMap<>(); + assertNull(m.entrySet().spliterator().trySplit()); + // For the empty map, the estimates are exact + assertEquals(0, m.entrySet().spliterator().estimateSize()); + assertEquals(0, m.entrySet().spliterator().getExactSizeIfKnown()); + + m.put("key1", "value1"); + assertSubsizeEstimate(m.entrySet().spliterator(), 0); + m.put("key2", "value2"); + assertSubsizeEstimate(m.entrySet().spliterator(), 1); + m.put("key3", "value3"); + m.put("key4", "value4"); + m.put("key5", "value5"); + m.put("key6", "value6"); + m.put("key7", "value7"); + m.put("key8", "value8"); + assertSubsizeEstimate(m.entrySet().spliterator(), 4); + + m.put("key9", "value9"); + assertSubsizeEstimate(m.entrySet().spliterator(), 4); + assertFalse(m.entrySet().spliterator().trySplit().hasCharacteristics(Spliterator.SIZED)); + } + + /** + * Checks that HashMap.entrySet()'s spliterator halfs its estimate (rounding down) + * for each split, even though this estimate may be inaccurate. + */ + public void test_entrySet_subsizeEstimates_recursive() { + Map m = new HashMap<>(); + for (int i = 0; i < 100; i++) { + m.put(i, "value"); + } + Set> entries = m.entrySet(); + // Recursive splitting - HashMap will estimate the size halving each split, rounding down. + assertSubsizeEstimate(entries.spliterator(), 50); + assertSubsizeEstimate(entries.spliterator().trySplit(), 25); + assertSubsizeEstimate(entries.spliterator().trySplit().trySplit(), 12); + assertSubsizeEstimate(entries.spliterator().trySplit().trySplit().trySplit(), 6); + assertSubsizeEstimate(entries.spliterator().trySplit().trySplit().trySplit().trySplit(), 3); + assertSubsizeEstimate( + entries.spliterator().trySplit().trySplit().trySplit().trySplit().trySplit(), 1); + assertSubsizeEstimate(entries.spliterator().trySplit().trySplit().trySplit().trySplit() + .trySplit().trySplit(), 0); + } + + /** + * Checks that HashMap.EntryIterator is SIZED but not SUBSIZED. + */ + public void test_entrySet_spliterator_sizedButNotSubsized() { + Map m = new HashMap<>(); + assertTrue(m.entrySet().spliterator().hasCharacteristics(Spliterator.SIZED)); + assertFalse(m.entrySet().spliterator().hasCharacteristics(Spliterator.SUBSIZED)); + m.put("key1", "value1"); + m.put("key2", "value2"); + assertTrue(m.entrySet().spliterator().hasCharacteristics(Spliterator.SIZED)); + assertFalse(m.entrySet().spliterator().hasCharacteristics(Spliterator.SUBSIZED)); + Spliterator> parent = m.entrySet().spliterator(); + Spliterator> child = parent.trySplit(); + assertFalse(parent.hasCharacteristics(Spliterator.SIZED)); + assertFalse(child.hasCharacteristics(Spliterator.SIZED)); + assertFalse(parent.hasCharacteristics(Spliterator.SUBSIZED)); + assertFalse(child.hasCharacteristics(Spliterator.SUBSIZED)); + } + + /** + * Tests that the given spliterator can be trySplit(), resulting in children that each + * estimate the specified size. + */ + private static void assertSubsizeEstimate(Spliterator spliterator, + long expectedEstimate) { + Spliterator child = spliterator.trySplit(); + assertNotNull(child); + assertEquals(expectedEstimate, spliterator.estimateSize()); + assertEquals(expectedEstimate, child.estimateSize()); + } + public void test_replaceAll() throws Exception { HashMap map = new HashMap<>(); map.put("one", "1"); @@ -83,12 +236,9 @@ public void test_replaceAll() throws Exception { assertEquals(3, map.size()); try { - map.replaceAll(new java.util.function.BiFunction() { - @Override - public String apply(String k, String v) { - map.put("foo1", v); - return v; - } + map.replaceAll((k, v) -> { + map.put("foo1", v); + return v; }); fail(); } catch(ConcurrentModificationException expected) {} diff --git a/luni/src/test/java/libcore/java/util/HashtableTest.java b/luni/src/test/java/libcore/java/util/HashtableTest.java index 40fbf25f3..b7a3202ce 100644 --- a/luni/src/test/java/libcore/java/util/HashtableTest.java +++ b/luni/src/test/java/libcore/java/util/HashtableTest.java @@ -17,14 +17,20 @@ package libcore.java.util; import java.util.ConcurrentModificationException; +import java.util.HashMap; import java.util.Hashtable; import java.util.Map; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.io.ByteArrayInputStream; +import java.lang.reflect.Field; public class HashtableTest extends junit.framework.TestCase { public void test_getOrDefault() { MapDefaultMethodTester.test_getOrDefault(new Hashtable<>(), false /*doesNotAcceptNullKey*/, - false /*doesNotAcceptNullValue*/); + false /*doesNotAcceptNullValue*/, true /*getAcceptsAnyObject*/); } public void test_forEach() { @@ -100,6 +106,59 @@ public String apply(String k, String v) { try { ht.replaceAll((k, v) -> null); + fail(); } catch (NullPointerException expected) {} } + + + /** + * Check that {@code Hashtable.Entry} compiles and refers to + * {@link java.util.Map.Entry}, which is required for source + * compatibility with earlier versions of Android. + */ + public void test_entryCompatibility_compiletime() { + assertEquals(Map.Entry.class, Hashtable.Entry.class); + } + + /** + * Checks that there is no nested class named 'Entry' in Hashtable. + * If {@link #test_entryCompatibility_compiletime()} passes but + * this test fails, then the test was probably compiled against a + * version of Hashtable that does not have a nested Entry class, + * but run against a version that does. + */ + public void test_entryCompatibility_runtime() { + String forbiddenClassName = "java.util.Hashtable$Entry"; + try { + Class.forName(forbiddenClassName); + fail("Class " + forbiddenClassName + " should not exist"); + } catch (ClassNotFoundException expected) { + } + } + + public void test_deserializedArrayLength() throws Exception { + final float loadFactor = 0.75f; + final int entriesCount = 100; + // Create table + Hashtable hashtable1 = new Hashtable<>(1, loadFactor); + for (int i = 0; i < entriesCount; i++) { + hashtable1.put(i, 1); + } + + // Serialize and deserialize + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(hashtable1); + } + Hashtable hashtable2 = + (Hashtable) new ObjectInputStream( + new ByteArrayInputStream(bos.toByteArray())).readObject(); + + // Check that table size is >= min expected size. Due to a bug in + // Hashtable deserialization this wasn't the case. + Field tableField = Hashtable.class.getDeclaredField("table"); + tableField.setAccessible(true); + Object[] table2 = (Object[]) tableField.get(hashtable2); + assertTrue(table2.length >= (entriesCount / loadFactor)); + } } diff --git a/luni/src/test/java/libcore/java/util/InvalidPropertiesFormatExceptionTest.java b/luni/src/test/java/libcore/java/util/InvalidPropertiesFormatExceptionTest.java new file mode 100644 index 000000000..694c06990 --- /dev/null +++ b/luni/src/test/java/libcore/java/util/InvalidPropertiesFormatExceptionTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2016 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. + * + */ + +package libcore.java.util; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.NotSerializableException; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.InvalidPropertiesFormatException; +import libcore.util.SerializationTester; + +public class InvalidPropertiesFormatExceptionTest extends TestCase { + + public void testConstructorArgs() { + InvalidPropertiesFormatException e = new InvalidPropertiesFormatException("testing"); + assertEquals("testing", e.getMessage()); + assertNull(e.getCause()); + + InvalidPropertiesFormatException e2 = new InvalidPropertiesFormatException(e); + assertSame(e, e2.getCause()); + assertEquals(e.toString(), e2.getMessage()); + } + + public void testDeserialize_notSupported() throws Exception { + // Result of + // SerializationTester.serializeHex(new InvalidPropertiesFormatException("testing")) + // using a InvalidPropertiesFormatException class that had its + // writeObject() method commented out. + String hex = "aced00057372002a6a6176612e7574696c2e496e76616c696450726f" + + "70657274696573466f726d6174457863657074696f6e6bbbea5ee5f9cb5" + + "b020000787200136a6176612e696f2e494f457863657074696f6e6c8073" + + "646525f0ab020000787200136a6176612e6c616e672e457863657074696" + + "f6ed0fd1f3e1a3b1cc4020000787200136a6176612e6c616e672e546872" + + "6f7761626c65d5c635273977b8cb0300044c000563617573657400154c6" + + "a6176612f6c616e672f5468726f7761626c653b4c000d64657461696c4d" + + "6573736167657400124c6a6176612f6c616e672f537472696e673b5b000" + + "a737461636b547261636574001e5b4c6a6176612f6c616e672f53746163" + + "6b5472616365456c656d656e743b4c00147375707072657373656445786" + + "3657074696f6e737400104c6a6176612f7574696c2f4c6973743b787071" + + "007e000874000774657374696e677572001e5b4c6a6176612e6c616e672" + + "e537461636b5472616365456c656d656e743b02462a3c3cfd2239020000" + + "78700000000a7372001b6a6176612e6c616e672e537461636b547261636" + + "5456c656d656e746109c59a2636dd8502000449000a6c696e654e756d62" + + "65724c000e6465636c6172696e67436c61737371007e00054c000866696" + + "c654e616d6571007e00054c000a6d6574686f644e616d6571007e000578" + + "70000000457400366c6962636f72652e6a6176612e7574696c2e496e766" + + "16c696450726f70657274696573466f726d6174457863657074696f6e54" + + "657374740029496e76616c696450726f70657274696573466f726d61744" + + "57863657074696f6e546573742e6a61766174001a746573745365726961" + + "6c697a655f6e6f74537570706f727465647371007e000cfffffffe74001" + + "86a6176612e6c616e672e7265666c6563742e4d6574686f6474000b4d65" + + "74686f642e6a617661740006696e766f6b657371007e000c000000c2740" + + "028766f6761722e7461726765742e6a756e69742e4a756e69743324566f" + + "6761724a556e69745465737474000b4a756e6974332e6a6176617400037" + + "2756e7371007e000c0000003b740024766f6761722e7461726765742e6a" + + "756e69742e566f6761725465737452756e6e65722431740014566f67617" + + "25465737452756e6e65722e6a6176617400086576616c75617465737100" + + "7e000c0000004874002b766f6761722e7461726765742e6a756e69742e5" + + "4696d656f7574416e6441626f727452756e52756c65243274001b54696d" + + "656f7574416e6441626f727452756e52756c652e6a61766174000463616" + + "c6c7371007e000c0000004474002b766f6761722e7461726765742e6a75" + + "6e69742e54696d656f7574416e6441626f727452756e52756c652432740" + + "01b54696d656f7574416e6441626f727452756e52756c652e6a61766174" + + "000463616c6c7371007e000c000000ed74001f6a6176612e7574696c2e6" + + "36f6e63757272656e742e4675747572655461736b74000f467574757265" + + "5461736b2e6a61766174000372756e7371007e000c0000046d7400276a6" + + "176612e7574696c2e636f6e63757272656e742e546872656164506f6f6c" + + "4578656375746f72740017546872656164506f6f6c4578656375746f722" + + "e6a61766174000972756e576f726b65727371007e000c0000025f74002e" + + "6a6176612e7574696c2e636f6e63757272656e742e546872656164506f6" + + "f6c4578656375746f7224576f726b6572740017546872656164506f6f6c" + + "4578656375746f722e6a61766174000372756e7371007e000c000002f87" + + "400106a6176612e6c616e672e54687265616474000b5468726561642e6a" + + "61766174000372756e7372001f6a6176612e7574696c2e436f6c6c65637" + + "4696f6e7324456d7074794c6973747ab817b43ca79ede020000787078"; + try { + Object obj = SerializationTester.deserializeHex(hex); + fail("Deserialized to " + obj); + } catch (NotSerializableException expected) { + // Sanity check that this is the right exception that we expected. + assertEquals("Not serializable.", expected.getMessage()); + } + } + + public void testSerialize_notSupported() throws Exception { + Serializable notActuallySerializable = new InvalidPropertiesFormatException("testing"); + try { + try (ObjectOutputStream out = new ObjectOutputStream(new ByteArrayOutputStream())) { + out.writeObject(notActuallySerializable); + } + fail(); + } catch (NotSerializableException expected) { + // Sanity check that this is the right exception that we expected. + assertEquals("Not serializable.", expected.getMessage()); + } + } +} diff --git a/luni/src/test/java/libcore/java/util/LibcoreIoDerivedBase64Test.java b/luni/src/test/java/libcore/java/util/LibcoreIoDerivedBase64Test.java new file mode 100644 index 000000000..2a888f76a --- /dev/null +++ b/luni/src/test/java/libcore/java/util/LibcoreIoDerivedBase64Test.java @@ -0,0 +1,312 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package libcore.java.util; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +import java.util.Base64; +import java.util.Base64.Decoder; +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +/** + * Additional tests for {@link java.util.Base64} derived from old tests for + * the removed class {@code libcore.io.Base64}. + */ +public final class LibcoreIoDerivedBase64Test extends TestCase { + + public void testEncodeDecode() throws Exception { + assertEncodeDecode(""); + assertEncodeDecode("Eg==", 0x12); + assertEncodeDecode("EjQ=", 0x12, 0x34); + assertEncodeDecode("EjRW", 0x12, 0x34, 0x56); + assertEncodeDecode("EjRWeA==", 0x12, 0x34, 0x56, 0x78); + assertEncodeDecode("EjRWeJo=", 0x12, 0x34, 0x56, 0x78, 0x9A); + assertEncodeDecode("EjRWeJq8", 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc); + } + + public void testEncode_doesNotWrap() throws Exception { + int[] data = new int[61]; + Arrays.fill(data, 0xff); + String expected = "///////////////////////////////////////////////////////////////////////" + + "//////////w=="; // 84 chars + assertEncodeDecode(expected, data); + } + + private static void assertEncodeDecode(String expectedEncoded, int... toEncode) + throws Exception { + // We should never expect (or receive) non-ASCII text from Base64.encoder. + asciiToBytes(expectedEncoded); + + // Convert the convenient ints to the bytes we need. + byte[] inputBytes = new byte[toEncode.length]; + for (int i = 0; i < toEncode.length; i++) { + inputBytes[i] = (byte) toEncode[i]; + } + String encoded = encode(inputBytes); + assertEquals(expectedEncoded, encoded); + + // Check we can round-trip the encoded bytes to + // arrive at what we started with. + int[] actualDecodedBytes = decodeToInts(encoded); + assertArrayEquals(toEncode, actualDecodedBytes); + } + + public void testDecode_empty() throws Exception { + byte[] decoded = decode(new byte[0]); + assertEquals(0, decoded.length); + } + + public void testDecode_truncated() throws Exception { + // Correct data, for reference. + assertEquals("hello, world", decodeToString("aGVsbG8sIHdvcmxk")); + + // The following are missing the final bytes + assertEquals("hello, worl", decodeToString("aGVsbG8sIHdvcmx")); + assertEquals("hello, wor", decodeToString("aGVsbG8sIHdvcm")); + assertEquals(null, decodeToString("aGVsbG8sIHdvc")); + assertEquals("hello, wo", decodeToString("aGVsbG8sIHdv")); + } + + public void testDecode_extraChars() throws Exception { + // Characters outside of alphabet before padding. + assertEquals(null, decodeToString(" aGVsbG8sIHdvcmxk")); + assertEquals(null, decodeToString("aGV sbG8sIHdvcmxk")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk ")); + assertEquals(null, decodeToString("*aGVsbG8sIHdvcmxk")); + assertEquals(null, decodeToString("aGV*sbG8sIHdvcmxk")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk*")); + assertEquals(null, decodeToString("\r\naGVsbG8sIHdvcmxk")); + assertEquals(null, decodeToString("aGV\r\nsbG8sIHdvcmxk")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk\r\n")); + assertEquals(null, decodeToString("\naGVsbG8sIHdvcmxk")); + assertEquals(null, decodeToString("aGV\nsbG8sIHdvcmxk")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk\n")); + + // padding 0 + assertEquals("hello, world", decodeToString("aGVsbG8sIHdvcmxk")); + // Extra padding + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk=")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk==")); + // Characters outside alphabet intermixed with (too much) padding. + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk =")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxk = = ")); + + // padding 1 + assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE=")); + // Missing padding + assertEquals("hello, world?!", decodeToString("aGVsbG8sIHdvcmxkPyE")); + // Characters outside alphabet before padding. + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE =")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE*=")); + // Trailing characters, otherwise valid. + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE= ")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=*")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=X")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=XY")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=XYZ")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=XYZA")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=\n")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE=\r\n")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE= ")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE==")); + // Whitespace characters outside alphabet intermixed with (too much) padding. + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE ==")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkPyE = = ")); + + // padding 2 + assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg==")); + // Missing padding + assertEquals("hello, world.", decodeToString("aGVsbG8sIHdvcmxkLg")); + // Partially missing padding + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg=")); + // Characters outside alphabet before padding. + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg ==")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg*==")); + // Trailing characters, otherwise valid. + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg== ")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==*")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==X")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==XY")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==XYZ")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==XYZA")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==\n")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg==\r\n")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg== ")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg===")); + // Characters outside alphabet inside padding. + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg= =")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg=*=")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg=\r\n=")); + // Characters inside alphabet inside padding. + assertEquals(null, decodeToString("aGVsbG8sIHdvcmxkLg=X=")); + + // Table 1 chars + assertEquals(null, decodeToString("_aGVsbG8sIHdvcmx")); + assertEquals(null, decodeToString("aGV_sbG8sIHdvcmx")); + assertEquals(null, decodeToString("aGVsbG8sIHdvcmx_")); + + // Table 2 chars. + assertArrayEquals( + new int[] {0xfd, 0xa1, 0x95, 0xb1, 0xb1, 0xbc, 0xb0, 0x81, 0xdd, 0xbd, 0xc9, + 0xb1 }, + decodeToInts("/aGVsbG8sIHdvcmx")); + assertArrayEquals( + new int[] { 0x68, 0x65, 0x7f, 0xb1, 0xb1, 0xbc, 0xb0, 0x81, 0xdd, 0xbd, 0xc9, + 0xb1 }, + decodeToInts("aGV/sbG8sIHdvcmx")); + assertArrayEquals( + new int[] { 104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 0x7f }, + decodeToInts("aGVsbG8sIHdvcmx/")); + } + + private static final int[] BYTE_VALUES = { + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77 + }; + + public void testDecode_nonAsciiBytes() throws Exception { + assertSubArrayEquals(BYTE_VALUES, 0, decodeToInts("")); + assertSubArrayEquals(BYTE_VALUES, 1, decodeToInts("/w==")); + assertSubArrayEquals(BYTE_VALUES, 2, decodeToInts("/+4=")); + assertSubArrayEquals(BYTE_VALUES, 3, decodeToInts("/+7d")); + assertSubArrayEquals(BYTE_VALUES, 4, decodeToInts("/+7dzA==")); + assertSubArrayEquals(BYTE_VALUES, 5, decodeToInts("/+7dzLs=")); + assertSubArrayEquals(BYTE_VALUES, 6, decodeToInts("/+7dzLuq")); + assertSubArrayEquals(BYTE_VALUES, 7, decodeToInts("/+7dzLuqmQ==")); + assertSubArrayEquals(BYTE_VALUES, 8, decodeToInts("/+7dzLuqmYg=")); + } + + public void testDecode_urlAlphabet() throws Exception { + assertNull(decodeToInts("_w==")); + assertNull(decodeToInts("-w==")); + } + + /** + * Convenience function for decoding from a Base64 ASCII String to an ASCII String. A String is + * used for the output to make the tests compact. Can return null if the decoder returns null. + * If any of the strings involved are non-ASCII an exception is thrown. + * Use {@link #decodeToInts(String)} for decode tests that produce bytes + * outside of the ASCII range. + */ + private static String decodeToString(String in) throws Exception { + byte[] bytes = asciiToBytes(in); + byte[] out = decode(bytes); + if (out == null) { + return null; + } + return bytesToAscii(out); + } + + private static String bytesToAscii(byte[] bytes) { + try { + CharsetDecoder decoder = StandardCharsets.US_ASCII.newDecoder(); + decoder.onMalformedInput(CodingErrorAction.REPORT); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + ByteBuffer bytesBuffer = ByteBuffer.wrap(bytes); + CharBuffer charsBuffer = decoder.decode(bytesBuffer); + char[] chars = new char[charsBuffer.remaining()]; + charsBuffer.get(chars, 0, chars.length); + return new String(chars); + } catch (CharacterCodingException e) { + // Use bytes in your test, not Strings. + throw new AssertionFailedError("Cannot convert test bytes to String safely: " + + Arrays.toString(bytesToInts(bytes)) + " contains non-ASCII codes"); + } + } + + private static byte[] asciiToBytes(String string) { + try { + char[] chars = string.toCharArray(); + + CharsetEncoder encoder = StandardCharsets.US_ASCII.newEncoder(); + encoder.onMalformedInput(CodingErrorAction.REPORT); + encoder.onUnmappableCharacter(CodingErrorAction.REPORT); + CharBuffer charsBuffer = CharBuffer.wrap(chars); + ByteBuffer bytesBuffer = encoder.encode(charsBuffer); + byte[] bytes = new byte[bytesBuffer.remaining()]; + bytesBuffer.get(bytes, 0, bytes.length); + return bytes; + } catch (CharacterCodingException e) { + // Use bytes in your test, not Strings. + throw new AssertionFailedError("Cannot convert test String to bytes safely: " + string + + " contains non-ASCII characters"); + } + } + + /** Decodes an ASCII string, returning an int array. */ + private static int[] decodeToInts(String in) throws Exception { + byte[] bytes = decode(asciiToBytes(in)); + return bytesToInts(bytes); + } + + private static byte[] decode(byte[] encoded) { + Decoder decoder = Base64.getDecoder(); + try { + return decoder.decode(encoded); + } catch (IllegalArgumentException e) { + return null; + } + } + + private static String encode(byte[] data) { + return Base64.getEncoder().encodeToString(data); + } + + /** + * Convert a byte[] to an int[]. int is used because it is more convenient to use ints in + * tests. + */ + private static int[] bytesToInts(byte[] bytes) { + if (bytes == null) { + return null; + } + int[] ints = new int[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + ints[i] = bytes[i] & 0xff; + } + return ints; + } + + private static void assertArrayEquals(int[] expected, int[] actual) { + assertSubArrayEquals(expected, expected.length, actual); + } + + /** Assert that actual equals the first len bytes of expected. */ + private static void assertSubArrayEquals(int[] expected, int len, int[] actual) { + // Convert the arrays to Strings for easy comparison / reporting. + String expectedString = intsToString(expected, len); + String actualString = intsToString(actual, actual.length); + assertEquals(expectedString, actualString); + } + + private static String intsToString(int[] toConvert, int length) { + String[] out = new String[length]; + for (int i = 0; i < length; i++) { + out[i] = "0x" + Integer.toHexString(toConvert[i]); + } + return Arrays.toString(out); + } +} + diff --git a/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java b/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java index fec95b16f..65a42abb7 100644 --- a/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java +++ b/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java @@ -16,9 +16,22 @@ package libcore.java.util; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.ConcurrentModificationException; +import java.util.HashMap; +import java.lang.Iterable; +import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Objects; +import java.util.Random; +import java.util.Set; +import java.util.Spliterator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -27,7 +40,7 @@ public class LinkedHashMapTest extends junit.framework.TestCase { public void test_getOrDefault() { MapDefaultMethodTester .test_getOrDefault(new LinkedHashMap<>(), true /*acceptsNullKey*/, - true /*acceptsNullValue*/); + true /*acceptsNullValue*/, true /*getAcceptsAnyObject*/); // Test for access order Map m = new LinkedHashMap(8, .75f, true); @@ -209,33 +222,46 @@ public void test_merge() { assertEquals("value3", newest.getValue()); } - // http://b/27929722 - // This tests the behaviour is consistent with earlier Android releases. - // This behaviour is NOT consistent with the RI. Future Android releases - // might change this. + // This tests the behaviour is consistent with the RI. + // This behaviour is NOT consistent with earlier Android releases up to + // and including Android N, see http://b/27929722 public void test_removeEldestEntry() { final AtomicBoolean removeEldestEntryReturnValue = new AtomicBoolean(false); final AtomicInteger removeEldestEntryCallCount = new AtomicInteger(0); LinkedHashMap m = new LinkedHashMap() { @Override protected boolean removeEldestEntry(Entry eldest) { + int size = size(); + assertEquals(size, iterableSize(entrySet())); + assertEquals(size, iterableSize(keySet())); + assertEquals(size, iterableSize(values())); + assertEquals(size, removeEldestEntryCallCount.get() + 1); removeEldestEntryCallCount.incrementAndGet(); return removeEldestEntryReturnValue.get(); } }; - m.put("foo", "bar"); assertEquals(0, removeEldestEntryCallCount.get()); - m.put("baz", "quux"); + m.put("foo", "bar"); assertEquals(1, removeEldestEntryCallCount.get()); + m.put("baz", "quux"); + assertEquals(2, removeEldestEntryCallCount.get()); removeEldestEntryReturnValue.set(true); m.put("foob", "faab"); - assertEquals(2, removeEldestEntryCallCount.get()); + assertEquals(3, removeEldestEntryCallCount.get()); assertEquals(2, m.size()); assertFalse(m.containsKey("foo")); } + private static int iterableSize(Iterable iterable) { + int result = 0; + for (E element : iterable) { + result++; + } + return result; + } + public void test_replaceAll() { LinkedHashMap map = new LinkedHashMap<>(); map.put("one", "1"); @@ -249,12 +275,9 @@ public void test_replaceAll() { assertEquals(3, map.size()); try { - map.replaceAll(new java.util.function.BiFunction() { - @Override - public String apply(String k, String v) { - map.put("foo1", v); - return v; - } + map.replaceAll((k, v) -> { + map.put("foo1", v); + return v; }); fail(); } catch(ConcurrentModificationException expected) {} @@ -264,4 +287,160 @@ public String apply(String k, String v) { fail(); } catch(NullPointerException expected) {} } + + public void test_eldest_empty() { + LinkedHashMap emptyMap = createMap(); + assertNull(eldest(emptyMap)); + } + + public void test_eldest_nonempty() { + assertEntry("key", "value", eldest(createMap("key", "value"))); + assertEntry("A", "1", eldest(createMap("A", "1", "B", "2", "C", "3"))); + assertEntry("A", "4", eldest(createMap("A", "1", "B", "2", "C", "3", "A", "4"))); + assertEntry("A", "4", eldest(createMap("A", "1", "B", "2", "C", "3", "A", "4", "D", "5"))); + } + + public void test_eldest_compatibleWithIterationOrder() { + check_eldest_comparibleWithIterationOrder(createMap()); + check_eldest_comparibleWithIterationOrder(createMap("key", "value")); + check_eldest_comparibleWithIterationOrder(createMap("A", "1", "B", "2")); + check_eldest_comparibleWithIterationOrder(createMap("A", "1", "B", "2", "A", "3")); + check_eldest_comparibleWithIterationOrder(createMap("A", "1", "A", "2", "A", "3")); + + Random random = new Random(31337); // arbitrary + LinkedHashMap m = new LinkedHashMap<>(); + for (int i = 0; i < 8000; i++) { + m.put(String.valueOf(random.nextInt(4000)), String.valueOf(random.nextDouble())); + } + check_eldest_comparibleWithIterationOrder(m); + } + + private void check_eldest_comparibleWithIterationOrder(LinkedHashMap map) { + Iterator> it = map.entrySet().iterator(); + if (it.hasNext()) { + Map.Entry expected = it.next(); + Object expectedKey = expected.getKey(); + Object expectedValue = expected.getValue(); + assertEntry(expectedKey, expectedValue, eldest(map)); + } else { + assertNull(eldest(map)); + } + } + + /** + * Check that {@code LinkedHashMap.Entry} compiles and refers to + * {@link java.util.Map.Entry}, which is required for source + * compatibility with earlier versions of Android. + */ + public void test_entryCompatibility_compiletime() { + assertEquals(Map.Entry.class, LinkedHashMap.Entry.class); + } + + /** + * Checks that there is no nested class named 'Entry' in LinkedHashMap. + * If {@link #test_entryCompatibility_compiletime()} passes but + * this test fails, then the test was probably compiled against a + * version of LinkedHashMap that does not have a nested Entry class, + * but run against a version that does. + */ + public void test_entryCompatibility_runtime() { + String forbiddenClassName = "java.util.LinkedHashMap$Entry"; + try { + Class.forName(forbiddenClassName); + fail("Class " + forbiddenClassName + " should not exist"); + } catch (ClassNotFoundException expected) { + } + } + + public void test_spliterator_keySet() { + Map m = new LinkedHashMap<>(); + m.put("a", 1); + m.put("b", 2); + m.put("c", 3); + m.put("d", 4); + m.put("e", 5); + m.put("f", 6); + m.put("g", 7); + m.put("h", 8); + m.put("i", 9); + m.put("j", 10); + ArrayList expectedKeys = new ArrayList<>( + Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")); + Set keys = m.keySet(); + SpliteratorTester.runBasicIterationTests(keys.spliterator(), expectedKeys); + SpliteratorTester.runBasicSplitTests(keys, expectedKeys); + SpliteratorTester.testSpliteratorNPE(keys.spliterator()); + SpliteratorTester.runOrderedTests(keys); + SpliteratorTester.runSizedTests(keys.spliterator(), 10); + SpliteratorTester.runSubSizedTests(keys.spliterator(), 10); + assertEquals( + Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED + | Spliterator.SUBSIZED, + keys.spliterator().characteristics()); + SpliteratorTester.assertSupportsTrySplit(keys); + } + + public void test_spliterator_values() { + Map m = new LinkedHashMap<>(); + m.put("a", 1); + m.put("b", 2); + m.put("c", 3); + m.put("d", 4); + m.put("e", 5); + m.put("f", 6); + m.put("g", 7); + m.put("h", 8); + m.put("i", 9); + m.put("j", 10); + ArrayList expectedValues = new ArrayList<>( + Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + ); + Collection values = m.values(); + SpliteratorTester.runBasicIterationTests( + values.spliterator(), expectedValues); + SpliteratorTester.runBasicSplitTests(values, expectedValues); + SpliteratorTester.testSpliteratorNPE(values.spliterator()); + SpliteratorTester.runOrderedTests(values); + SpliteratorTester.runSizedTests(values, 10); + SpliteratorTester.runSubSizedTests(values, 10); + assertEquals(Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED, + values.spliterator().characteristics()); + SpliteratorTester.assertSupportsTrySplit(values); + } + + public void test_spliterator_entrySet() { + MapDefaultMethodTester + .test_entrySet_spliterator_unordered(new LinkedHashMap<>()); + + Map m = new LinkedHashMap<>(Collections.singletonMap("key", 23)); + assertEquals( + Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED | + Spliterator.SUBSIZED, + m.entrySet().spliterator().characteristics()); + } + + private static Map.Entry eldest(LinkedHashMap map) { + // Should be the same as: return (map.isEmpty()) ? null : map.entrySet().iterator().next(); + return map.eldest(); + } + + private static void assertEntry(Object key, Object value, Map.Entry entry) { + String msg = String.format(Locale.US, "Expected (%s, %s), got (%s, %s)", + key, value, entry.getKey(), entry.getValue()); + boolean equal = Objects.equals(key, entry.getKey()) + && Objects.equals(value, entry.getValue()); + if (!equal) { + fail(msg); + } + } + + private static LinkedHashMap createMap(T... keysAndValues) { + assertEquals(0, keysAndValues.length % 2); + LinkedHashMap result = new LinkedHashMap<>(); + for (int i = 0; i < keysAndValues.length; i += 2) { + result.put(keysAndValues[i], keysAndValues[i+1]); + } + return result; + } + } diff --git a/luni/src/test/java/libcore/java/util/LocaleLanguageRangeTest.java b/luni/src/test/java/libcore/java/util/LocaleLanguageRangeTest.java new file mode 100644 index 000000000..e99478816 --- /dev/null +++ b/luni/src/test/java/libcore/java/util/LocaleLanguageRangeTest.java @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2016 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. + */ + +package libcore.java.util; + +import static java.util.Locale.LanguageRange.MAX_WEIGHT; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale.LanguageRange; +import java.util.Map; +import java.util.stream.Collectors; + +import junit.framework.TestCase; + +/** + * Tests {@link LanguageRange}. + */ +public class LocaleLanguageRangeTest extends TestCase { + + /** + * Checks that the constants for min/max weight don't accidentally change. + */ + public void testWeight_constantValues() { + assertEquals(0.0, LanguageRange.MIN_WEIGHT); + assertEquals(1.0, MAX_WEIGHT); + } + + public void testConstructor_defaultsToMaxWeight() { + assertEquals(MAX_WEIGHT, new LanguageRange("de-DE").getWeight()); + } + + public void testConstructor_invalidWeight() { + try { + new LanguageRange("de-DE", -0.00000001); + fail(); + } catch (IllegalArgumentException expected) { + + } + try { + new LanguageRange("de-DE", 1.00000001); + fail(); + } catch (IllegalArgumentException expected) { + } + // These work: + new LanguageRange("de-DE", 0); + new LanguageRange("de-DE", 1); + } + + public void testConstructor_nullRange() { + try { + new LanguageRange(null); + fail(); + } catch (NullPointerException expected) { + } + try { + new LanguageRange(null, MAX_WEIGHT); + fail(); + } catch (NullPointerException expected) { + } + new LanguageRange("de-DE", MAX_WEIGHT); // works + } + + public void testConstructor_checksForAtLeastOneSubtag() { + assertRangeMalformed(""); + // The fact that ArrayIndexOutOfBoundsException instead of + // IllegalArgumentException is thrown here is somewhat + // inconsistent; the checks below ensure that we're aware + // if we change the behavior in future. + try { + new LanguageRange("-"); + fail(); + } catch (ArrayIndexOutOfBoundsException expected) { + } + try { + new LanguageRange("--"); + fail(); + } catch (ArrayIndexOutOfBoundsException expected) { + } + } + + public void testConstructor_checksForWellFormedSubtags() { + // first subtag must not have digits + assertRangeMalformed("012-xx"); + assertRangeMalformed("b0b-xx"); + new LanguageRange("bob-xx"); // okay + new LanguageRange("bob-01"); // okay + + // subtags must be <= 8 characters + assertRangeMalformed("de-abcdefghi-xx"); + new LanguageRange("de-abcdefgh-xx"); // okay + + // "-" only between subtags and only one in a row + assertRangeMalformed("-de"); + assertRangeMalformed("de-"); + assertRangeMalformed("de--DE"); + new LanguageRange("de-DE"); // okay + new LanguageRange("de"); // okay + } + + public void testConstructor_acceptsWildcardSubtags() { + new LanguageRange("de-*"); + new LanguageRange("*-DE"); + new LanguageRange("de-*-DE"); + new LanguageRange("*"); + } + + public void testEqualsAndHashCode() { + checkEqual(new LanguageRange("en-US"), new LanguageRange("en-US")); + checkNotEqual(new LanguageRange("en-US"), new LanguageRange("en-AU")); + + checkEqual(new LanguageRange("en-US"), + new LanguageRange("en-US", LanguageRange.MAX_WEIGHT)); + checkNotEqual(new LanguageRange("en-US"), new LanguageRange("en-US", 0.4)); + + checkEqual(new LanguageRange("en-US", 0.3), new LanguageRange("en-US", 0.3)); + checkNotEqual(new LanguageRange("en-US", 0.3), new LanguageRange("en-US", 0.4)); + checkNotEqual(new LanguageRange("ja-JP", 0.5), new LanguageRange("de-DE", 0.5)); + } + + private static void checkEqual(T a, T b) { + assertEquals(a, b); + assertEquals(b, a); + assertEquals(a.hashCode(), b.hashCode()); + } + + private static void checkNotEqual(T a, T b) { + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + assertTrue(a.hashCode() != b.hashCode()); + } + + public void testGetRange() { + assertEquals("de-de", new LanguageRange("de-DE", 0.12345).getRange()); + } + + public void testGetWeight() { + assertEquals(0.12345, new LanguageRange("de-DE", 0.12345).getWeight()); + } + + public void testMapEquivalents_emptyList() { + List noRange = Collections.emptyList(); + assertEquals(noRange, LanguageRange.mapEquivalents(noRange, Collections.emptyMap())); + assertEquals(noRange, LanguageRange.mapEquivalents(noRange, + Collections.singletonMap("en-US", Arrays.asList("en-US", "en-AU", "en-UK")))); + } + + public void testMapEquivalents_emptyMap_createsModifiableCopy() { + List inputRanges = Collections.unmodifiableList(Arrays.asList( + new LanguageRange("de-DE"), + new LanguageRange("ja-JP"))); + List outputRanges = + LanguageRange.mapEquivalents(inputRanges, Collections.emptyMap()); + assertEquals(inputRanges, outputRanges); + assertNotSame(inputRanges, outputRanges); + // result is modifiable + outputRanges.add(new LanguageRange("fr-FR")); + outputRanges.clear(); + } + + /** + * Tests the example from the {@link LanguageRange#mapEquivalents(List, Map)} documentation. + */ + public void testMapEquivalents_exampleFromDocumentation() { + Map> map = new HashMap<>(); + map.put("zh", Collections.unmodifiableList(Arrays.asList("zh", "zh-Hans"))); + map.put("zh-HK", Collections.singletonList("zh-HK")); + map.put("zh-TW", Collections.singletonList("zh-TW")); + + List inputPriorityList = Arrays.asList( + new LanguageRange("zh"), + new LanguageRange("zh-CN"), + new LanguageRange("en"), + new LanguageRange("zh-TW"), + new LanguageRange("zh-TW") + ); + List expectedOutput = Arrays.asList( + new LanguageRange("zh"), + new LanguageRange("zh-Hans"), + new LanguageRange("zh-CN"), + new LanguageRange("zh-Hans-CN"), + new LanguageRange("en"), + new LanguageRange("zh-TW"), + new LanguageRange("zh-TW") + ); + List outputProrityList = LanguageRange + .mapEquivalents(inputPriorityList, map); + assertEquals(expectedOutput, outputProrityList); + } + + public void testMapEquivalents_nullList() { + try { + LanguageRange.mapEquivalents(null, Collections.emptyMap()); + fail(); + } catch (NullPointerException expected) { + } + } + + /** + * The documentation doesn't specify whether {@code mapEquivalents()} accepts a + * null map, but the current behavior is the same as for an empty map. This test + * ensures that we're aware if this behavior changse. + */ + public void testMapEquivalents_nullMap() { + List priorityList = Collections.unmodifiableList(Arrays.asList( + new LanguageRange("de-DE"), + new LanguageRange("en-UK"), + new LanguageRange("zh-CN"))); + assertEquals(priorityList, LanguageRange.mapEquivalents(priorityList, null)); + } + + /** Tests {@link LanguageRange#parse(String, Map)}. */ + public void testMapEquivalents() { + List expected = Arrays.asList( + new LanguageRange("de-de", 1.0), + new LanguageRange("en-us", 0.7), + new LanguageRange("en-au", 0.7) + ); + Map> map = new HashMap<>(); + map.put("fr", Arrays.asList("de-DE")); + map.put("en", Arrays.asList("en-US", "en-AU")); + String ranges = "Accept-Language: fr,en;q=0.7"; + assertEquals(expected, LanguageRange.parse(ranges, map)); + // Per the documentation, this should be equivalent + assertEquals(expected, LanguageRange.mapEquivalents(LanguageRange.parse(ranges), map)); + } + + /** + * Because {@code mapEquivalents(ranges, map)} behaves identically + * to {@code mapEquivalents(parse(ranges), map}, any equivalent + * locales from {@link sun.util.locale.LocaleEquivalentMaps}, + * such as {@code "iw" -> "he"}, are expanded before the mapping + * from {@code map} is applied. + */ + public void testParse_map_localeEquivalent() { + Map> map = new HashMap<>(); + map.put("iw", Arrays.asList("de-DE")); + map.put("en", Arrays.asList("en-US", "en-AU")); + + List expectedOutput = Arrays.asList( + new LanguageRange("de-de", 1.0), // iw -> de-de (map) + new LanguageRange("he", 1.0), // iw -> he (LocaleEquivalentMaps) + new LanguageRange("en-us", 0.7), // en -> en-us (map) + new LanguageRange("en-au", 0.7)); // en -> en-au (map) + + String ranges = "Accept-Language: iw,en;q=0.7"; + assertEquals(expectedOutput, LanguageRange.parse(ranges, map)); + // Per the documentation, this should be equivalent + assertEquals(expectedOutput, + LanguageRange.mapEquivalents(LanguageRange.parse(ranges), map)); + } + + /** + * Tests the example from the {@link LanguageRange#parse(String)} documentation. + */ + public void testParse_acceptLanguage_exampleFromDocumentation() { + List expected = Arrays.asList( + new LanguageRange("iw", 1.0), + new LanguageRange("he", 1.0), + new LanguageRange("en-us", 0.7), + new LanguageRange("en", 0.3) + ); + assertEquals(expected, LanguageRange.parse("Accept-Language: iw,en-us;q=0.7,en;q=0.3")); + } + + /** + * Tests parsing the example from RFC 2616 section 14.4. + */ + public void testParse_acceptLanguage_exampleFromRfc2616() { + List expected = Arrays.asList( + new LanguageRange("da", 1.0), + new LanguageRange("en-gb", 0.8), + new LanguageRange("en", 0.7) + ); + assertEquals(expected, LanguageRange.parse("Accept-Language: da, en-gb;q=0.8, en;q=0.7")); + } + + public void testParse_acceptLanguage_malformed() { + try { + LanguageRange.parse("Accept-Language: fr,en-us;q=1;q=0.5"); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + LanguageRange.parse("Accept-Language: q=0.5"); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + LanguageRange.parse("Accept-Language: ;q=0.5"); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + LanguageRange.parse("Accept-Language: thislanguagetagistoolong;q=0.5"); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + /** + * The current implementation doesn't require a ' ' after the "Accept-Language:". + * This test ensures that we're aware if this behavior changes. + */ + public void testParse_acceptLanguage_missingSpaceAfterColon() { + List languageRanges = Arrays.asList( + new LanguageRange("fr"), + new LanguageRange("en-us", 1) + ); + assertEquals(languageRanges, LanguageRange.parse("Accept-Language:fr,en-us;q=1")); + } + + public void testParse_acceptLanguage_wildCards() { + List expected = Arrays.asList( + new LanguageRange("da", 1.0), + new LanguageRange("en-*", 0.8), + new LanguageRange("*", 0.7) + ); + assertEquals(expected, LanguageRange.parse("Accept-Language: da, en-*;q=0.8, *;q=0.7")); + } + + public void testParse_acceptLanguage_weightValid() { + LanguageRange fr = new LanguageRange("fr"); + assertEquals(Arrays.asList(fr, new LanguageRange("en-us", 1.0)), + LanguageRange.parse("Accept-Language: fr,en-us;q=1")); + assertEquals(Arrays.asList(fr, new LanguageRange("en-us", 0.1)), + LanguageRange.parse("Accept-Language: fr,en-us;q=.1")); + assertEquals(Arrays.asList(fr, new LanguageRange("en-us", 0.12345678901234567890)), + LanguageRange.parse("Accept-Language: fr,en-us;q=0.12345678901234567890")); + assertEquals(Arrays.asList(fr, new LanguageRange("en-us", 0)), + LanguageRange.parse("Accept-Language: fr,en-us;q=0")); + } + + public void testParse_acceptLanguage_weightInvalid() { + try { + LanguageRange.parse("Accept-Language: iw,en-us;q=1.1"); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + LanguageRange.parse("Accept-Language: iw,en-us;q=-0.1"); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + // Based on a test case that was contributed back to upstream maintainers through + // https://bugs.openjdk.java.net/browse/JDK-8166994 + public void testParse_multiEquivalent_consistency() { + List parsed = rangesToStrings(LanguageRange.parse("ccq-xx")); + + assertEquals(parsed, rangesToStrings(LanguageRange.parse("ccq-xx"))); // consistency + assertEquals(Arrays.asList("ccq-xx", "ybd-xx", "rki-xx"), parsed); // expected result + } + + /** + * Tests parsing a Locale range matching an entry from + * {@link sun.util.locale.LocaleEquivalentMaps#singleEquivMap}. + */ + public void testParse_singleEquivalent() { + assertParseRanges("art-lojban", "jbo"); // example from RFC 4647 section 3.2 + assertParseRanges("yue", "zh-yue"); + assertParseRanges("yue-xx", "zh-yue-xx"); + } + + /** + * Tests parsing a Locale range matching an entry from + * {@link sun.util.locale.LocaleEquivalentMaps#multiEquivsMap}. + */ + public void testParse_multiEquivalent() { + assertParseRanges("mst", "myt", "mry"); + assertParseRanges("i-hak", "zh-hakka", "hak"); + } + + /** + * Tests parsing a Locale range matching an entry from + * {@link sun.util.locale.LocaleEquivalentMaps#regionVariantEquivMap}. + */ + public void testParse_regionEquivalent() { + // Region ("-de" or "-dd") matches the end + assertParseRanges("de-de", "de-dd"); + assertParseRanges("xx-dd", "xx-de"); + + // Region ("-de" or "-dd") matches the middle + assertParseRanges("xx-de-yy", "xx-dd-yy"); + assertParseRanges("xx-dd-yy", "xx-de-yy"); + + assertParseRanges("xx-bu", "xx-mm"); + assertParseRanges("xx-mm", "xx-bu"); + } + + /** + * Tests parsing a Locale range matching entries from both + * {@link sun.util.locale.LocaleEquivalentMaps#singleEquivMap} and + * {@link sun.util.locale.LocaleEquivalentMaps#regionVariantEquivMap}. + */ + public void testParse_singleAndRegionEquivalent() { + assertParseRanges("sgn-ch-de", "sgg", "sgn-ch-dd"); + assertParseRanges("sgn-ch-de-xx", "sgg-xx", "sgn-ch-dd-xx"); + } + + /** + * Asserts that {@code LanguageRange(ranges)} returns LanguageRanges whose + * {@link LanguageRange#getRange() Range string}s are {@code ranges} and + * {@code expectedAdditional}, in order. + */ + private static void assertParseRanges(String ranges, String... expectedAdditional) { + List expected = new ArrayList<>(); + expected.add(ranges); + expected.addAll(Arrays.asList(expectedAdditional)); + + List actual = rangesToStrings(LanguageRange.parse(ranges)); + + assertEquals(expected, actual); + } + + private static List rangesToStrings(List languageRanges) { + return languageRanges.stream().map(LanguageRange::getRange).collect(Collectors.toList()); + } + + private void assertRangeMalformed(String range) { + try { + new LanguageRange(range); + fail("Range should be recognized as malformed: " + range); + } catch (IllegalArgumentException expected) { + // Check for the exception that is thrown when a malformed subtag is detected. + // The exception message used here may change in future. + assertEquals("range=" + range.toLowerCase(), expected.getMessage()); + } + } + +} diff --git a/luni/src/test/java/libcore/java/util/LocaleTest.java b/luni/src/test/java/libcore/java/util/LocaleTest.java index f9146b3d3..969402b6a 100644 --- a/luni/src/test/java/libcore/java/util/LocaleTest.java +++ b/luni/src/test/java/libcore/java/util/LocaleTest.java @@ -16,6 +16,12 @@ package libcore.java.util; +import static java.util.Locale.FilteringMode.AUTOSELECT_FILTERING; +import static java.util.Locale.FilteringMode.EXTENDED_FILTERING; +import static java.util.Locale.FilteringMode.IGNORE_EXTENDED_RANGES; +import static java.util.Locale.FilteringMode.MAP_EXTENDED_RANGES; +import static java.util.Locale.FilteringMode.REJECT_EXTENDED_RANGES; + import java.io.ObjectInputStream; import java.text.BreakIterator; import java.text.Collator; @@ -23,12 +29,41 @@ import java.text.DateFormatSymbols; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; +import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.IllformedLocaleException; +import java.util.List; import java.util.Locale; +import java.util.Locale.LanguageRange; import java.util.MissingResourceException; public class LocaleTest extends junit.framework.TestCase { + + public void test_extension_absent() throws Exception { + Locale locale = Locale.forLanguageTag("en-US"); + assertFalse(locale.hasExtensions()); + assertEquals(locale, locale.stripExtensions()); + } + + public void test_extension_builder() throws Exception { + Locale.Builder b = new Locale.Builder(); + Locale localeWithoutExtension = b.build(); + b.setExtension('g', "FO_ba-BR_bg"); + Locale locale = b.build(); + assertTrue(locale.hasExtensions()); + assertFalse(locale.stripExtensions().hasExtensions()); + assertEquals(localeWithoutExtension, locale.stripExtensions()); + } + + public void test_extension_languageTag() throws Exception { + Locale lA = Locale.forLanguageTag("en-Latn-US-x-foo"); + Locale lB = Locale.forLanguageTag("en-Latn-US"); + assertTrue(lA.hasExtensions()); + assertFalse(lB.hasExtensions()); + assertEquals(lB, lA.stripExtensions()); + } + // http://b/2611311; if there's no display language/country/variant, use the raw codes. public void test_getDisplayName_invalid() throws Exception { Locale invalid = new Locale("AaBbCc", "DdEeFf", "GgHhIi"); @@ -101,8 +136,6 @@ public void test_getDisplayCountry_8870289() throws Exception { assertEquals("Palestine", new Locale("", "PS").getDisplayCountry(Locale.US)); assertEquals("Cocos (Keeling) Islands", new Locale("", "CC").getDisplayCountry(Locale.US)); - assertEquals("Congo (DRC)", new Locale("", "CD").getDisplayCountry(Locale.US)); - assertEquals("Congo (Republic)", new Locale("", "CG").getDisplayCountry(Locale.US)); assertEquals("Falkland Islands (Islas Malvinas)", new Locale("", "FK").getDisplayCountry(Locale.US)); assertEquals("Macedonia (FYROM)", new Locale("", "MK").getDisplayCountry(Locale.US)); assertEquals("Myanmar (Burma)", new Locale("", "MM").getDisplayCountry(Locale.US)); @@ -122,8 +155,8 @@ public void test_tl_and_fil() throws Exception { Locale tl_PH = new Locale("tl", "PH"); assertEquals("Tagalog", tl.getDisplayLanguage(Locale.ENGLISH)); assertEquals("Tagalog", tl_PH.getDisplayLanguage(Locale.ENGLISH)); - assertEquals("tl", tl.getDisplayLanguage(tl)); - assertEquals("tl", tl_PH.getDisplayLanguage(tl_PH)); + assertEquals("Tagalog", tl.getDisplayLanguage(tl)); + assertEquals("Tagalog", tl_PH.getDisplayLanguage(tl_PH)); Locale es_MX = new Locale("es", "MX"); assertEquals("tagalo", tl.getDisplayLanguage(es_MX)); @@ -752,6 +785,223 @@ private void test_setLanguageTag_withWellFormedExtensions(boolean useBuilder) { assertEquals("a-b-c-d-e-fo", l.getExtension('x')); } + /** + * Tests filtering locales using basic language ranges (without "*"). + */ + public void test_filter_basic() { + List tags = tagsOf( + "en-US", + "en-Latn-US", + "zh-Hant-TW", + "es-419", + "fr-FR", + "ja-JP" + ); + List ranges = new ArrayList<>(); + ranges.add(new LanguageRange("en-US")); + + // By default, basic filtering is used for basic language ranges + assertFilter(tagsOf("en-US"), ranges, tags); + + // Since no extended ranges are given, these should produce the same result + assertFilter(tagsOf("en-US"), ranges, tags, AUTOSELECT_FILTERING); + assertFilter(tagsOf("en-US"), ranges, tags, REJECT_EXTENDED_RANGES); + assertFilter(tagsOf("en-US"), ranges, tags, IGNORE_EXTENDED_RANGES); + + // EXTENDED_FILTERING can be enabled explicitly even when the priority + // list only contains basic; then, en-US also matches en-Latn-US. + assertFilter(tagsOf("en-US", "en-Latn-US"), ranges, tags, EXTENDED_FILTERING); + + ranges.add(new LanguageRange("zh-Hant-TW")); + assertFilter(tagsOf("en-US", "zh-Hant-TW"), ranges, tags); + } + + /** + * Tests that filtering is case insensitive. + */ + public void test_filter_caseInsensitive() { + List tags = tagsOf("de-DE", "de-Latn-DE", "ja-jp"); + + assertFilter(tagsOf("de-DE"), languageRangesOf("dE-De"), tags); + assertFilter(tagsOf("ja-jp"), languageRangesOf("ja-JP"), tags); + assertFilter(tagsOf("ja-jp"), languageRangesOf("JA-jp"), tags); + } + + /** + * Tests filtering locales using extended language ranges (with "*"), per + * the example from RFC 4647 section 3.3.2 + */ + public void test_filter_extended() { + List priorityList = languageRangesOf("de-DE", "de-*-DE"); + List tags = tagsOf( + "de", // not matched: missing 'DE' + "de-DE", // German, as used in Germany + "de-de", // German, as used in Germany + "de-Latn-DE", // Latin script + "de-Latf-DE", // Fraktur variant of Latin script + "de-DE-x-goethe", // private-use subtag + "de-Latn-DE-1996", + "de-Deva", // not matched: 'Deva' not equal to 'DE' + "de-Deva-DE", // Devanagari script + "de-x-DE" // not matched: singleton 'x' occurs before 'DE' + ); + + List filteredTags = tagsOf( + "de-DE", // German, as used in Germany + "de-Latn-DE", // Latin script + "de-Latf-DE", // Fraktur variant of Latin script + "de-DE-x-goethe", // private-use subtag + "de-Latn-DE-1996", + "de-Deva-DE" // Devanagari script + ); + + assertFilter(filteredTags, priorityList, tags, EXTENDED_FILTERING); + + // Because the priority list contains an extended language range, filtering + // should default to extended, so default filtering should yield the same results: + assertFilter(filteredTags, priorityList, tags); + assertFilter(filteredTags, priorityList, tags, AUTOSELECT_FILTERING); + + // Ignoring the extended range (de-*-DE) matches only a single language tag, "de-DE" + assertFilter(tagsOf("de-DE", "de-DE-x-goethe"), priorityList, tags, IGNORE_EXTENDED_RANGES); + } + + /** + * Tests that filtering with {@link Locale.FilteringMode#REJECT_EXTENDED_RANGES} + * throws IllegalArgumentException if passed an extended tag / language range. + */ + public void test_filter_extended_reject() { + try { + Locale.filter( + languageRangesOf("de-DE", "de-*-DE"), + localesOf("de-DE", "fr-FR"), + REJECT_EXTENDED_RANGES); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + Locale.filterTags( + languageRangesOf("de-DE", "de-*-DE"), + tagsOf("de-DE", "fr-FR"), + REJECT_EXTENDED_RANGES); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + /** + * Checks that a '*' occurring in a LanguageRange is interpreted in compliance + * with RFC 4647 section 3.2: if the first subtag is a '*' then the entire range + * is treated as "*", otherwise each wildcard subtag is removed. + */ + public void test_filter_extended_wildcardInLanguageRange() { + List tags = tagsOf("en-US", "de-DE", "en-AU", "en-Latn-US"); + // en-*-US is treated as "en-US", so only en-US matches + assertFilter(tagsOf("en-US"), languageRangesOf("en-*-US"), tags, MAP_EXTENDED_RANGES); + + // *-US is treated as "*", so all locales match + assertFilter(tags, languageRangesOf("*-US"), tags, MAP_EXTENDED_RANGES); + + // Same behavior with just "*" + assertFilter(tags, languageRangesOf("*"), tags, MAP_EXTENDED_RANGES); + } + + /** + * Tests that a '*' in a Locale in the priority list matches a subtag only + * when extended filtering is used; note that this is different from a + * '*' occuring in a LanguageRange, where it is ignored. + */ + public void test_filter_extended_wildcardInPriorityList() { + List tags = tagsOf("de-DE", "de-Latn-DE", "ja-JP"); + assertFilter(tagsOf("de-DE", "de-Latn-DE"), + languageRangesOf("dE-*-De"), tags); + assertFilter(tagsOf("de-DE", "de-Latn-DE"), + languageRangesOf("dE-De"), tags, EXTENDED_FILTERING); + } + + public void test_filter_noMatch() { + List noTag = Collections.emptyList(); + + List tags = tagsOf("en-US", "fr-Fr", "de-DE"); + + assertFilter(noTag, languageRangesOf("en-AU"), tags); + assertFilter(noTag, languageRangesOf("es-419"), tags); + assertFilter(noTag, languageRangesOf("zh-*-TW"), tags); + } + + /** + * Tests that various methods throw NullPointerException when given {@code null} + * as an argument. + */ + public void test_filter_nullArguments() { + List tags = tagsOf("de-DE", "de-Latn-DE"); + List locales = localesOf(tags); + List languageRanges = languageRangesOf("en-*-US", "de-DE"); + + assertThrowsNpe(() -> { Locale.filter(null, locales); }); + assertThrowsNpe(() -> { Locale.filter(languageRanges, null); }); + + assertThrowsNpe(() -> { Locale.filterTags(null, tags); }); + assertThrowsNpe(() -> { Locale.filterTags(languageRanges, null); }); + + // The documentation doesn't say whether FilteringMode is allowed to be + // null or what the sematnics of that null are; currently it is allowed. + // This test ensures that we are aware if we change this behavior in future. + List filteredLocales = Locale.filter(languageRanges, locales, null); + List filteredTags = Locale.filterTags(languageRanges, tags, null); + assertEquals(localesOf("de-DE"), filteredLocales); + assertEquals(tagsOf("de-DE"), filteredTags); + } + + /** + * Tests that filtered locales are returned in priority order. + */ + public void test_filter_priorityOrder() { + List priorityList = languageRangesOf("zh-Hant-TW", "en-US"); + + List tags = tagsOf( + "en-US", + "zh-Hant-TW", + "es-419", + "fr-FR" + ); + assertFilter(tagsOf("zh-Hant-TW", "en-US"), languageRangesOf("zh-Hant-TW", "en-US"), tags); + assertFilter(tagsOf("en-US", "zh-Hant-TW"), languageRangesOf("en-US", "zh-Hant-TW"), tags); + } + + /** + * Tests that the List returned by the various {@code filter} methods is modifiable, + * as specified by the documentation. + */ + public void test_filter_resultIsModifiable_locales() { + List priorityList = languageRangesOf("de-DE", "de-*-DE"); + List locales = localesOf("de-DE", "de-Latn-DE", "ja-JP"); + + Locale dummy = Locale.FRANCE; + // should not throw + Locale.filter(priorityList, locales).add(dummy); + Locale.filter(priorityList, locales, AUTOSELECT_FILTERING).add(dummy); + Locale.filter(priorityList, locales, EXTENDED_FILTERING).add(dummy); + Locale.filter(priorityList, locales, IGNORE_EXTENDED_RANGES).add(dummy); + Locale.filter(priorityList, locales, MAP_EXTENDED_RANGES).add(dummy); + Locale.filter(languageRangesOf("de-DE"), locales, REJECT_EXTENDED_RANGES).add(dummy); + } + + public void test_filter_resultIsModifiable_tags() { + List priorityList = languageRangesOf("de-DE", "de-*-DE"); + List tags = tagsOf("de-DE", "de-Latn-DE", "ja-JP"); + + String dummy = "fr-FR"; + // should not throw + Locale.filterTags(priorityList, tags).add(dummy); + Locale.filterTags(priorityList, tags, AUTOSELECT_FILTERING).add(dummy); + Locale.filterTags(priorityList, tags, EXTENDED_FILTERING).add(dummy); + Locale.filterTags(priorityList, tags, IGNORE_EXTENDED_RANGES).add(dummy); + Locale.filterTags(priorityList, tags, MAP_EXTENDED_RANGES).add(dummy); + Locale.filterTags(languageRangesOf("de-DE"), tags, REJECT_EXTENDED_RANGES).add(dummy); + } + public void test_forLanguageTag() { test_setLanguageTag_wellFormedsingleSubtag(false); test_setLanguageTag_twoWellFormedSubtags(false); @@ -774,22 +1024,27 @@ public void test_getDisplayScript() { Locale l = b.build(); - // getDisplayScript() test relies on the default locale. We set it here to avoid test - // failures if the test device is set to a non-English locale. - Locale.setDefault(Locale.US); - assertEquals("Latin", l.getDisplayScript()); + // getAndSetDefaultForTest(uncategorizedLocale, displayLocale, formatLocale) + Locales locales = Locales.getAndSetDefaultForTest(Locale.US, Locale.GERMANY, Locale.FRANCE); + try { + // Check that getDisplayScript() uses the default DISPLAY Locale. + assertEquals("Lateinisch", l.getDisplayScript()); // the German word for "Latin" - assertEquals("Lateinisch", l.getDisplayScript(Locale.GERMAN)); - // Fallback for navajo, a language for which we don't have data. - assertEquals("Latin", l.getDisplayScript(new Locale("nv", "US"))); + assertEquals("latino", l.getDisplayScript(Locale.ITALY)); - b= new Locale.Builder(); - b.setLanguage("en").setRegion("US").setScript("Fooo"); + // Fallback for navajo, a language for which we don't have data. + assertEquals("Latin", l.getDisplayScript(new Locale("nv", "US"))); - // Will be equivalent to getScriptCode for scripts that aren't - // registered with ISO-15429 (but are otherwise well formed). - l = b.build(); - assertEquals("Fooo", l.getDisplayScript()); + b = new Locale.Builder(); + b.setLanguage("en").setRegion("US").setScript("Fooo"); + + // Will be equivalent to getScriptCode for scripts that aren't + // registered with ISO-15429 (but are otherwise well formed). + l = b.build(); + assertEquals("Fooo", l.getDisplayScript()); + } finally { + locales.setAsDefault(); + } } public void test_setLanguageTag_malformedTags() { @@ -992,6 +1247,74 @@ public void test_immutability() { } } + public void test_lookup_noMatch() { + // RFC 4647 section 3.4. + List languageRanges = languageRangesOf( + "zh-Hant-CN-x-private1-private2", + "zh-Hant-CN-x-private1", + "zh-Hant-CN", + "zh-Hant", + "zh" + ); + assertNull(Locale.lookup(languageRanges, localesOf("de-DE", "fr-FR", "ja-JP"))); + assertNull(Locale.lookupTag(languageRanges, tagsOf("de-DE", "fr-FR", "ja-JP"))); + } + + /** + * Tests that lookup returns the tag/locale that matches the highest priority + * LanguageRange. + */ + public void test_lookup_order() { + // RFC 4647 section 3.4. + List languageRanges = languageRangesOf( + "de-Latn-DE-1996", + "zh-Hant-CN", + "de" + ); + + // de would also match, but de-Latn-DE-1997 occurs earlier in the + // (sorted by descending priority) languageRanges + assertLookup("de-Latn-DE-1996", + languageRanges, + tagsOf("de", "de-Latn-DE-1996")); + + // de-Latn-DE-1996 also includes de-Latn-DE, de-Latn, de; therefore + // de-Latn-DE is preferred over zh-Hant-CN + assertLookup("de-Latn-DE", + languageRanges, + tagsOf("de", "de-Latn-DE", "de-DE-1996", "zh-Hant-CN")); + + // After reversing the priority list of the LanguageRanges, "de" now has the + // highest priority. + assertLookup("de", + languageRangesOf( + "de", + "zh-Hant-CN", + "de-Latn-DE-1996" + ), + tagsOf("de", "de-Latn-DE", "de-DE-1996")); + + // Dropping "de" from the priority list of LanguageRanges false back to de-Latn-DE + assertLookup("de-Latn-DE", + languageRangesOf( + "zh-Hant-CN", + "de-Latn-DE-1996" + ), + tagsOf("de", "de-Latn-DE", "de-DE-1996")); + } + + public void test_lookup_nullArguments() { + List tags = tagsOf("de-DE", "de-Latn-DE"); + List locales = localesOf(tags); + List languageRanges = languageRangesOf("en-*-US", "de-DE"); + + assertThrowsNpe(() -> { Locale.lookup(null, locales); }); + assertThrowsNpe(() -> { Locale.lookup(languageRanges, null); }); + + assertThrowsNpe(() -> { Locale.lookupTag(null, tags); }); + assertThrowsNpe(() -> { Locale.lookupTag(languageRanges, null); }); + } + public void test_toLanguageTag() { Locale.Builder b = new Locale.Builder(); @@ -1254,6 +1577,7 @@ public void test_SerializationBug_26387905() throws Exception { public void test_setDefault_withCategory() { final Locale defaultLocale = Locale.getDefault(); try { + // Establish a baseline for the checks further down Locale.setDefault(Locale.US); assertEquals(Locale.US, Locale.getDefault(Locale.Category.FORMAT)); assertEquals(Locale.US, Locale.getDefault(Locale.Category.DISPLAY)); @@ -1273,8 +1597,78 @@ public void test_setDefault_withCategory() { assertEquals(Locale.FRANCE, Locale.getDefault(Locale.Category.FORMAT)); assertEquals(Locale.FRANCE, Locale.getDefault(Locale.Category.DISPLAY)); assertEquals(Locale.FRANCE, Locale.getDefault()); + + // Check that setDefault(Locale) sets all three defaults + Locale.setDefault(Locale.US); + assertEquals(Locale.US, Locale.getDefault(Locale.Category.FORMAT)); + assertEquals(Locale.US, Locale.getDefault(Locale.Category.DISPLAY)); + assertEquals(Locale.US, Locale.getDefault()); } finally { Locale.setDefault(defaultLocale); } } + + private static List localesOf(String... languageTags) { + return localesOf(tagsOf(languageTags)); + } + + private static List localesOf(List languageTags) { + List result = new ArrayList<>(); + for (String languageTag : languageTags) { + result.add(Locale.forLanguageTag(languageTag)); + } + return Collections.unmodifiableList(result); + } + + private static List tagsOf(String... tags) { + List result = new ArrayList<>(); + for (String tag : tags) { + result.add(tag.toLowerCase()); + } + return Collections.unmodifiableList(result); + } + + private static List languageRangesOf(String... languageRanges) { + List result = new ArrayList<>(); + for (String languageRange : languageRanges) { + result.add(new LanguageRange(languageRange)); + } + return Collections.unmodifiableList(result); + } + + private static void assertFilter(List filteredTags, List languageRanges, + List tags) { + assertEquals(filteredTags, Locale.filterTags(languageRanges, tags)); + + List locales = localesOf(tags); + List filteredLocales = localesOf(filteredTags); + assertEquals(filteredLocales, Locale.filter(languageRanges, locales)); + } + + private static void assertFilter(List filteredTags, List languageRanges, + List tags, Locale.FilteringMode filteringMode) { + assertEquals(filteredTags, + Locale.filterTags(languageRanges, tags, filteringMode)); + + List locales = localesOf(tags); + List filteredLocales = localesOf(filteredTags); + assertEquals(filteredLocales, Locale.filter(languageRanges, locales, filteringMode)); + } + + private static void assertThrowsNpe(Runnable runnable) { + try { + runnable.run(); + fail("Should have thrown NullPointerException"); + } catch (NullPointerException expected) { + } + } + + private static void assertLookup(String expectedTag, List languageRanges, + List tags) { + assertEquals(expectedTag.toLowerCase(), Locale.lookupTag(languageRanges, tags)); + + assertEquals(Locale.forLanguageTag(expectedTag), + Locale.lookup(languageRanges, localesOf(tags))); + } + } diff --git a/luni/src/test/java/libcore/java/util/Locales.java b/luni/src/test/java/libcore/java/util/Locales.java new file mode 100644 index 000000000..102188b54 --- /dev/null +++ b/luni/src/test/java/libcore/java/util/Locales.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 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. + * + */ + +package libcore.java.util; + +import java.util.Locale; +import java.util.Objects; + +import static java.util.Locale.Category.DISPLAY; +import static java.util.Locale.Category.FORMAT; +import static org.junit.Assert.assertEquals; + +/** + * Helper class for tests that need to temporarily change the default Locales. + */ +class Locales { + private final Locale uncategorizedLocale; + private final Locale displayLocale; + private final Locale formatLocale; + + private Locales(Locale uncategorizedLocale, Locale displayLocale, Locale formatLocale) { + this.uncategorizedLocale = uncategorizedLocale; + this.displayLocale = displayLocale; + this.formatLocale = formatLocale; + } + + /** + * Sets the specified default Locale, default DISPLAY Locale and default FORMAT Locale. + * Every call to this method should be paired with exactly one corresponding call to + * reset the previous values: + *
+     *     Locales locales = Locales.getAndSetDefaultForTest(Locale.US, Locale.CHINA, Locale.UK);
+     *     try {
+     *         ...
+     *     } finally {
+     *         locales.setAsDefault();
+     *     }
+     * 
+ */ + public static Locales getAndSetDefaultForTest(Locale uncategorizedLocale, Locale displayLocale, + Locale formatLocale) { + Locales oldLocales = getDefault(); + Locales newLocales = new Locales(uncategorizedLocale, displayLocale, formatLocale); + newLocales.setAsDefault(); + assertEquals(newLocales, getDefault()); // sanity check + return oldLocales; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Locales)) { + return false; + } + Locales that = (Locales) obj; + return uncategorizedLocale.equals(that.uncategorizedLocale) + && displayLocale.equals(that.displayLocale) + && formatLocale.equals(that.formatLocale); + } + + @Override + public int hashCode() { + return Objects.hash(uncategorizedLocale, displayLocale, formatLocale); + } + + @Override + public String toString() { + return "Locales[displayLocale=" + displayLocale + ", locale=" + uncategorizedLocale + + ", formatLocale=" + formatLocale + ']'; + } + + /** + * Reset the system's default Locale values to what they were when this + * Locales was obtained. + */ + public void setAsDefault() { + // The lines below must set the Locales in this order because setDefault(Locale) + // overwrites the other ones. + Locale.setDefault(uncategorizedLocale); + Locale.setDefault(DISPLAY, displayLocale); + Locale.setDefault(FORMAT, formatLocale); + } + + public static Locales getDefault() { + return new Locales( + Locale.getDefault(), Locale.getDefault(DISPLAY), Locale.getDefault(FORMAT)); + } + +} diff --git a/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java b/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java index e3c4ad122..098801b22 100644 --- a/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java +++ b/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java @@ -16,41 +16,121 @@ package libcore.java.util; +import java.util.AbstractList; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.Spliterator; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.fail; +import static org.junit.Assert.assertSame; public class MapDefaultMethodTester { private MapDefaultMethodTester() {} - public static void test_getOrDefault(Map m, boolean acceptsNullKey, - boolean acceptsNullValue) { - // Unmapped key - assertEquals(-1.0, m.getOrDefault(1, -1.0)); + /** + * @param getAcceptsAnyObject whether get() and getOrDefault() allow any + * nonnull key Object, returning false rather than throwing + * ClassCastException if the key is inappropriate for the map. + */ + public static void test_getOrDefault(Map m, boolean acceptsNullKey, + boolean acceptsNullValue, boolean getAcceptsAnyObject) { + // absent key + if (acceptsNullKey) { + checkGetOrDefault("default", m, null, "default"); + if (acceptsNullValue) { + checkGetOrDefault(null, m, null, null); + } + } + m.put("key", "value"); + if (acceptsNullValue) { + checkGetOrDefault(null, m, "absentkey", null); + if (acceptsNullKey) { + checkGetOrDefault(null, m, null, null); + } + } + checkGetOrDefault("default", m, "absentkey", "default"); + m.put("anotherkey", "anothervalue"); + checkGetOrDefault("default", m, "absentkey", "default"); - // Mapped key - m.put(1, 11.0); - assertEquals(11.0, m.getOrDefault(1, -1.0)); + // absent key - inappropriate type + boolean getAcceptedObject; + try { + assertSame("default", m.getOrDefault(new Object(), "default")); + getAcceptedObject = true; + } catch (ClassCastException e) { + getAcceptedObject = false; + } + assertEquals(getAcceptsAnyObject, getAcceptedObject); + + // present key + checkGetOrDefault("value", m, "key", "default"); + checkGetOrDefault("value", m, "key", new String("value")); - // Check for null value + // null value if (acceptsNullValue) { - m.put(1, null); - assertEquals(null, m.getOrDefault(1, -1.0)); + m.put("keyWithNullValue", null); + checkGetOrDefault(null, m, "keyWithNullValue", "default"); } - // Check for null key + // null key if (acceptsNullKey) { - m.put(null, 1.0); - assertEquals(1.0, m.getOrDefault(null, -1.0)); + m.put(null, "valueForNullKey"); + checkGetOrDefault("valueForNullKey", m, null, "valueForNullKey"); } } + /** + * Checks that the value returned by {@link LinkedHashMap#getOrDefault(Object, Object)} + * is consistent with various other ways getOrDefault() could be computed. + */ + private static void checkGetOrDefault( + V expected, Map map, K key, V defaultValue) { + V actual = map.getOrDefault(key, defaultValue); + assertSame(expected, actual); + + assertSame(expected, getOrDefault_hashMap(map, key, defaultValue)); + assertSame(expected, getOrDefault_optimizeForPresent(map, key, defaultValue)); + assertSame(expected, getOrDefault_optimizeForAbsent(map, key, defaultValue)); + } + + /** Implementation of getOrDefault() on top of HashMap.getOrDefault(). */ + private static V getOrDefault_hashMap(Map map, K key, V defaultValue) { + return new HashMap<>(map).getOrDefault(key, defaultValue); + } + + /** + * Implementation of Map.getOrDefault() that only needs one lookup if the key is + * absent. + */ + private static V getOrDefault_optimizeForAbsent(Map map, K key, V defaultValue) { + return map.containsKey(key) ? map.get(key) : defaultValue; + } + + /** + * Implementation of getOrDefault() that only needs one lookup if the key is + * present and not mapped to null. + */ + private static V getOrDefault_optimizeForPresent(Map map, K key, V defaultValue) { + V result = map.get(key); + if (result == null && !map.containsKey(key)) { + result = defaultValue; + } + return result; + } + public static void test_forEach(Map m) { Map replica = new HashMap<>(); m.put(1, 10.0); @@ -63,6 +143,7 @@ public static void test_forEach(Map m) { // Null pointer exception for empty function try { m.forEach(null); + fail(); } catch (NullPointerException expected) { } } @@ -275,6 +356,7 @@ public static void test_computeIfPresent(Map m, boolean accepts // If the remapping function is null try { m.computeIfPresent(1, null); + fail(); } catch (NullPointerException expected) {} if (acceptsNullKey) { @@ -283,6 +365,7 @@ public static void test_computeIfPresent(Map m, boolean accepts } else { try { m.computeIfPresent(null, (k, v) -> 5.0); + fail(); } catch (NullPointerException expected) {} } } @@ -351,4 +434,79 @@ public static void test_merge(Map m, boolean acceptsNullKey) { } catch (NullPointerException expected) {} } } + + public static void test_entrySet_spliterator_unordered(Map m) { + checkEntrySpliterator(m); + m.put("key", "value"); + checkEntrySpliterator(m, "key", "value"); + m.put("key2", "value2"); + checkEntrySpliterator(m, "key", "value", "key2", "value2"); + m.put("key", "newValue"); + checkEntrySpliterator(m, "key", "newValue", "key2", "value2"); + m.remove("key2"); + checkEntrySpliterator(m, "key", "newValue"); + m.clear(); + + // Check 100 entries in random order + Random random = new Random(1000); // arbitrary + + final List order = new ArrayList<>(new AbstractList() { + @Override public Integer get(int index) { return index; } + @Override public int size() { return 100; } + }); + List> entries = new AbstractList>() { + @Override + public Map.Entry get(int index) { + int i = order.get(index); + return new AbstractMap.SimpleEntry<>("key" + i, "value" + i); + } + @Override public int size() { return order.size(); } + }; + Collections.shuffle(order, random); // Pick a random put() order of the entries + for (Map.Entry entry : entries) { + m.put(entry.getKey(), entry.getValue()); + } + Collections.shuffle(order, random); // Pick a different random order for the assertion + checkEntrySpliterator(m, new ArrayList<>(entries)); + } + + private static void checkEntrySpliterator(Map m, + String... expectedKeysAndValues) { + checkEntrySpliterator(m, makeEntries(expectedKeysAndValues)); + } + + private static void checkEntrySpliterator(Map m, + ArrayList> expectedEntries) { + Set> entrySet = m.entrySet(); + Comparator> keysThenValuesComparator = + Map.Entry.comparingByKey() + .thenComparing(Map.Entry.comparingByValue()); + + assertTrue(entrySet.spliterator().hasCharacteristics(Spliterator.DISTINCT)); + + SpliteratorTester.runBasicIterationTests_unordered(entrySet.spliterator(), + expectedEntries, keysThenValuesComparator); + SpliteratorTester.runBasicSplitTests(entrySet.spliterator(), + expectedEntries, keysThenValuesComparator); + SpliteratorTester.testSpliteratorNPE(entrySet.spliterator()); + + boolean isSized = entrySet.spliterator().hasCharacteristics(Spliterator.SIZED); + if (isSized) { + SpliteratorTester.runSizedTests(entrySet.spliterator(), entrySet.size()); + } + Spliterator subSpliterator = entrySet.spliterator().trySplit(); + if (subSpliterator != null && subSpliterator.hasCharacteristics(Spliterator.SIZED)) { + SpliteratorTester.runSubSizedTests(entrySet.spliterator(), entrySet.size()); + } + } + + private static ArrayList> makeEntries(T... keysAndValues) { + assertEquals(0, keysAndValues.length % 2); + ArrayList> result = new ArrayList<>(); + for (int i = 0; i < keysAndValues.length; i += 2) { + result.add(new AbstractMap.SimpleEntry<>(keysAndValues[i], keysAndValues[i+1])); + } + return result; + } + } diff --git a/luni/src/test/java/libcore/java/util/ResourceLeakageDetector.java b/luni/src/test/java/libcore/java/util/ResourceLeakageDetector.java deleted file mode 100644 index 954665ad0..000000000 --- a/luni/src/test/java/libcore/java/util/ResourceLeakageDetector.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2014 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. - */ -package libcore.java.util; - -/** - * Detects resource leakages for resources that are protected by CloseGuard mechanism. - * - *

If multiple instances of this are active at the same time, i.e. have been created but not yet - * had their {@link #checkForLeaks()} method called then while they will report all the leakages - * detected they may report the leakages caused by the code being tested by another detector. - * - *

The underlying CloseGuardMonitor is loaded using reflection to ensure that this will run, - * albeit doing nothing, on the reference implementation. - */ -public class ResourceLeakageDetector { - /** The class for the CloseGuardMonitor, null if not supported. */ - private static final Class CLOSE_GUARD_MONITOR_CLASS; - - static { - ClassLoader classLoader = ResourceLeakageDetector.class.getClassLoader(); - Class clazz; - try { - // Make sure that the CloseGuard class exists; this ensures that this is not running - // on a RI JVM. - classLoader.loadClass("dalvik.system.CloseGuard"); - - // Load the monitor class for later instantiation. - clazz = classLoader.loadClass("dalvik.system.CloseGuardMonitor"); - - } catch (ClassNotFoundException e) { - System.err.println("Resource leakage will not be detected; " - + "this is expected in the reference implementation"); - e.printStackTrace(System.err); - - // Ignore, probably running in reference implementation. - clazz = null; - } - - CLOSE_GUARD_MONITOR_CLASS = clazz; - } - - /** - * The underlying CloseGuardMonitor that will perform the post test checks for resource - * leakage. - */ - private Runnable postTestChecker; - - /** - * Create a new detector. - * - * @return The new {@link ResourceLeakageDetector}, its {@link #checkForLeaks()} method must be - * called otherwise it will not clean up properly after itself. - */ - public static ResourceLeakageDetector newDetector() - throws Exception { - return new ResourceLeakageDetector(); - } - - private ResourceLeakageDetector() - throws Exception { - if (CLOSE_GUARD_MONITOR_CLASS != null) { - postTestChecker = (Runnable) CLOSE_GUARD_MONITOR_CLASS.newInstance(); - } - } - - /** - * Detect any leaks that have arisen since this was created. - * - * @throws Exception If any leaks were detected. - */ - public void checkForLeaks() throws Exception { - // If available check for resource leakage. - if (postTestChecker != null) { - postTestChecker.run(); - } - } -} diff --git a/luni/src/test/java/libcore/java/util/ResourceLeakageDetectorTest.java b/luni/src/test/java/libcore/java/util/ResourceLeakageDetectorTest.java deleted file mode 100644 index d86c9f2f1..000000000 --- a/luni/src/test/java/libcore/java/util/ResourceLeakageDetectorTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2014 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. - */ -package libcore.java.util; - -import dalvik.system.CloseGuard; -import junit.framework.TestCase; - -/** - * Test for {@link ResourceLeakageDetector} - */ -public class ResourceLeakageDetectorTest extends TestCase { - /** - * This test will not work on RI as it does not support the CloseGuard or similar - * mechanism. - */ - public void testDetectsUnclosedCloseGuard() throws Exception { - ResourceLeakageDetector detector = ResourceLeakageDetector.newDetector(); - try { - CloseGuard closeGuard = createCloseGuard(); - closeGuard.open("open"); - } finally { - try { - System.logI("Checking for leaks"); - detector.checkForLeaks(); - fail(); - } catch (AssertionError expected) { - } - } - } - - public void testIgnoresClosedCloseGuard() throws Exception { - ResourceLeakageDetector detector = ResourceLeakageDetector.newDetector(); - try { - CloseGuard closeGuard = createCloseGuard(); - closeGuard.open("open"); - closeGuard.close(); - } finally { - detector.checkForLeaks(); - } - } - - /** - * Private method to ensure that the CloseGuard object is garbage collected. - */ - private CloseGuard createCloseGuard() { - final CloseGuard closeGuard = CloseGuard.get(); - new Object() { - @Override - protected void finalize() throws Throwable { - try { - closeGuard.warnIfOpen(); - } finally { - super.finalize(); - } - } - }; - - return closeGuard; - } -} diff --git a/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java b/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java index b69a2dd31..f17eb22c4 100644 --- a/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java +++ b/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java @@ -44,7 +44,7 @@ public void test_missingRegisteredClass() throws Exception { ServiceLoader.load(ServiceLoaderTestInterfaceMissingClass.class).iterator().next(); fail(); } catch (ServiceConfigurationError expected) { - assertTrue(expected.getCause() instanceof ClassNotFoundException); + assertTrue(expected.toString(), expected.getCause() instanceof ClassNotFoundException); } } @@ -55,7 +55,7 @@ public void test_wrongTypeRegisteredClass() throws Exception { ServiceLoader.load(ServiceLoaderTestInterfaceWrongType.class).iterator().next(); fail(); } catch (ServiceConfigurationError expected) { - assertTrue(expected.getCause() instanceof ClassCastException); + assertTrue(expected.toString(), expected.getCause() instanceof ClassCastException); } } diff --git a/luni/src/test/java/libcore/java/util/SpliteratorTester.java b/luni/src/test/java/libcore/java/util/SpliteratorTester.java index a5b076b7a..5b2700a9a 100644 --- a/luni/src/test/java/libcore/java/util/SpliteratorTester.java +++ b/luni/src/test/java/libcore/java/util/SpliteratorTester.java @@ -18,27 +18,36 @@ import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.List; +import java.util.Locale; import java.util.Spliterator; import java.util.function.Consumer; +import static java.util.Spliterator.ORDERED; +import static java.util.Spliterator.SIZED; +import static java.util.Spliterator.SUBSIZED; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; public class SpliteratorTester { public static void runBasicIterationTests(Spliterator spliterator, - ArrayList expectedElements) { - ArrayList recorder = new ArrayList(expectedElements.size()); + List expectedElements) { + List recorder = new ArrayList(expectedElements.size()); Consumer consumer = (T value) -> recorder.add(value); // tryAdvance. - assertTrue(spliterator.tryAdvance(consumer)); - assertEquals(expectedElements.get(0), recorder.get(0)); + boolean didAdvance = spliterator.tryAdvance(consumer); + assertEquals(!expectedElements.isEmpty(), didAdvance); // forEachRemaining. spliterator.forEachRemaining(consumer); @@ -50,13 +59,17 @@ public static void runBasicIterationTests(Spliterator spliterator, } public static void runBasicIterationTests_unordered(Spliterator spliterator, - ArrayList expectedElements, Comparator comparator) { + List expectedElements, Comparator comparator) { ArrayList recorder = new ArrayList(expectedElements.size()); Consumer consumer = (T value) -> recorder.add(value); // tryAdvance. - assertTrue(spliterator.tryAdvance(consumer)); - assertTrue(expectedElements.contains(recorder.get(0))); + if (expectedElements.isEmpty()) { + assertFalse(spliterator.tryAdvance(consumer)); + } else { + assertTrue(spliterator.tryAdvance(consumer)); + assertTrue(expectedElements.contains(recorder.get(0))); + } // forEachRemaining. spliterator.forEachRemaining(consumer); @@ -97,20 +110,25 @@ public static void testSpliteratorNPE(Spliterator spliterator) { } public static > void runBasicSplitTests( - Iterable spliterable, ArrayList expectedElements) { + Iterable spliterable, List expectedElements) { runBasicSplitTests(spliterable, expectedElements, T::compareTo); } public static void runBasicSplitTests(Spliterator spliterator, - ArrayList expectedElements, Comparator comparator) { + List expectedElements, Comparator comparator) { + boolean empty = expectedElements.isEmpty(); ArrayList recorder = new ArrayList<>(); // Advance the original spliterator by one element. - assertTrue(spliterator.tryAdvance(value -> recorder.add(value))); + boolean didAdvance = spliterator.tryAdvance(value -> recorder.add(value)); + assertEquals(!empty, didAdvance); // Try splitting it. Spliterator split1 = spliterator.trySplit(); - if (split1 != null) { + // trySplit() may always return null, but is only required to when empty + if (empty) { + assertNull(split1); + } else if (split1 != null) { // Try to split the resulting split. Spliterator split1_1 = split1.trySplit(); Spliterator split1_2 = split1.trySplit(); @@ -124,7 +142,6 @@ public static void runBasicSplitTests(Spliterator spliterator, // Iterate over the remainder of split1. recordAndAssertBasicIteration(split1, recorder); } - // Try to split the original iterator again. Spliterator split2 = spliterator.trySplit(); if (split2 != null) { @@ -139,6 +156,13 @@ public static void runBasicSplitTests(Spliterator spliterator, assertEquals(expectedElements, recorder); } + public static void assertSupportsTrySplit(Iterable spliterable) { + assertNotNull(spliterable.spliterator().trySplit()); + // only non-empty Iterables may return a non-null value from trySplit() + assertTrue("Expected nonempty iterable, got " + spliterable, + spliterable.iterator().hasNext()); + } + /** * Note that the contract of trySplit() is generally quite weak (as it must be). There * are no demands about when the spliterator can or cannot split itself. In general, this @@ -147,28 +171,68 @@ public static void runBasicSplitTests(Spliterator spliterator, * iterated over. */ public static void runBasicSplitTests(Iterable spliterable, - ArrayList expectedElements, Comparator comparator) { + List expectedElements, Comparator comparator) { runBasicSplitTests(spliterable.spliterator(), expectedElements, comparator); } - public static void runOrderedTests(Iterable spliterable) { - ArrayList iteration1 = new ArrayList<>(); - ArrayList iteration2 = new ArrayList<>(); + private static List toList(Iterator iterator) { + List result = new ArrayList<>(); + while (iterator.hasNext()) { + result.add(iterator.next()); + } + return result; + } - spliterable.spliterator().forEachRemaining(value -> iteration1.add(value)); - spliterable.spliterator().forEachRemaining(value -> iteration2.add(value)); + private static List toList(Spliterator spliterator) { + List result = new ArrayList<>(); + spliterator.forEachRemaining(value -> result.add(value)); + return result; + } - assertEquals(iteration1, iteration2); + public static void runOrderedTests(Iterable spliterable) { + List elements = toList(spliterable.spliterator()); + assertEquals("Ordering should be consistent", elements, toList(spliterable.spliterator())); - iteration1.clear(); - iteration2.clear(); + // NOTE: This would fail for some Collections because of b/34757089: + // assertTrue(spliterable.spliterator().hasCharacteristics(ORDERED)); - spliterable.spliterator().trySplit().forEachRemaining(value -> iteration1.add(value)); - spliterable.spliterator().trySplit().forEachRemaining(value -> iteration2.add(value)); - assertEquals(iteration1, iteration2); + if (spliterable instanceof Collection) { + assertEquals("ORDERED Spliterator must be consistent with Iterator: " + + spliterable.getClass(), elements, toList(spliterable.iterator())); + } + + boolean isEmpty = !spliterable.iterator().hasNext(); + + Spliterator sa = spliterable.spliterator(); + Spliterator sb = spliterable.spliterator(); + Spliterator saSplit = sa.trySplit(); + Spliterator sbSplit = sb.trySplit(); + // trySplit() may always return null, but is only required to when empty + if (isEmpty) { + assertNull(saSplit); + assertNull(sbSplit); + } else { + // A non-empty Iterable may still return null from trySplit(); + // if it does, then the un-split parent spliterators (sa, sb) must + // each still contain all of the elements. Regardless of whether + // the split was successful, sa and sb must behave the consistently + // with each other since they came from the same Iterable. + if (saSplit != null) { + assertEquals(toList(saSplit), toList(sbSplit)); + assertEquals(toList(sa), toList(sb)); + } else { + assertEquals(elements, toList(sa)); + assertEquals(elements, toList(sb)); + } + } } + /** + * Checks that the specified SIZED Spliterator reports containing the + * specified number of elements. + */ public static void runSizedTests(Spliterator spliterator, int expectedSize) { + assertHasCharacteristics(SIZED, spliterator); assertEquals(expectedSize, spliterator.estimateSize()); assertEquals(expectedSize, spliterator.getExactSizeIfKnown()); } @@ -177,14 +241,28 @@ public static void runSizedTests(Iterable spliterable, int expectedSize) runSizedTests(spliterable.spliterator(), expectedSize); } + /** + * Checks that the specified Spliterator and its {@link Spliterator#trySplit() + * children} are SIZED and SUBSIZED and report containing the specified number + * of elements. + */ public static void runSubSizedTests(Spliterator spliterator, int expectedSize) { + assertHasCharacteristics(SIZED | SUBSIZED, spliterator); assertEquals(expectedSize, spliterator.estimateSize()); assertEquals(expectedSize, spliterator.getExactSizeIfKnown()); - - Spliterator split1 = spliterator.trySplit(); - assertEquals(expectedSize, spliterator.estimateSize() + split1.estimateSize()); - assertEquals(expectedSize, spliterator.getExactSizeIfKnown() + split1.getExactSizeIfKnown()); + Spliterator child = spliterator.trySplit(); + assertHasCharacteristics(SIZED | SUBSIZED, spliterator); + if (expectedSize == 0) { + assertNull(child); + assertEquals(expectedSize, spliterator.estimateSize()); + assertEquals(expectedSize, spliterator.getExactSizeIfKnown()); + } else { + assertHasCharacteristics(SIZED | SUBSIZED, child); + assertEquals(expectedSize, spliterator.estimateSize() + child.estimateSize()); + assertEquals(expectedSize, + spliterator.getExactSizeIfKnown() + child.getExactSizeIfKnown()); + } } public static void runSubSizedTests(Iterable spliterable, int expectedSize) { @@ -201,7 +279,10 @@ public static void runDistinctTests(Iterable spliterable) { // First test that iterating via the spliterator using forEachRemaining // yields distinct elements. spliterator.forEachRemaining(value -> { distinct.add(value); allElements.add(value); }); - split1.forEachRemaining(value -> { distinct.add(value); allElements.add(value); }); + // trySplit() may return null, even when non-empty + if (split1 != null) { + split1.forEachRemaining(value -> { distinct.add(value); allElements.add(value); }); + } assertEquals(distinct.size(), allElements.size()); distinct.clear(); @@ -213,7 +294,10 @@ public static void runDistinctTests(Iterable spliterable) { while (spliterator.tryAdvance(value -> { distinct.add(value); allElements.add(value); })) { } - while (split1.tryAdvance(value -> { distinct.add(value); allElements.add(value); })) { + // trySplit() may return null, even when non-empty + if (split1 != null) { + while (split1.tryAdvance(value -> { distinct.add(value); allElements.add(value); })) { + } } assertEquals(distinct.size(), allElements.size()); @@ -241,4 +325,13 @@ public static void runSortedTests(Iterable spliterable, Comparator com public static > void runSortedTests(Iterable spliterable) { runSortedTests(spliterable, T::compareTo); } + + public static void assertHasCharacteristics(int expectedCharacteristics, + Spliterator spliterator) { + int actualCharacteristics = spliterator.characteristics(); + String msg = String.format(Locale.US, + "Expected expectedCharacteristics containing 0x%x, got 0x%x", + expectedCharacteristics, actualCharacteristics); + assertTrue(msg, spliterator.hasCharacteristics(expectedCharacteristics)); + } } diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java index 5a6fa7f8c..375eb367c 100644 --- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java +++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java @@ -16,13 +16,21 @@ package libcore.java.util; +import junit.framework.TestCase; + import java.text.SimpleDateFormat; +import java.time.ZoneId; +import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.SimpleTimeZone; import java.util.TimeZone; -import junit.framework.TestCase; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicInteger; public class TimeZoneTest extends TestCase { // http://code.google.com/p/android/issues/detail?id=877 @@ -140,7 +148,15 @@ public void testHasSameRules() throws Exception { // http://code.google.com/p/android/issues/detail?id=24036 public void testNullId() throws Exception { try { - TimeZone.getTimeZone(null); + TimeZone.getTimeZone((String) null); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testNullZoneId() throws Exception { + try { + TimeZone.getTimeZone((ZoneId) null); fail(); } catch (NullPointerException expected) { } @@ -200,15 +216,26 @@ public void testSimpleTimeZoneDoesNotCallOverrideableMethodsFromConstructor() { // http://b/7955614 and http://b/8026776. public void testDisplayNames() throws Exception { + checkDisplayNames(Locale.US); + } + + public void testDisplayNames_nonUS() throws Exception { + // run checkDisplayNames with an arbitrary set of Locales. + checkDisplayNames(Locale.CHINESE); + checkDisplayNames(Locale.FRENCH); + checkDisplayNames(Locale.forLanguageTag("bn-BD")); + } + + public void checkDisplayNames(Locale locale) throws Exception { // Check that there are no time zones that use DST but have the same display name for // both standard and daylight time. StringBuilder failures = new StringBuilder(); for (String id : TimeZone.getAvailableIDs()) { TimeZone tz = TimeZone.getTimeZone(id); - String longDst = tz.getDisplayName(true, TimeZone.LONG, Locale.US); - String longStd = tz.getDisplayName(false, TimeZone.LONG, Locale.US); - String shortDst = tz.getDisplayName(true, TimeZone.SHORT, Locale.US); - String shortStd = tz.getDisplayName(false, TimeZone.SHORT, Locale.US); + String longDst = tz.getDisplayName(true, TimeZone.LONG, locale); + String longStd = tz.getDisplayName(false, TimeZone.LONG, locale); + String shortDst = tz.getDisplayName(true, TimeZone.SHORT, locale); + String shortStd = tz.getDisplayName(false, TimeZone.SHORT, locale); if (tz.useDaylightTime()) { // The long std and dst strings must differ! @@ -261,6 +288,31 @@ public void testDisplayNames() throws Exception { assertEquals("", failures.toString()); } + // http://b/30527513 + public void testDisplayNamesWithScript() throws Exception { + Locale latinLocale = Locale.forLanguageTag("sr-Latn-RS"); + Locale cyrillicLocale = Locale.forLanguageTag("sr-Cyrl-RS"); + Locale noScriptLocale = Locale.forLanguageTag("sr-RS"); + TimeZone tz = TimeZone.getTimeZone("Europe/London"); + + final String latinName = "Srednje vreme po Griniču"; + final String cyrillicName = "Средње време по Гриничу"; + + // Check java.util.TimeZone + assertEquals(latinName, tz.getDisplayName(latinLocale)); + assertEquals(cyrillicName, tz.getDisplayName(cyrillicLocale)); + assertEquals(cyrillicName, tz.getDisplayName(noScriptLocale)); + + // Check ICU TimeZoneNames + // The one-argument getDisplayName() override uses LONG_GENERIC style which is different + // from what java.util.TimeZone uses. Force the LONG style to get equivalent results. + final int style = android.icu.util.TimeZone.LONG; + android.icu.util.TimeZone utz = android.icu.util.TimeZone.getTimeZone(tz.getID()); + assertEquals(latinName, utz.getDisplayName(false, style, latinLocale)); + assertEquals(cyrillicName, utz.getDisplayName(false, style, cyrillicLocale)); + assertEquals(cyrillicName, utz.getDisplayName(false, style, noScriptLocale)); + } + // http://b/7955614 public void testApia() throws Exception { TimeZone tz = TimeZone.getTimeZone("Pacific/Apia"); @@ -288,15 +340,6 @@ private static String formatGmtString(TimeZone tz, boolean daylight) { return String.format("GMT%c%02d:%02d", sign, offset / 60, offset % 60); } - public void testAllDisplayNames() throws Exception { - for (Locale locale : Locale.getAvailableLocales()) { - for (String id : TimeZone.getAvailableIDs()) { - TimeZone tz = TimeZone.getTimeZone(id); - assertNotNull(tz.getDisplayName(false, TimeZone.LONG, locale)); - } - } - } - // http://b/18839557 public void testOverflowing32BitUnixDates() { final TimeZone tz = TimeZone.getTimeZone("America/New_York"); @@ -340,4 +383,107 @@ public void testSetDefaultAppliesToIcuTimezone() { TimeZone.setDefault(origTz); } } + + // http://b/30937209 + public void testSetDefaultDeadlock() throws InterruptedException, BrokenBarrierException { + // Since this tests a deadlock, the test has two fundamental problems: + // - it is probabilistic: it's not guaranteed to fail if the problem exists + // - if it fails, it will effectively hang the current runtime, as no other thread will + // be able to call TimeZone.getDefault()/setDefault() successfully any more. + + // 10 was too low to be reliable, 100 failed more than half the time (on a bullhead). + final int iterations = 100; + TimeZone otherTimeZone = TimeZone.getTimeZone("Europe/London"); + AtomicInteger setterCount = new AtomicInteger(); + CyclicBarrier startBarrier = new CyclicBarrier(2); + Thread setter = new Thread(() -> { + waitFor(startBarrier); + for (int i = 0; i < iterations; i++) { + TimeZone.setDefault(otherTimeZone); + TimeZone.setDefault(null); + setterCount.set(i+1); + } + }); + setter.setName("testSetDefaultDeadlock setter"); + + AtomicInteger getterCount = new AtomicInteger(); + Thread getter = new Thread(() -> { + waitFor(startBarrier); + for (int i = 0; i < iterations; i++) { + android.icu.util.TimeZone.getDefault(); + getterCount.set(i+1); + } + }); + getter.setName("testSetDefaultDeadlock getter"); + + setter.start(); + getter.start(); + + // 2 seconds is plenty: If successful, we usually complete much faster. + setter.join(1000); + getter.join(1000); + if (setter.isAlive() || getter.isAlive()) { + fail("Threads are still alive. Getter iteration count: " + getterCount.get() + + ", setter iteration count: " + setterCount.get()); + } + // Guard against unexpected uncaught exceptions. + assertEquals("Setter iterations", iterations, setterCount.get()); + assertEquals("Getter iterations", iterations, getterCount.get()); + } + + // http://b/30979219 + public void testSetDefaultRace() throws InterruptedException { + // Since this tests a race condition, the test is probabilistic: it's not guaranteed to + // fail if the problem exists + + // These iterations are significantly faster than the ones in #testSetDefaultDeadlock + final int iterations = 10000; + List exceptions = Collections.synchronizedList(new ArrayList<>()); + Thread.UncaughtExceptionHandler handler = (t, e) -> exceptions.add(e); + + CyclicBarrier startBarrier = new CyclicBarrier(2); + Thread clearer = new Thread(() -> { + waitFor(startBarrier); + for (int i = 0; i < iterations; i++) { + // This is not public API but can effectively be invoked via + // java.util.TimeZone.setDefault. Call it directly to reduce the amount of code + // involved in this test. + android.icu.util.TimeZone.clearCachedDefault(); + } + }); + clearer.setName("testSetDefaultRace clearer"); + clearer.setUncaughtExceptionHandler(handler); + + Thread getter = new Thread(() -> { + waitFor(startBarrier); + for (int i = 0; i < iterations; i++) { + android.icu.util.TimeZone.getDefault(); + } + }); + getter.setName("testSetDefaultRace getter"); + getter.setUncaughtExceptionHandler(handler); + + clearer.start(); + getter.start(); + + // 2 seconds is plenty: If successful, we usually complete much faster. + clearer.join(1000); + getter.join(1000); + + if (!exceptions.isEmpty()) { + Throwable firstException = exceptions.get(0); + firstException.printStackTrace(); + fail("Threads did not succeed successfully: " + firstException); + } + assertFalse("clearer thread is still alive", clearer.isAlive()); + assertFalse("getter thread is still alive", getter.isAlive()); + } + + private static void waitFor(CyclicBarrier barrier) { + try { + barrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException(e); + } + } } diff --git a/luni/src/test/java/libcore/java/util/TreeMapTest.java b/luni/src/test/java/libcore/java/util/TreeMapTest.java index 355d29d46..87f82399b 100644 --- a/luni/src/test/java/libcore/java/util/TreeMapTest.java +++ b/luni/src/test/java/libcore/java/util/TreeMapTest.java @@ -16,13 +16,17 @@ package libcore.java.util; +import junit.framework.TestCase; + +import java.util.AbstractMap; import java.util.AbstractMap.SimpleEntry; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NavigableMap; @@ -30,9 +34,6 @@ import java.util.SortedMap; import java.util.Spliterator; import java.util.TreeMap; -import java.util.WeakHashMap; - -import junit.framework.TestCase; import libcore.util.SerializationTester; public class TreeMapTest extends TestCase { @@ -218,6 +219,33 @@ public void testNullsWithNaturalOrder() { } } + // Tests for naming of TreeMap.TreeMapEntry. Based on similar tests + // that exist for LinkedHashMap.LinkedHashMapEntry. + /** + * Check that {@code TreeMap.Entry} compiles and refers to + * {@link java.util.Map.Entry}, which is required for source + * compatibility with earlier versions of Android. + */ + public void test_entryCompatibility_compiletime() { + assertEquals(Map.Entry.class, TreeMap.Entry.class); + } + + /** + * Checks that there is no nested class named 'Entry' in TreeMap. + * If {@link #test_entryCompatibility_compiletime()} passes but + * this test fails, then the test was probably compiled against a + * version of TreeMap that does not have a nested Entry class, + * but run against a version that does. + */ + public void test_entryCompatibility_runtime() { + String forbiddenClassName = "java.util.TreeMap$Entry"; + try { + Class.forName(forbiddenClassName); + fail("Class " + forbiddenClassName + " should not exist"); + } catch (ClassNotFoundException expected) { + } + } + public void testClassCastExceptions() { Map map = new TreeMap(); map.put("A", "a"); @@ -442,6 +470,150 @@ public void testJava5SubMapSerialization() { }.test(); } + /** + * Taking a headMap or tailMap (exclusive or inclusive of the bound) of + * a TreeMap with unbounded range never throws IllegalArgumentException. + */ + public void testBounds_fromUnbounded() { + applyBound('[', new TreeMap<>()); + applyBound(']', new TreeMap<>()); + applyBound('(', new TreeMap<>()); + applyBound(')', new TreeMap<>()); + } + + /** + * Taking an exclusive-end submap of a parent map with an exclusive + * range is allowed only if the bounds go in the same direction + * (if parent and child are either both headMaps or both tailMaps, + * but not otherwise). + */ + public void testBounds_openSubrangeOfOpenRange() { + // NavigableMap.{tail,head}Map(T key, boolean inclusive)'s + // documentation says that it throws IAE "if this map itself has a + // restricted range, and key lies outside the bounds of the range". + // Since that documentation doesn't mention the value of inclusive, + // one could argue that the following two cases should throw IAE, + // but the actual implementation in TreeMap does not. This test + // asserts the actual behavior. + assertTrue(isWithinBounds(')', ')')); + assertTrue(isWithinBounds('(', '(')); + + // The following two tests check that TreeMap's behavior matches + // that from earlier versions of Android (from before Android N). + // AOSP commit b4105e7f1e3ab24131976f68be4554e694a0e1d4 ensured + // that Android N was consistent with earlier versions' behavior. + // Specifically, on Android, + // new TreeMap<>().headMap(0, false).tailMap(0, false) + // and new TreeMap<>().tailMap(0, false).headMap(0, false) + // are both not allowed. + assertFalse(isWithinBounds(')', '(')); + assertFalse(isWithinBounds('(', ')')); + } + + /** + * Taking a exclusive-end submap of an inclusive-end parent map is not + * allowed regardless of the direction of the constraints (headMap + * vs. tailMap) because the inclusive bound of the submap is not + * contained in the exclusive range of the parent map. + */ + public void testBounds_closedSubrangeOfOpenRange() { + assertFalse(isWithinBounds(']', '(')); + assertFalse(isWithinBounds('[', ')')); + assertFalse(isWithinBounds(']', ')')); + assertFalse(isWithinBounds('[', '(')); + } + + /** + * Taking an inclusive-end submap of an inclusive-end parent map + * is allowed regardless of the direction of the constraints (headMap + * vs. tailMap) because the inclusive bound of the submap is + * contained in the inclusive range of the parent map. + */ + public void testBounds_closedSubrangeOfClosedRange() { + assertTrue(isWithinBounds(']', '[')); + assertTrue(isWithinBounds('[', ']')); + assertTrue(isWithinBounds(']', ']')); + assertTrue(isWithinBounds('[', '[')); + } + + /** + * Taking an exclusive-end submap of an inclusive-end parent map + * is allowed regardless of the direction of the constraints (headMap + * vs. tailMap) because the exclusive bound of the submap is + * contained in the inclusive range of the parent map. + * + * Note that + * (a) isWithinBounds(')', '[') == true, while + * (b) isWithinBounds('[', ')') == false + * means that + * {@code new TreeMap<>().tailMap(0, true).headMap(0, false)} + * is allowed but + * {@code new TreeMap<>().headMap(0, false).tailMap(0, true)} + * is not. + */ + public void testBounds_openSubrangeOfClosedRange() { + assertTrue(isWithinBounds(')', '[')); + assertTrue(isWithinBounds('(', ']')); + assertTrue(isWithinBounds('(', '[')); + assertTrue(isWithinBounds(')', ']')); + + // This is allowed: + new TreeMap<>().tailMap(0, true).headMap(0, false); + + // This is not: + try { + new TreeMap<>().headMap(0, false).tailMap(0, true); + fail("Should have thrown"); + } catch (IllegalArgumentException expected) { + // expected + } + } + + /** + * Asserts whether constructing a (head or tail) submap with (inclusive or + * exclusive) bound 0 is allowed on a (head or tail) map with (inclusive or + * exclusive) bound 0. For example, + * + * {@code isWithinBounds(')', ']'} is true because the boundary of "0)" + * (an infinitesimally small negative value) lies within the range "0]", + * but {@code isWithinBounds(']', ')'} is false because 0 does not lie + * within the range "0)". + */ + private static boolean isWithinBounds(char submapBound, char mapBound) { + NavigableMap m = applyBound(mapBound, new TreeMap<>()); + IllegalArgumentException thrownException = null; + try { + applyBound(submapBound, m); + } catch (IllegalArgumentException e) { + thrownException = e; + } + return (thrownException == null); + } + + /** + * Constructs a submap of the specified map, constrained by the given bound. + */ + private static NavigableMap applyBound(char bound, NavigableMap m) { + Integer boundValue = 0; // arbitrary + if (isLowerBound(bound)) { + return m.tailMap(boundValue, isBoundInclusive(bound)); + } else { + return m.headMap(boundValue, isBoundInclusive(bound)); + } + } + + private static boolean isBoundInclusive(char bound) { + return bound == '[' || bound == ']'; + } + + /** + * Returns whether the specified bound corresponds to a tailMap, i.e. a Map whose + * range of values has an (exclusive or inclusive) lower bound. + */ + private static boolean isLowerBound(char bound) { + return bound == '[' || bound == '('; + } + // http://b//26336181 // // Note that this is only worth working around because these bogus comparators worked @@ -471,101 +643,134 @@ public int compare(String o1, String o2) { public void test_spliterator_keySet() { TreeMap treeMap = new TreeMap<>(); + // Elements are added out of order to ensure ordering is still preserved. treeMap.put("a", "1"); + treeMap.put("i", "9"); + treeMap.put("j", "10"); + treeMap.put("k", "11"); + treeMap.put("l", "12"); treeMap.put("b", "2"); treeMap.put("c", "3"); treeMap.put("d", "4"); treeMap.put("e", "5"); + treeMap.put("n", "14"); + treeMap.put("o", "15"); + treeMap.put("p", "16"); treeMap.put("f", "6"); treeMap.put("g", "7"); treeMap.put("h", "8"); - treeMap.put("i", "9"); - treeMap.put("j", "10"); - treeMap.put("k", "11"); - treeMap.put("l", "12"); treeMap.put("m", "13"); - treeMap.put("n", "14"); - treeMap.put("o", "15"); - treeMap.put("p", "16"); Set keys = treeMap.keySet(); - ArrayList expectedKeys = new ArrayList<>(keys); + List expectedKeys = Arrays.asList( + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k" , "l", "m", "n", "o", "p"); SpliteratorTester.runBasicIterationTests_unordered(keys.spliterator(), expectedKeys, String::compareTo); SpliteratorTester.runBasicSplitTests(keys, expectedKeys); SpliteratorTester.testSpliteratorNPE(keys.spliterator()); - - assertTrue(keys.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SORTED)); + assertEquals( + Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SORTED, + keys.spliterator().characteristics()); SpliteratorTester.runSortedTests(keys); SpliteratorTester.runOrderedTests(keys); + SpliteratorTester.assertSupportsTrySplit(keys); } - public void test_spliterator_valueSet() { + public void test_spliterator_values() { TreeMap treeMap = new TreeMap<>(); + // Elements are added out of order to ensure ordering is still preserved. treeMap.put("a", "1"); + treeMap.put("i", "9"); + treeMap.put("j", "10"); + treeMap.put("k", "11"); + treeMap.put("l", "12"); treeMap.put("b", "2"); treeMap.put("c", "3"); treeMap.put("d", "4"); treeMap.put("e", "5"); + treeMap.put("n", "14"); + treeMap.put("o", "15"); + treeMap.put("p", "16"); treeMap.put("f", "6"); treeMap.put("g", "7"); treeMap.put("h", "8"); - treeMap.put("i", "9"); - treeMap.put("j", "10"); - treeMap.put("k", "11"); - treeMap.put("l", "12"); treeMap.put("m", "13"); - treeMap.put("n", "14"); - treeMap.put("o", "15"); - treeMap.put("p", "16"); Collection values = treeMap.values(); - ArrayList expectedValues = new ArrayList<>(values); + List expectedValues = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "11", "12", "13", "14", "15", "16"); SpliteratorTester.runBasicIterationTests_unordered( values.spliterator(), expectedValues, String::compareTo); SpliteratorTester.runBasicSplitTests(values, expectedValues); SpliteratorTester.testSpliteratorNPE(values.spliterator()); - assertTrue(values.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SIZED)); + assertEquals(Spliterator.ORDERED | Spliterator.SIZED, + values.spliterator().characteristics()); SpliteratorTester.runSizedTests(values, 16); SpliteratorTester.runOrderedTests(values); + SpliteratorTester.assertSupportsTrySplit(values); } public void test_spliterator_entrySet() { TreeMap treeMap = new TreeMap<>(); + // Elements are added out of order to ensure ordering is still preserved. treeMap.put("a", "1"); + treeMap.put("i", "9"); + treeMap.put("j", "10"); + treeMap.put("k", "11"); + treeMap.put("l", "12"); treeMap.put("b", "2"); treeMap.put("c", "3"); treeMap.put("d", "4"); treeMap.put("e", "5"); + treeMap.put("n", "14"); + treeMap.put("o", "15"); + treeMap.put("p", "16"); treeMap.put("f", "6"); treeMap.put("g", "7"); treeMap.put("h", "8"); - treeMap.put("i", "9"); - treeMap.put("j", "10"); - treeMap.put("k", "11"); - treeMap.put("l", "12"); treeMap.put("m", "13"); - treeMap.put("n", "14"); - treeMap.put("o", "15"); - treeMap.put("p", "16"); - Set> values = treeMap.entrySet(); - ArrayList> expectedValues = new ArrayList<>(values); + Set> entries = treeMap.entrySet(); + List> expectedValues = Arrays.asList( + entry("a", "1"), + entry("b", "2"), + entry("c", "3"), + entry("d", "4"), + entry("e", "5"), + entry("f", "6"), + entry("g", "7"), + entry("h", "8"), + entry("i", "9"), + entry("j", "10"), + entry("k", "11"), + entry("l", "12"), + entry("m", "13"), + entry("n", "14"), + entry("o", "15"), + entry("p", "16") + ); Comparator> comparator = (a, b) -> (a.getKey().compareTo(b.getKey())); - SpliteratorTester.runBasicIterationTests_unordered(values.spliterator(), expectedValues, + SpliteratorTester.runBasicIterationTests_unordered(entries.spliterator(), expectedValues, (a, b) -> (a.getKey().compareTo(b.getKey()))); - SpliteratorTester.runBasicSplitTests(values, expectedValues, comparator); - SpliteratorTester.testSpliteratorNPE(values.spliterator()); + SpliteratorTester.runBasicSplitTests(entries, expectedValues, comparator); + SpliteratorTester.testSpliteratorNPE(entries.spliterator()); + + assertEquals( + Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SORTED, + entries.spliterator().characteristics()); + SpliteratorTester.runSortedTests(entries, (a, b) -> (a.getKey().compareTo(b.getKey()))); + SpliteratorTester.runOrderedTests(entries); + SpliteratorTester.assertSupportsTrySplit(entries); + } - assertTrue(values.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SORTED)); - SpliteratorTester.runSortedTests(values, (a, b) -> (a.getKey().compareTo(b.getKey()))); - SpliteratorTester.runOrderedTests(values); + private static Map.Entry entry(K key, V value) { + return new AbstractMap.SimpleEntry<>(key, value); } public void test_replaceAll() throws Exception { @@ -586,12 +791,9 @@ public void test_replaceAll() throws Exception { } catch(NullPointerException expected) {} try { - map.replaceAll(new java.util.function.BiFunction() { - @Override - public String apply(String k, String v) { - map.put("foo", v); - return v; - } + map.replaceAll((k, v) -> { + map.put("foo", v); + return v; }); fail(); } catch(ConcurrentModificationException expected) {} diff --git a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java index 5cbaa8fd1..54b354ed2 100644 --- a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java +++ b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java @@ -24,7 +24,8 @@ public class ConcurrentSkipListMapTest extends junit.framework.TestCase { public void test_getOrDefault() { MapDefaultMethodTester.test_getOrDefault(new ConcurrentSkipListMap<>(), - false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/); + false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/, + false /*getAcceptsAnyObject*/); } public void test_forEach() { diff --git a/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java b/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java index 631cc30fa..4d9a648ba 100644 --- a/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java +++ b/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java @@ -318,11 +318,6 @@ public void test_sort() { assertEquals(-3.0, l.get(0)); assertEquals(2.0, l.get(1)); assertEquals(5.0, l.get(2)); - - try { - l.sort((v1, v2) -> v1.compareTo(v2)); - } catch (NullPointerException expected) { - } } public void test_forEach() { @@ -341,6 +336,7 @@ public void test_forEach() { try { l.forEach(null); + fail(); } catch (NullPointerException expected) { } } @@ -406,11 +402,6 @@ public void test_subList_sort() { assertEquals(9, (int)completeList.get(3)); assertEquals(22, (int)completeList.get(4)); assertEquals(12, (int)completeList.get(5)); - - try { - l.sort((v1, v2) -> v1.compareTo(v2)); - } catch (NullPointerException expected) { - } } public void test_subList_forEach() { @@ -430,6 +421,7 @@ public void test_subList_forEach() { try { l.forEach(null); + fail(); } catch (NullPointerException expected) { } } diff --git a/luni/src/test/java/libcore/java/util/logging/OldFileHandlerTest.java b/luni/src/test/java/libcore/java/util/logging/OldFileHandlerTest.java index 785b2655b..ead0b2e93 100644 --- a/luni/src/test/java/libcore/java/util/logging/OldFileHandlerTest.java +++ b/luni/src/test/java/libcore/java/util/logging/OldFileHandlerTest.java @@ -33,6 +33,7 @@ import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; +import junit.framework.AssertionFailedError; import junit.framework.TestCase; public class OldFileHandlerTest extends TestCase { @@ -162,11 +163,15 @@ public void testFileHandler_1params() throws Exception { FileHandler h7 = new FileHandler("%t/log/string%u.log"); h7.publish(r); h7.close(); + boolean assertionPassed = false; try { - assertFileContent(TEMPPATH + SEP + "log", "string0.log", h - .getFormatter()); - fail("should assertion failed"); - } catch (Error e) { + assertFileContent(TEMPPATH + SEP + "log", "string0.log", h.getFormatter()); + assertionPassed = true; + } catch (AssertionFailedError e) { + // Assertion failed as expected. + } + if (assertionPassed) { + fail("assertion should have failed"); } File file = new File(TEMPPATH + SEP + "log"); assertTrue("length list of file is incorrect", file.list().length <= 2); diff --git a/luni/src/test/java/libcore/java/util/prefs/OldPreferenceChangeEventTest.java b/luni/src/test/java/libcore/java/util/prefs/OldPreferenceChangeEventTest.java index d77a11c0c..2f071294c 100644 --- a/luni/src/test/java/libcore/java/util/prefs/OldPreferenceChangeEventTest.java +++ b/luni/src/test/java/libcore/java/util/prefs/OldPreferenceChangeEventTest.java @@ -130,13 +130,6 @@ private static class MockPreferenceChangeListener implements PreferenceChangeLis private boolean addDispatched = false; protected boolean result = false; - public synchronized void waitForEvent() { - try { - wait(500); - } catch (InterruptedException expected) { - } - } - public synchronized void preferenceChange(PreferenceChangeEvent pce) { changed++; addDispatched = true; diff --git a/luni/src/test/java/libcore/java/util/regex/OldMatcherTest.java b/luni/src/test/java/libcore/java/util/regex/OldMatcherTest.java index deb06261d..399093ac7 100644 --- a/luni/src/test/java/libcore/java/util/regex/OldMatcherTest.java +++ b/luni/src/test/java/libcore/java/util/regex/OldMatcherTest.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import junit.framework.TestCase; public class OldMatcherTest extends TestCase { @@ -576,10 +577,10 @@ public void testConcurrentMatcherAccess() throws Exception { final Matcher m = p.matcher(""); ArrayList threads = new ArrayList(); - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < 5; ++i) { Thread t = new Thread(new Runnable() { public void run() { - for (int i = 0; i < 4096; ++i) { + for (int i = 0; i < 1024; ++i) { String s = "some example text"; m.reset(s); try { @@ -613,4 +614,60 @@ public void test33040() throws Exception { String result = p.matcher("mama").region(2, 4).replaceFirst("mi"); assertEquals("mima", result); } + + public void testNamedGroupCapture() throws Exception { + Matcher m = Pattern.compile("(?[a-f]*)(?[h-k]*)") + .matcher("abcdefhkhk"); + + assertTrue(m.matches()); + assertEquals("abcdef", m.group("first")); + assertEquals(0, m.start("first")); + assertEquals(6, m.end("first")); + + assertEquals("hkhk", m.group("second")); + assertEquals(6, m.start("second")); + assertEquals(10, m.end("second")); + + try { + m.group("third"); + fail(); + } catch (IllegalArgumentException expected) {} + + try { + Pattern.compile("(?<>[a-f]*)"); + fail(); + } catch(PatternSyntaxException expected) {} + } + + public void testNamedGroupBackreference() throws Exception { + Matcher m = Pattern.compile("(?[a-z]+)X\\k") + .matcher("foobarXfoobar"); + + assertTrue(m.matches()); + assertEquals("foobar", m.group("somegroup")); + assertEquals(0, m.start("somegroup")); + assertEquals(6, m.end("somegroup")); + + try { + Pattern.compile("\\k"); + fail(); + } catch(PatternSyntaxException expected) {} + } + + public void testNamedGroupReplace() throws Exception { + assertEquals("a0123zxx", "0123zxx".replaceAll("(?[0-9]+)", "a${numbers}")); + + // badly formatted replace string + try { + "0123zxx".replaceAll("(?[0-9]+)", "a${numbers"); + fail(); + } catch(IllegalArgumentException expected) {} + + // group that doesn't exist + try { + "0123zxx".replaceAll("(?[0-9]+)", "a${other}"); + fail(); + } catch(IllegalArgumentException expected) {} + } + } diff --git a/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java index 9e049c011..c4f41f687 100644 --- a/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java +++ b/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java @@ -34,11 +34,16 @@ import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; -import junit.framework.TestCase; - +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; import tests.support.resource.Support_Resources; -public abstract class AbstractZipFileTest extends TestCase { +public abstract class AbstractZipFileTest extends TestCaseWithRules { + @Rule + public TestRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule(); + /** * Exercise Inflater's ability to refill the zlib's input buffer. As of this * writing, this buffer's max size is 64KiB compressed bytes. We'll write a @@ -426,8 +431,9 @@ public void test_getComment_unset() throws Exception { out.putNextEntry(ze); out.close(); - ZipFile zipFile = new ZipFile(file); - assertEquals(null, zipFile.getComment()); + try (ZipFile zipFile = new ZipFile(file)) { + assertEquals(null, zipFile.getComment()); + } } // https://code.google.com/p/android/issues/detail?id=58465 @@ -458,6 +464,7 @@ public void testCrc() throws IOException { // setCrc takes a long, not an int, so -1 isn't a valid CRC32 (because it's 64 bits). try { ze.setCrc(-1); + fail(); } catch (IllegalArgumentException expected) { } diff --git a/luni/src/test/java/libcore/java/util/zip/DeflaterInputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/DeflaterInputStreamTest.java index 938b16e9b..505e017c8 100644 --- a/luni/src/test/java/libcore/java/util/zip/DeflaterInputStreamTest.java +++ b/luni/src/test/java/libcore/java/util/zip/DeflaterInputStreamTest.java @@ -23,9 +23,14 @@ import java.util.Arrays; import java.util.zip.DeflaterInputStream; import java.util.zip.InflaterInputStream; -import junit.framework.TestCase; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public final class DeflaterInputStreamTest extends TestCase { +public final class DeflaterInputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); public void testReadByteByByte() throws IOException { byte[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -50,14 +55,15 @@ public void testReadByteByByte() throws IOException { } public byte[] inflate(byte[] bytes) throws IOException { - java.io.InputStream in = new InflaterInputStream(new ByteArrayInputStream(bytes)); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int count; - while ((count = in.read(buffer)) != -1) { - out.write(buffer, 0, count); + try (InputStream in = new InflaterInputStream(new ByteArrayInputStream(bytes))) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int count; + while ((count = in.read(buffer)) != -1) { + out.write(buffer, 0, count); + } + return out.toByteArray(); } - return out.toByteArray(); } public void testReadWithBuffer() throws IOException { diff --git a/luni/src/test/java/libcore/java/util/zip/DeflaterOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/DeflaterOutputStreamTest.java index 6b25f08bf..80a855b2d 100644 --- a/luni/src/test/java/libcore/java/util/zip/DeflaterOutputStreamTest.java +++ b/luni/src/test/java/libcore/java/util/zip/DeflaterOutputStreamTest.java @@ -19,12 +19,11 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; @@ -34,9 +33,14 @@ import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.util.zip.InflaterInputStream; -import junit.framework.TestCase; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public class DeflaterOutputStreamTest extends TestCase { +public class DeflaterOutputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); public void testSyncFlushEnabled() throws Exception { InputStream in = createInflaterStream(DeflaterOutputStream.class, true); diff --git a/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java b/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java index 1dfa775d9..36b05b3f7 100644 --- a/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java +++ b/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java @@ -19,17 +19,36 @@ import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; -import junit.framework.TestCase; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public class DeflaterTest extends TestCase { +public class DeflaterTest extends TestCaseWithRules { + @Rule + public TestRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule(); private byte[] compressed = new byte[32]; private byte[] decompressed = new byte[20]; - private Deflater deflater = new Deflater(); - private Inflater inflater = new Inflater(); + private Deflater deflater; + private Inflater inflater; private int totalDeflated = 0; private int totalInflated = 0; + @Override + protected void setUp() throws Exception { + super.setUp(); + deflater = new Deflater(); + inflater = new Inflater(); + } + + @Override + protected void tearDown() throws Exception { + deflater.end(); + inflater.end(); + super.tearDown(); + } + public void testDeflate() throws DataFormatException { deflater.setInput(new byte[] { 1, 2, 3 }); deflateInflate(Deflater.NO_FLUSH); @@ -47,6 +66,8 @@ public void testDeflate() throws DataFormatException { assertEquals(9, totalInflated); assertDecompressed(1, 2, 3, 4, 5, 6, 7, 8, 9); assertEquals(0, inflater.inflate(decompressed)); + + inflater.end(); inflater = new Inflater(true); // safe because we did a FULL_FLUSH deflater.setInput(new byte[] { 10, 11, 12 }); diff --git a/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java index 8be80025b..d9f3fe462 100644 --- a/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java +++ b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java @@ -29,11 +29,21 @@ import java.util.Random; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import junit.framework.TestCase; import libcore.io.IoUtils; import libcore.io.Streams; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public final class GZIPInputStreamTest extends TestCase { +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + + +public final class GZIPInputStreamTest extends TestCaseWithRules { + @Rule + public TestRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule(); private static final byte[] HELLO_WORLD_GZIPPED = new byte[] { 31, -117, 8, 0, 0, 0, 0, 0, 0, 0, // 10 byte header @@ -192,19 +202,53 @@ public void testMultipleMembersWithCustomBufferSize() throws Exception { } } - public static byte[] gunzip(byte[] bytes) throws IOException { - ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - InputStream in = new GZIPInputStream(bis); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int count; - while ((count = in.read(buffer)) != -1) { - out.write(buffer, 0, count); + /** + * Test a openJdk8 fix for case where GZIPInputStream.readTrailer may accidently + * close the input stream if trailing bytes looks "close" enough. Wrapping GZIP in + * a ZipOutputStream will do that. */ + public void testNoCloseInReadTrailerDueToRead() throws IOException { + final int numBytes = 128; + byte[] data = new byte[numBytes]; + final boolean[] closedHolder = new boolean[]{false}; + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) { + zipOutputStream.putNextEntry(new ZipEntry("entry1")); + try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(zipOutputStream)) { + gzipOutputStream.write(data); + } } - byte[] outArray = out.toByteArray(); - in.close(); + final byte[] compressedData = byteArrayOutputStream.toByteArray(); + InputStream byteArrayInputStream = + new ByteArrayInputStream(compressedData) { + @Override + public void close() throws IOException { + closedHolder[0] = true; + } + }; + + try (ZipInputStream zipInputStream = new ZipInputStream(byteArrayInputStream)) { + zipInputStream.getNextEntry(); + try (InputStream in = new GZIPInputStream(zipInputStream)) { + assertEquals(numBytes, in.skip(numBytes+1)); + assertFalse(closedHolder[0]); + } + assertTrue(closedHolder[0]); + } + } - return outArray; + public static byte[] gunzip(byte[] bytes) throws IOException { + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + try (InputStream in = new GZIPInputStream(bis)) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int count; + while ((count = in.read(buffer)) != -1) { + out.write(buffer, 0, count); + } + + return out.toByteArray(); + } } } diff --git a/luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java index 3b785c94d..1da59fd5b 100644 --- a/luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java +++ b/luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java @@ -16,7 +16,6 @@ package libcore.java.util.zip; -import junit.framework.TestCase; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -24,8 +23,15 @@ import java.util.Arrays; import java.util.Random; import java.util.zip.GZIPOutputStream; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public final class GZIPOutputStreamTest extends TestCaseWithRules { + @Rule + public TestRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule(); -public final class GZIPOutputStreamTest extends TestCase { public void testShortMessage() throws IOException { byte[] data = gzip(("Hello World").getBytes("UTF-8")); assertEquals("[31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -13, 72, -51, -55, -55, 87, 8, -49, " + diff --git a/luni/src/test/java/libcore/java/util/zip/InflaterTest.java b/luni/src/test/java/libcore/java/util/zip/InflaterTest.java index cce08f38c..9d8ac387a 100644 --- a/luni/src/test/java/libcore/java/util/zip/InflaterTest.java +++ b/luni/src/test/java/libcore/java/util/zip/InflaterTest.java @@ -20,9 +20,15 @@ import java.util.zip.Adler32; import java.util.zip.Deflater; import java.util.zip.Inflater; -import junit.framework.TestCase; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public class InflaterTest extends TestCaseWithRules { + @Rule + public TestRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule(); -public class InflaterTest extends TestCase { public void testDefaultDictionary() throws Exception { assertRoundTrip(null); } @@ -98,22 +104,26 @@ public void testEmptyFileAndEmptyBuffer() throws Exception { assertFalse(inflater.finished()); assertEquals(0, inflater.inflate(new byte[0], 0, 0)); assertTrue(inflater.finished()); + inflater.end(); } private static byte[] deflate(byte[] input, byte[] dictionary) { Deflater deflater = new Deflater(); - if (dictionary != null) { - deflater.setDictionary(dictionary); - } - deflater.setInput(input); - deflater.finish(); ByteArrayOutputStream deflatedBytes = new ByteArrayOutputStream(); - byte[] buf = new byte[8]; - while (!deflater.finished()) { - int byteCount = deflater.deflate(buf); - deflatedBytes.write(buf, 0, byteCount); + try { + if (dictionary != null) { + deflater.setDictionary(dictionary); + } + deflater.setInput(input); + deflater.finish(); + byte[] buf = new byte[8]; + while (!deflater.finished()) { + int byteCount = deflater.deflate(buf); + deflatedBytes.write(buf, 0, byteCount); + } + } finally { + deflater.end(); } - deflater.end(); return deflatedBytes.toByteArray(); } @@ -125,9 +135,8 @@ private static int adler32(byte[] bytes) { public void testInflaterCounts() throws Exception { Inflater inflater = new Inflater(); - byte[] decompressed = new byte[32]; - byte[] compressed = deflate(new byte[] { 1, 2, 3}, null); + byte[] compressed = deflate(new byte[] { 1, 2, 3 }, null); assertEquals(11, compressed.length); // Feed in bytes [0, 5) to the first iteration. @@ -152,5 +161,6 @@ public void testInflaterCounts() throws Exception { assertEquals(0, inflater.getTotalIn()); assertEquals(0, inflater.getBytesWritten()); assertEquals(0, inflater.getTotalOut()); + inflater.end(); } } diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java index 02210ac1b..2175289d6 100644 --- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java +++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java @@ -16,7 +16,19 @@ package libcore.java.util.zip; +import android.system.OsConstants; +import libcore.io.Libcore; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; import java.io.OutputStream; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; public final class ZipFileTest extends AbstractZipFileTest { @@ -25,4 +37,52 @@ public final class ZipFileTest extends AbstractZipFileTest { protected ZipOutputStream createZipOutputStream(OutputStream wrapped) { return new ZipOutputStream(wrapped); } + + // http://b/30407219 + public void testZipFileOffsetNeverChangesAfterInit() throws Exception { + final File f = createTemporaryZipFile(); + writeEntries(createZipOutputStream(new BufferedOutputStream(new FileOutputStream(f))), + 2 /* number of entries */, 1024 /* entry size */, true /* setEntrySize */); + + ZipFile zipFile = new ZipFile(f); + FileDescriptor fd = new FileDescriptor(); + fd.setInt$(zipFile.getFileDescriptor()); + + long initialOffset = android.system.Os.lseek(fd, 0, OsConstants.SEEK_CUR); + + Enumeration entries = zipFile.entries(); + assertOffset(initialOffset, fd); + + // Get references to the two elements in the file. + ZipEntry entry1 = entries.nextElement(); + ZipEntry entry2 = entries.nextElement(); + assertFalse(entries.hasMoreElements()); + assertOffset(initialOffset, fd); + + InputStream is1 = zipFile.getInputStream(entry1); + assertOffset(initialOffset, fd); + is1.read(new byte[256]); + assertOffset(initialOffset, fd); + is1.close(); + + assertNotNull(zipFile.getEntry(entry2.getName())); + assertOffset(initialOffset, fd); + + zipFile.close(); + } + + private static void assertOffset(long initialOffset, FileDescriptor fd) throws Exception { + long currentOffset = android.system.Os.lseek(fd, 0, OsConstants.SEEK_CUR); + assertEquals(initialOffset, currentOffset); + } + + // b/31077136 + public void test_FileNotFound() throws Exception { + File nonExistentFile = new File("fileThatDefinitelyDoesntExist.zip"); + assertFalse(nonExistentFile.exists()); + + try (ZipFile zipFile = new ZipFile(nonExistentFile, ZipFile.OPEN_READ)) { + fail(); + } catch(FileNotFoundException expected) {} + } } diff --git a/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java index 1dc22ca30..8485874bb 100644 --- a/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java +++ b/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java @@ -16,31 +16,26 @@ package libcore.java.util.zip; +import libcore.io.Streams; +import tests.support.resource.Support_Resources; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; -import java.util.List; import java.util.Random; -import java.util.Set; import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -import junit.framework.TestCase; - -import tests.support.resource.Support_Resources; - -public final class ZipInputStreamTest extends TestCase { +public final class ZipInputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); public void testShortMessage() throws IOException { byte[] data = "Hello World".getBytes("UTF-8"); @@ -115,4 +110,51 @@ public void testReadOnIncompleteStream() throws Exception { zi.close(); } + + public void testAvailable() throws Exception { + // NOTE: We don't care about the contents of any of these entries as long as they're + // not empty. + ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream( + zip(new String[] { "foo", "bar", "baz" }, new byte[] { 0, 0, 0, 1, 1, 1 }))); + + assertEquals(1, zis.available()); + zis.getNextEntry(); + assertEquals(1, zis.available()); + zis.closeEntry(); + // On Android M and below, this call would return "1". That seems a bit odd given that the + // contract for available states that we should return 1 if there are any bytes left to read + // from the "current" entry. + assertEquals(0, zis.available()); + + // There shouldn't be any bytes left to read if the entry is fully consumed... + zis.getNextEntry(); + Streams.readFullyNoClose(zis); + assertEquals(0, zis.available()); + + // ... or if the entry is fully skipped over. + zis.getNextEntry(); + zis.skip(Long.MAX_VALUE); + assertEquals(0, zis.available()); + + // There are no entries left in the file, so there whould be nothing left to read. + assertNull(zis.getNextEntry()); + assertEquals(0, zis.available()); + + zis.close(); + } + + private static byte[] zip(String[] names, byte[] bytes) throws IOException { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + ZipOutputStream zippedOut = new ZipOutputStream(bytesOut); + + for (String name : names) { + ZipEntry entry = new ZipEntry(name); + zippedOut.putNextEntry(entry); + zippedOut.write(bytes); + zippedOut.closeEntry(); + } + + zippedOut.close(); + return bytesOut.toByteArray(); + } } diff --git a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java index 15600de98..4e7287482 100644 --- a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java +++ b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java @@ -26,9 +26,16 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipOutputStream; -import junit.framework.TestCase; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import libcore.junit.util.ResourceLeakageDetector.DisableResourceLeakageDetection; +import org.junit.Rule; +import org.junit.rules.TestRule; + +public final class ZipOutputStreamTest extends TestCaseWithRules { + @Rule + public TestRule guardRule = ResourceLeakageDetector.getRule(); -public final class ZipOutputStreamTest extends TestCase { public void testShortMessage() throws IOException { byte[] data = "Hello World".getBytes("UTF-8"); byte[] zipped = zip("short", data); @@ -65,6 +72,10 @@ public static byte[] zip(String name, byte[] bytes) throws IOException { * Reference implementation does NOT allow writing of an empty zip using a * {@link ZipOutputStream}. */ + @DisableResourceLeakageDetection( + why = "InflaterOutputStream.close() does not work properly if finish() throws an" + + " exception; finish() throws an exception if the output is invalid.", + bug = "31797037") public void testCreateEmpty() throws IOException { File result = File.createTempFile("ZipFileTest", "zip"); ZipOutputStream out = @@ -79,11 +90,12 @@ public void testCreateEmpty() throws IOException { /** Regression test for null comment causing a NullPointerException during write. */ public void testNullComment() throws IOException { - ZipOutputStream out = new ZipOutputStream(new ByteArrayOutputStream()); - out.setComment(null); - out.putNextEntry(new ZipEntry("name")); - out.write(new byte[1]); - out.closeEntry(); - out.finish(); + try (ZipOutputStream out = new ZipOutputStream(new ByteArrayOutputStream())) { + out.setComment(null); + out.putNextEntry(new ZipEntry("name")); + out.write(new byte[1]); + out.closeEntry(); + out.finish(); + } } } diff --git a/luni/src/test/java/libcore/javax/crypto/CipherInputStreamTest.java b/luni/src/test/java/libcore/javax/crypto/CipherInputStreamTest.java index 67ed36c56..7d6a6ffa3 100644 --- a/luni/src/test/java/libcore/javax/crypto/CipherInputStreamTest.java +++ b/luni/src/test/java/libcore/javax/crypto/CipherInputStreamTest.java @@ -16,19 +16,32 @@ package libcore.javax.crypto; +import junit.framework.TestCase; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Method; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; import java.security.spec.AlgorithmParameterSpec; import java.util.Arrays; +import javax.crypto.AEADBadTagException; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; +import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import junit.framework.TestCase; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; public final class CipherInputStreamTest extends TestCase { @@ -203,4 +216,148 @@ public void testCipherInputStream_NullInputStream_Failure() throws Exception { } catch (NullPointerException expected) { } } + + public void testCloseTwice() throws Exception { + InputStream mockIs = mock(InputStream.class); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + + CipherInputStream cis = new CipherInputStream(mockIs, cipher); + cis.close(); + cis.close(); + + verify(mockIs, times(1)).close(); + } + + /** + * CipherSpi that increments it's engineGetOutputSize output when + * engineUpdate is called. + */ + public static class CipherSpiWithGrowingOutputSize extends MockCipherSpi { + private int outputSizeDelta = 0; + + @Override + protected int engineGetOutputSize(int inputLen) { + return inputLen + outputSizeDelta; + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) throws ShortBufferException { + int expectedOutputSize = inputLen + outputSizeDelta++; + if ((output.length - outputOffset) < expectedOutputSize) { + throw new ShortBufferException(); + } + return expectedOutputSize; + } + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + int expectedOutputSize = inputLen + outputSizeDelta++; + return new byte[expectedOutputSize]; + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) { + return input; + } + } + + private static class MockProvider extends Provider { + public MockProvider() { + super("MockProvider", 1.0, "Mock provider used for testing"); + put("Cipher.GrowingOutputSize", + CipherSpiWithGrowingOutputSize.class.getName()); + } + } + + // http://b/32643789, check that CipherSpi.engineGetOutputSize is called and applied + // to output buffer size before calling CipherSpi.egineUpdate(byte[],int,int,byte[],int). + public void testCipherOutputSizeChange() throws Exception { + Provider mockProvider = new MockProvider(); + + Cipher cipher = Cipher.getInstance("GrowingOutputSize", mockProvider); + + cipher.init(Cipher.DECRYPT_MODE, key, iv); + InputStream mockEncryptedInputStream = new ByteArrayInputStream(new byte[1024]); + try (InputStream is = new CipherInputStream(mockEncryptedInputStream, cipher)) { + byte[] buffer = new byte[1024]; + // engineGetOutputSize returns 512+0, engineUpdate expects buf >= 512 + assertEquals(512, is.read(buffer)); + // engineGetOutputSize returns 512+1, engineUpdate expects buf >= 513 + // and will throw ShortBufferException buffer is smaller. + assertEquals(513, is.read(buffer)); + } + } + + // From b/31590622. CipherInputStream had a bug where it would ignore exceptions + // thrown during close(), because it was expecting exceptions to be thrown by read(). + public void testDecryptCorruptGCM() throws Exception { + for (Provider provider : Security.getProviders()) { + Cipher cipher; + try { + cipher = Cipher.getInstance("AES/GCM/NoPadding", provider); + } catch (NoSuchAlgorithmException e) { + continue; + } + SecretKey key; + if (provider.getName().equals("AndroidKeyStoreBCWorkaround")) { + key = getAndroidKeyStoreSecretKey(); + } else { + KeyGenerator keygen = KeyGenerator.getInstance("AES"); + keygen.init(256); + key = keygen.generateKey(); + } + GCMParameterSpec params = new GCMParameterSpec(128, new byte[12]); + byte[] unencrypted = new byte[200]; + + // Normal providers require specifying the IV, but KeyStore prohibits it, so + // we have to special-case it + if (provider.getName().equals("AndroidKeyStoreBCWorkaround")) { + cipher.init(Cipher.ENCRYPT_MODE, key); + } else { + cipher.init(Cipher.ENCRYPT_MODE, key, params); + } + byte[] encrypted = cipher.doFinal(unencrypted); + + // Corrupt the final byte, which will corrupt the authentication tag + encrypted[encrypted.length - 1] ^= 1; + + cipher.init(Cipher.DECRYPT_MODE, key, params); + CipherInputStream cis = new CipherInputStream( + new ByteArrayInputStream(encrypted), cipher); + try { + cis.read(unencrypted); + cis.close(); + fail("Reading a corrupted stream should throw an exception." + + " Provider: " + provider); + } catch (IOException expected) { + assertTrue(expected.getCause() instanceof AEADBadTagException); + } + } + + } + + // The AndroidKeyStoreBCWorkaround provider can't use keys created by anything + // but Android KeyStore, which requires using its own parameters class to create + // keys. Since we're in javax, we can't link against the frameworks classes, so + // we have to use reflection to make a suitable key. This will always be safe + // because if we're making a key for AndroidKeyStoreBCWorkaround, the KeyStore + // classes must be present. + private static SecretKey getAndroidKeyStoreSecretKey() throws Exception { + KeyGenerator keygen = KeyGenerator.getInstance("AES", "AndroidKeyStore"); + Class keyParamsBuilderClass = keygen.getClass().getClassLoader().loadClass( + "android.security.keystore.KeyGenParameterSpec$Builder"); + Object keyParamsBuilder = keyParamsBuilderClass.getConstructor(String.class, Integer.TYPE) + // 3 is PURPOSE_ENCRYPT | PURPOSE_DECRYPT + .newInstance("testDecryptCorruptGCM", 3); + keyParamsBuilderClass.getMethod("setBlockModes", new Class[]{String[].class}) + .invoke(keyParamsBuilder, new Object[]{new String[]{"GCM"}}); + keyParamsBuilderClass.getMethod("setEncryptionPaddings", new Class[]{String[].class}) + .invoke(keyParamsBuilder, new Object[]{new String[]{"NoPadding"}}); + AlgorithmParameterSpec spec = (AlgorithmParameterSpec) + keyParamsBuilderClass.getMethod("build", new Class[]{}).invoke(keyParamsBuilder); + keygen.init(spec); + return keygen.generateKey(); + } } diff --git a/luni/src/test/java/libcore/javax/crypto/CipherOutputStreamTest.java b/luni/src/test/java/libcore/javax/crypto/CipherOutputStreamTest.java new file mode 100644 index 000000000..dd9a9a000 --- /dev/null +++ b/luni/src/test/java/libcore/javax/crypto/CipherOutputStreamTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017 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. + */ + +package libcore.javax.crypto; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.AEADBadTagException; +import javax.crypto.Cipher; +import javax.crypto.CipherOutputStream; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; + +public final class CipherOutputStreamTest extends TestCase { + + // From b/36636576. CipherOutputStream had a bug where it would ignore exceptions + // thrown during close(). + public void testDecryptCorruptGCM() throws Exception { + for (Provider provider : Security.getProviders()) { + Cipher cipher; + try { + cipher = Cipher.getInstance("AES/GCM/NoPadding", provider); + } catch (NoSuchAlgorithmException e) { + continue; + } + SecretKey key; + if (provider.getName().equals("AndroidKeyStoreBCWorkaround")) { + key = getAndroidKeyStoreSecretKey(); + } else { + KeyGenerator keygen = KeyGenerator.getInstance("AES"); + keygen.init(256); + key = keygen.generateKey(); + } + GCMParameterSpec params = new GCMParameterSpec(128, new byte[12]); + byte[] unencrypted = new byte[200]; + + // Normal providers require specifying the IV, but KeyStore prohibits it, so + // we have to special-case it + if (provider.getName().equals("AndroidKeyStoreBCWorkaround")) { + cipher.init(Cipher.ENCRYPT_MODE, key); + } else { + cipher.init(Cipher.ENCRYPT_MODE, key, params); + } + byte[] encrypted = cipher.doFinal(unencrypted); + + // Corrupt the final byte, which will corrupt the authentication tag + encrypted[encrypted.length - 1] ^= 1; + + cipher.init(Cipher.DECRYPT_MODE, key, params); + CipherOutputStream cos = new CipherOutputStream(new ByteArrayOutputStream(), cipher); + try { + cos.write(encrypted); + cos.close(); + fail("Writing a corrupted stream should throw an exception." + + " Provider: " + provider); + } catch (IOException expected) { + assertTrue(expected.getCause() instanceof AEADBadTagException); + } + } + + } + + // The AndroidKeyStoreBCWorkaround provider can't use keys created by anything + // but Android KeyStore, which requires using its own parameters class to create + // keys. Since we're in javax, we can't link against the frameworks classes, so + // we have to use reflection to make a suitable key. This will always be safe + // because if we're making a key for AndroidKeyStoreBCWorkaround, the KeyStore + // classes must be present. + private static SecretKey getAndroidKeyStoreSecretKey() throws Exception { + KeyGenerator keygen = KeyGenerator.getInstance("AES", "AndroidKeyStore"); + Class keyParamsBuilderClass = keygen.getClass().getClassLoader().loadClass( + "android.security.keystore.KeyGenParameterSpec$Builder"); + Object keyParamsBuilder = keyParamsBuilderClass.getConstructor(String.class, Integer.TYPE) + // 3 is PURPOSE_ENCRYPT | PURPOSE_DECRYPT + .newInstance("testDecryptCorruptGCM", 3); + keyParamsBuilderClass.getMethod("setBlockModes", new Class[]{String[].class}) + .invoke(keyParamsBuilder, new Object[]{new String[]{"GCM"}}); + keyParamsBuilderClass.getMethod("setEncryptionPaddings", new Class[]{String[].class}) + .invoke(keyParamsBuilder, new Object[]{new String[]{"NoPadding"}}); + AlgorithmParameterSpec spec = (AlgorithmParameterSpec) + keyParamsBuilderClass.getMethod("build", new Class[]{}).invoke(keyParamsBuilder); + keygen.init(spec); + return keygen.generateKey(); + } +} diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java index 4ce883baa..2b3347c72 100644 --- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java +++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java @@ -29,7 +29,6 @@ import java.security.Key; import java.security.KeyFactory; import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; @@ -37,7 +36,9 @@ import java.security.Security; import java.security.cert.Certificate; import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.ArrayList; import java.util.Arrays; @@ -58,8 +59,10 @@ import javax.crypto.ShortBufferException; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.OAEPParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.PSource; import javax.crypto.spec.SecretKeySpec; import junit.framework.TestCase; import libcore.java.security.StandardNames; @@ -154,6 +157,9 @@ private static String getBaseAlgorithm(String algorithm) { if (algorithm.startsWith("AES/")) { return "AES"; } + if (algorithm.startsWith("AES_128/") || algorithm.startsWith("AES_256/")) { + return "AES"; + } if (algorithm.equals("GCM")) { return "AES"; } @@ -244,42 +250,70 @@ private static boolean isStreamMode(String algorithm) { || algorithm.contains("/CFB"); } + private static boolean isRandomizedEncryption(String algorithm) { + return algorithm.endsWith("/PKCS1PADDING") || algorithm.endsWith("/OAEPPADDING") + || algorithm.contains("/OAEPWITH"); + } + private static Map ENCRYPT_KEYS = new HashMap(); - private synchronized static Key getEncryptKey(String algorithm) throws Exception { + + /** + * Returns the key meant for enciphering for {@code algorithm}. + */ + private synchronized static Key getEncryptKey(String algorithm) { Key key = ENCRYPT_KEYS.get(algorithm); if (key != null) { return key; } - if (algorithm.startsWith("RSA")) { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus, - RSA_2048_privateExponent); - key = kf.generatePrivate(keySpec); - } else if (isPBE(algorithm)) { - SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm); - key = skf.generateSecret(new PBEKeySpec("secret".toCharArray())); - } else { - KeyGenerator kg = KeyGenerator.getInstance(getBaseAlgorithm(algorithm)); - key = kg.generateKey(); + try { + if (algorithm.startsWith("RSA")) { + KeyFactory kf = KeyFactory.getInstance("RSA"); + RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, + RSA_2048_publicExponent); + key = kf.generatePublic(keySpec); + } else if (isPBE(algorithm)) { + SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm); + key = skf.generateSecret(new PBEKeySpec("secret".toCharArray())); + } else { + KeyGenerator kg = KeyGenerator.getInstance(getBaseAlgorithm(algorithm)); + if (algorithm.startsWith("AES_256/")) { + // This is the 256-bit constrained version, so we have to switch from the + // default of 128-bit keys. + kg.init(256); + } + key = kg.generateKey(); + } + } catch (Exception e) { + throw new AssertionError("Error generating keys for test setup", e); } ENCRYPT_KEYS.put(algorithm, key); return key; } private static Map DECRYPT_KEYS = new HashMap(); - private synchronized static Key getDecryptKey(String algorithm) throws Exception { + + /** + * Returns the key meant for deciphering for {@code algorithm}. + */ + private synchronized static Key getDecryptKey(String algorithm) { Key key = DECRYPT_KEYS.get(algorithm); if (key != null) { return key; } - if (algorithm.startsWith("RSA")) { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, - RSA_2048_publicExponent); - key = kf.generatePublic(keySpec); - } else { - assertFalse(algorithm, isAsymmetric(algorithm)); - key = getEncryptKey(algorithm); + try { + if (algorithm.startsWith("RSA")) { + KeyFactory kf = KeyFactory.getInstance("RSA"); + RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(RSA_2048_modulus, + RSA_2048_publicExponent, RSA_2048_privateExponent, RSA_2048_primeP, + RSA_2048_primeQ, RSA_2048_primeExponentP, RSA_2048_primeExponentQ, + RSA_2048_crtCoefficient); + key = kf.generatePrivate(keySpec); + } else { + assertFalse(algorithm, isAsymmetric(algorithm)); + key = getEncryptKey(algorithm); + } + } catch (Exception e) { + throw new AssertionError("Error generating keys for test setup", e); } DECRYPT_KEYS.put(algorithm, key); return key; @@ -307,6 +341,20 @@ private synchronized static Key getDecryptKey(String algorithm) throws Exception setExpectedBlockSize("AES/OFB/PKCS5PADDING", 16); setExpectedBlockSize("AES/OFB/PKCS7PADDING", 16); setExpectedBlockSize("AES/OFB/NOPADDING", 16); + setExpectedBlockSize("AES_128/CBC/PKCS5PADDING", 16); + setExpectedBlockSize("AES_128/CBC/PKCS7PADDING", 16); + setExpectedBlockSize("AES_128/CBC/NOPADDING", 16); + setExpectedBlockSize("AES_128/ECB/PKCS5PADDING", 16); + setExpectedBlockSize("AES_128/ECB/PKCS7PADDING", 16); + setExpectedBlockSize("AES_128/ECB/NOPADDING", 16); + setExpectedBlockSize("AES_128/GCM/NOPADDING", 16); + setExpectedBlockSize("AES_256/CBC/PKCS5PADDING", 16); + setExpectedBlockSize("AES_256/CBC/PKCS7PADDING", 16); + setExpectedBlockSize("AES_256/CBC/NOPADDING", 16); + setExpectedBlockSize("AES_256/ECB/PKCS5PADDING", 16); + setExpectedBlockSize("AES_256/ECB/PKCS7PADDING", 16); + setExpectedBlockSize("AES_256/ECB/NOPADDING", 16); + setExpectedBlockSize("AES_256/GCM/NOPADDING", 16); setExpectedBlockSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16); setExpectedBlockSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16); setExpectedBlockSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16); @@ -381,6 +429,21 @@ private synchronized static Key getDecryptKey(String algorithm) throws Exception setExpectedBlockSize("RSA", Cipher.DECRYPT_MODE, 256); setExpectedBlockSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, 256); setExpectedBlockSize("RSA/ECB/PKCS1Padding", Cipher.DECRYPT_MODE, 256); + + // OAEP padding modes change the output and block size. SHA-1 is the default. + setExpectedBlockSize("RSA/ECB/OAEPPadding", Cipher.ENCRYPT_MODE, 214); + setExpectedBlockSize("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", Cipher.ENCRYPT_MODE, 214); + setExpectedBlockSize("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", Cipher.ENCRYPT_MODE, 198); + setExpectedBlockSize("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", Cipher.ENCRYPT_MODE, 190); + setExpectedBlockSize("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", Cipher.ENCRYPT_MODE, 158); + setExpectedBlockSize("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", Cipher.ENCRYPT_MODE, 126); + + setExpectedBlockSize("RSA/ECB/OAEPPadding", Cipher.DECRYPT_MODE, 256); + setExpectedBlockSize("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", Cipher.DECRYPT_MODE, 256); + setExpectedBlockSize("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", Cipher.DECRYPT_MODE, 256); + setExpectedBlockSize("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", Cipher.DECRYPT_MODE, 256); + setExpectedBlockSize("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", Cipher.DECRYPT_MODE, 256); + setExpectedBlockSize("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", Cipher.DECRYPT_MODE, 256); } } @@ -449,6 +512,10 @@ private static int getExpectedBlockSize(String algorithm, int mode, String provi setExpectedOutputSize("AES/CTS/NOPADDING", 0); setExpectedOutputSize("AES/ECB/NOPADDING", 0); setExpectedOutputSize("AES/OFB/NOPADDING", 0); + setExpectedOutputSize("AES_128/CBC/NOPADDING", 0); + setExpectedOutputSize("AES_128/ECB/NOPADDING", 0); + setExpectedOutputSize("AES_256/CBC/NOPADDING", 0); + setExpectedOutputSize("AES_256/ECB/NOPADDING", 0); setExpectedOutputSize("AES", Cipher.ENCRYPT_MODE, 16); setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16); @@ -464,6 +531,16 @@ private static int getExpectedBlockSize(String algorithm, int mode, String provi setExpectedOutputSize("AES/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8); setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16); setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16); + setExpectedOutputSize("AES_128/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16); + setExpectedOutputSize("AES_128/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16); + setExpectedOutputSize("AES_128/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16); + setExpectedOutputSize("AES_128/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16); + setExpectedOutputSize("AES_128/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8); + setExpectedOutputSize("AES_256/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16); + setExpectedOutputSize("AES_256/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16); + setExpectedOutputSize("AES_256/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16); + setExpectedOutputSize("AES_256/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16); + setExpectedOutputSize("AES_256/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8); setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16); setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16); setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16); @@ -497,6 +574,16 @@ private static int getExpectedBlockSize(String algorithm, int mode, String provi setExpectedOutputSize("AES/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0); setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0); setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("AES_128/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("AES_128/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("AES_128/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("AES_128/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("AES_128/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("AES_256/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("AES_256/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("AES_256/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("AES_256/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("AES_256/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0); setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0); setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0); setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0); @@ -589,6 +676,7 @@ private static int getExpectedBlockSize(String algorithm, int mode, String provi setExpectedOutputSize("RSA", Cipher.DECRYPT_MODE, 256); setExpectedOutputSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, 256); setExpectedOutputSize("RSA/ECB/PKCS1Padding", Cipher.DECRYPT_MODE, 245); + setExpectedOutputSize("RSA/ECB/OAEPPadding", Cipher.DECRYPT_MODE, 256); // SunJCE returns the full for size even when PKCS1Padding is specified setExpectedOutputSize("RSA/ECB/PKCS1Padding", Cipher.DECRYPT_MODE, "SunJCE", 256); @@ -596,6 +684,21 @@ private static int getExpectedBlockSize(String algorithm, int mode, String provi // BC strips the leading 0 for us even when NoPadding is specified setExpectedOutputSize("RSA", Cipher.DECRYPT_MODE, "BC", 255); setExpectedOutputSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, "BC", 255); + + // OAEP padding modes change the output and block size. SHA-1 is the default. + setExpectedOutputSize("RSA/ECB/OAEPPadding", Cipher.DECRYPT_MODE, 214); + setExpectedOutputSize("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", Cipher.DECRYPT_MODE, 214); + setExpectedOutputSize("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", Cipher.DECRYPT_MODE, 198); + setExpectedOutputSize("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", Cipher.DECRYPT_MODE, 190); + setExpectedOutputSize("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", Cipher.DECRYPT_MODE, 158); + setExpectedOutputSize("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", Cipher.DECRYPT_MODE, 126); + + setExpectedOutputSize("RSA/ECB/OAEPPadding", Cipher.ENCRYPT_MODE, 256); + setExpectedOutputSize("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", Cipher.ENCRYPT_MODE, 256); + setExpectedOutputSize("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", Cipher.ENCRYPT_MODE, 256); + setExpectedOutputSize("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", Cipher.ENCRYPT_MODE, 256); + setExpectedOutputSize("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", Cipher.ENCRYPT_MODE, 256); + setExpectedOutputSize("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", Cipher.ENCRYPT_MODE, 256); } private static void setExpectedOutputSize(String algorithm, int value) { @@ -714,7 +817,11 @@ private static byte[] getActualPlainText(String algorithm) { if (algorithm.equals("AES") || algorithm.equals("AES/CBC/NOPADDING") || algorithm.equals("AES/CTS/NOPADDING") - || algorithm.equals("AES/ECB/NOPADDING")) { + || algorithm.equals("AES/ECB/NOPADDING") + || algorithm.equals("AES_128/CBC/NOPADDING") + || algorithm.equals("AES_128/ECB/NOPADDING") + || algorithm.equals("AES_256/CBC/NOPADDING") + || algorithm.equals("AES_256/ECB/NOPADDING")) { return SIXTEEN_BYTE_BLOCK_PLAIN_TEXT; } if (algorithm.equals("DESEDE") @@ -730,7 +837,11 @@ private static byte[] getExpectedPlainText(String algorithm, String provider) { if (algorithm.equals("AES") || algorithm.equals("AES/CBC/NOPADDING") || algorithm.equals("AES/CTS/NOPADDING") - || algorithm.equals("AES/ECB/NOPADDING")) { + || algorithm.equals("AES/ECB/NOPADDING") + || algorithm.equals("AES_128/CBC/NOPADDING") + || algorithm.equals("AES_128/ECB/NOPADDING") + || algorithm.equals("AES_256/CBC/NOPADDING") + || algorithm.equals("AES_256/ECB/NOPADDING")) { return SIXTEEN_BYTE_BLOCK_PLAIN_TEXT; } if (algorithm.equals("DESEDE") @@ -751,7 +862,9 @@ private static AlgorithmParameterSpec getEncryptAlgorithmParameterSpec(String al new SecureRandom().nextBytes(salt); return new PBEParameterSpec(salt, 1024); } - if (algorithm.equals("AES/GCM/NOPADDING")) { + if (algorithm.equals("AES/GCM/NOPADDING") + || algorithm.equals("AES_128/GCM/NOPADDING") + || algorithm.equals("AES_256/GCM/NOPADDING")) { final byte[] iv = new byte[12]; new SecureRandom().nextBytes(iv); return new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv); @@ -762,7 +875,13 @@ private static AlgorithmParameterSpec getEncryptAlgorithmParameterSpec(String al || algorithm.equals("AES/CFB/NOPADDING") || algorithm.equals("AES/CTR/NOPADDING") || algorithm.equals("AES/CTS/NOPADDING") - || algorithm.equals("AES/OFB/NOPADDING")) { + || algorithm.equals("AES/OFB/NOPADDING") + || algorithm.equals("AES_128/CBC/NOPADDING") + || algorithm.equals("AES_128/CBC/PKCS5PADDING") + || algorithm.equals("AES_128/CBC/PKCS7PADDING") + || algorithm.equals("AES_256/CBC/NOPADDING") + || algorithm.equals("AES_256/CBC/PKCS5PADDING") + || algorithm.equals("AES_256/CBC/PKCS7PADDING")) { final byte[] iv = new byte[16]; new SecureRandom().nextBytes(iv); return new IvParameterSpec(iv); @@ -792,7 +911,9 @@ private static AlgorithmParameterSpec getDecryptAlgorithmParameterSpec(Algorithm } byte[] iv = encryptCipher.getIV(); if (iv != null) { - if ("AES/GCM/NOPADDING".equals(algorithm)) { + if ("AES/GCM/NOPADDING".equals(algorithm) + || "AES_128/GCM/NOPADDING".equals(algorithm) + || "AES_256/GCM/NOPADDING".equals(algorithm)) { return new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv); } return new IvParameterSpec(iv); @@ -1212,7 +1333,9 @@ public void test_getInstance() throws Exception { seenBaseCipherNames.add(algorithm); } else { final String baseCipherName = algorithm.substring(0, firstSlash); - if (!seenBaseCipherNames.contains(baseCipherName)) { + if (!seenBaseCipherNames.contains(baseCipherName) + && !(baseCipherName.equals("AES_128") + || baseCipherName.equals("AES_256"))) { seenCiphersWithModeAndPadding.add(baseCipherName); } if (!"AndroidOpenSSL".equals(provider.getName())) { @@ -1301,10 +1424,11 @@ private void test_Cipher(Cipher c) throws Exception { try { c.getOutputSize(0); + fail(); } catch (IllegalStateException expected) { } - // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs JCERSAPrivateKey) + // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs BCRSAPrivateKey) Key encryptKey = getEncryptKey(algorithm); final AlgorithmParameterSpec encryptSpec = getEncryptAlgorithmParameterSpec(algorithm); @@ -1366,36 +1490,14 @@ && isStreamMode(algorithm)) { } AlgorithmParameters encParams = c.getParameters(); - if (encryptSpec == null) { - assertNull(cipherID + " getParameters()", encParams); - } else if (encryptSpec instanceof GCMParameterSpec) { - GCMParameterSpec gcmDecryptSpec = (GCMParameterSpec) encParams - .getParameterSpec(GCMParameterSpec.class); - assertEquals(cipherID + " getIV()", - Arrays.toString(((GCMParameterSpec) encryptSpec).getIV()), - Arrays.toString(gcmDecryptSpec.getIV())); - assertEquals(cipherID + " getTLen()", ((GCMParameterSpec) encryptSpec).getTLen(), - gcmDecryptSpec.getTLen()); - } else if (encryptSpec instanceof IvParameterSpec) { - IvParameterSpec ivDecryptSpec = (IvParameterSpec) encParams - .getParameterSpec(IvParameterSpec.class); - assertEquals(cipherID + " getIV()", - Arrays.toString(((IvParameterSpec) encryptSpec).getIV()), - Arrays.toString(ivDecryptSpec.getIV())); - } else if (encryptSpec instanceof PBEParameterSpec) { - // Bouncycastle seems to be undecided about whether it returns this - // or not - if (!"BC".equals(providerName)) { - assertNotNull(cipherID + " getParameters()", encParams); - } - } + assertCorrectAlgorithmParameters(providerName, cipherID, encryptSpec, encParams); final AlgorithmParameterSpec decryptSpec = getDecryptAlgorithmParameterSpec(encryptSpec, c); int decryptMode = getDecryptMode(algorithm); test_Cipher_init_Decrypt_NullParameters(c, decryptMode, encryptKey, decryptSpec != null); - c.init(decryptMode, encryptKey, decryptSpec); + c.init(decryptMode, getDecryptKey(algorithm), decryptSpec); assertEquals(cipherID + " getBlockSize() decryptMode", getExpectedBlockSize(algorithm, decryptMode, providerName), c.getBlockSize()); assertEquals(cipherID + " getOutputSize(0) decryptMode", @@ -1428,35 +1530,13 @@ && isStreamMode(algorithm)) { } AlgorithmParameters decParams = c.getParameters(); - if (decryptSpec == null) { - assertNull(cipherID + " getParameters()", decParams); - } else if (decryptSpec instanceof GCMParameterSpec) { - GCMParameterSpec gcmDecryptSpec = (GCMParameterSpec) decParams - .getParameterSpec(GCMParameterSpec.class); - assertEquals(cipherID + " getIV()", - Arrays.toString(((GCMParameterSpec) decryptSpec).getIV()), - Arrays.toString(gcmDecryptSpec.getIV())); - assertEquals(cipherID + " getTLen()", ((GCMParameterSpec) decryptSpec).getTLen(), - gcmDecryptSpec.getTLen()); - } else if (decryptSpec instanceof IvParameterSpec) { - IvParameterSpec ivDecryptSpec = (IvParameterSpec) decParams - .getParameterSpec(IvParameterSpec.class); - assertEquals(cipherID + " getIV()", - Arrays.toString(((IvParameterSpec) decryptSpec).getIV()), - Arrays.toString(ivDecryptSpec.getIV())); - } else if (decryptSpec instanceof PBEParameterSpec) { - // Bouncycastle seems to be undecided about whether it returns this or not - if (!"BC".equals(providerName)) { - assertNotNull(cipherID + " getParameters()", decParams); - } - } + assertCorrectAlgorithmParameters(providerName, cipherID, decryptSpec, decParams); assertNull(cipherID, c.getExemptionMechanism()); // Test wrapping a key. Every cipher should be able to wrap. Except those that can't. /* Bouncycastle is broken for wrapping because getIV() fails. */ - if (isSupportedForWrapping(algorithm) - && !algorithm.equals("AES/GCM/NOPADDING") && !providerName.equals("BC")) { + if (isSupportedForWrapping(algorithm) && !providerName.equals("BC")) { // Generate a small SecretKey for AES. KeyGenerator kg = KeyGenerator.getInstance("AES"); kg.init(128); @@ -1484,13 +1564,10 @@ && isStreamMode(algorithm)) { c.updateAAD(new byte[24]); } byte[] cipherText = c.doFinal(getActualPlainText(algorithm)); - if (isAEAD(algorithm)) { - c.updateAAD(new byte[24]); + if (!isRandomizedEncryption(algorithm) && !isAEAD(algorithm)) { + byte[] cipherText2 = c.doFinal(getActualPlainText(algorithm)); + assertEquals(cipherID, Arrays.toString(cipherText), Arrays.toString(cipherText2)); } - byte[] cipherText2 = c.doFinal(getActualPlainText(algorithm)); - assertEquals(cipherID, - Arrays.toString(cipherText), - Arrays.toString(cipherText2)); c.init(Cipher.DECRYPT_MODE, getDecryptKey(algorithm), decryptSpec); if (isAEAD(algorithm)) { c.updateAAD(new byte[24]); @@ -1509,6 +1586,74 @@ && isStreamMode(algorithm)) { } } + private void assertCorrectAlgorithmParameters(String providerName, String cipherID, + final AlgorithmParameterSpec spec, AlgorithmParameters params) + throws InvalidParameterSpecException, Exception { + if (spec == null) { + return; + } + + // Bouncycastle has a bug where PBE algorithms sometimes return null parameters. + if ("BC".equals(providerName) && isPBE(cipherID) && params == null) { + return; + } + + assertNotNull(cipherID + " getParameters() should not be null", params); + + if (spec instanceof GCMParameterSpec) { + GCMParameterSpec gcmDecryptSpec = (GCMParameterSpec) params + .getParameterSpec(GCMParameterSpec.class); + assertEquals(cipherID + " getIV()", Arrays.toString(((GCMParameterSpec) spec).getIV()), + Arrays.toString(gcmDecryptSpec.getIV())); + assertEquals(cipherID + " getTLen()", ((GCMParameterSpec) spec).getTLen(), + gcmDecryptSpec.getTLen()); + } else if (spec instanceof IvParameterSpec) { + IvParameterSpec ivDecryptSpec = (IvParameterSpec) params + .getParameterSpec(IvParameterSpec.class); + assertEquals(cipherID + " getIV()", Arrays.toString(((IvParameterSpec) spec).getIV()), + Arrays.toString(ivDecryptSpec.getIV())); + } else if (spec instanceof PBEParameterSpec) { + // Bouncycastle seems to be undecided about whether it returns this + // or not + if (!"BC".equals(providerName)) { + assertNotNull(cipherID + " getParameters()", params); + } + } else if (spec instanceof OAEPParameterSpec) { + assertOAEPParametersEqual((OAEPParameterSpec) spec, + params.getParameterSpec(OAEPParameterSpec.class)); + } else { + fail("Unhandled algorithm specification class: " + spec.getClass().getName()); + } + } + + private static void assertOAEPParametersEqual(OAEPParameterSpec expectedOaepSpec, + OAEPParameterSpec actualOaepSpec) throws Exception { + assertEquals(expectedOaepSpec.getDigestAlgorithm(), actualOaepSpec.getDigestAlgorithm()); + + assertEquals(expectedOaepSpec.getMGFAlgorithm(), actualOaepSpec.getMGFAlgorithm()); + if ("MGF1".equals(expectedOaepSpec.getMGFAlgorithm())) { + MGF1ParameterSpec expectedMgf1Spec = (MGF1ParameterSpec) expectedOaepSpec + .getMGFParameters(); + MGF1ParameterSpec actualMgf1Spec = (MGF1ParameterSpec) actualOaepSpec + .getMGFParameters(); + assertEquals(expectedMgf1Spec.getDigestAlgorithm(), + actualMgf1Spec.getDigestAlgorithm()); + } else { + fail("Unknown MGF algorithm: " + expectedOaepSpec.getMGFAlgorithm()); + } + + if (expectedOaepSpec.getPSource() instanceof PSource.PSpecified + && actualOaepSpec.getPSource() instanceof PSource.PSpecified) { + assertEquals( + Arrays.toString( + ((PSource.PSpecified) expectedOaepSpec.getPSource()).getValue()), + Arrays.toString( + (((PSource.PSpecified) actualOaepSpec.getPSource()).getValue()))); + } else { + fail("Unknown PSource type"); + } + } + /** * Try various .init(...) calls with null parameters to make sure it is * handled. @@ -1602,18 +1747,21 @@ public void testInputPKCS1Padding() throws Exception { } private void testInputPKCS1Padding(String provider) throws Exception { - testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT, getEncryptKey("RSA"), getDecryptKey("RSA")); + // Type 1 is for signatures (PrivateKey to "encrypt") + testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT, getDecryptKey("RSA"), getEncryptKey("RSA")); try { - testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT, getEncryptKey("RSA"), getDecryptKey("RSA")); + testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT, getDecryptKey("RSA"), getEncryptKey("RSA")); fail(); } catch (BadPaddingException expected) { } + + // Type 2 is for enciphering (PublicKey to "encrypt") + testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT, getEncryptKey("RSA"), getDecryptKey("RSA")); try { - testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT, getDecryptKey("RSA"), getEncryptKey("RSA")); + testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT, getEncryptKey("RSA"), getDecryptKey("RSA")); fail(); } catch (BadPaddingException expected) { } - testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT, getDecryptKey("RSA"), getEncryptKey("RSA")); } private void testInputPKCS1Padding(String provider, byte[] prePaddedPlainText, Key encryptKey, Key decryptKey) throws Exception { @@ -1645,8 +1793,10 @@ public void testOutputPKCS1Padding() throws Exception { } private void testOutputPKCS1Padding(String provider) throws Exception { - testOutputPKCS1Padding(provider, (byte) 1, getEncryptKey("RSA"), getDecryptKey("RSA")); - testOutputPKCS1Padding(provider, (byte) 2, getDecryptKey("RSA"), getEncryptKey("RSA")); + // Type 1 is for signatures (PrivateKey to "encrypt") + testOutputPKCS1Padding(provider, (byte) 1, getDecryptKey("RSA"), getEncryptKey("RSA")); + // Type 2 is for enciphering (PublicKey to "encrypt") + testOutputPKCS1Padding(provider, (byte) 2, getEncryptKey("RSA"), getDecryptKey("RSA")); } private void testOutputPKCS1Padding(String provider, byte expectedBlockType, Key encryptKey, Key decryptKey) throws Exception { @@ -1918,6 +2068,63 @@ private Certificate certificateWithKeyUsage(int keyUsage) throws Exception { (byte) 0x39, }); + private static final BigInteger RSA_2048_primeExponentP = new BigInteger(1, new byte[] { + (byte) 0x51, (byte) 0x82, (byte) 0x8F, (byte) 0x1E, (byte) 0xC6, (byte) 0xFD, (byte) 0x99, (byte) 0x60, + (byte) 0x29, (byte) 0x90, (byte) 0x1B, (byte) 0xAF, (byte) 0x1D, (byte) 0x7E, (byte) 0x33, (byte) 0x7B, + (byte) 0xA5, (byte) 0xF0, (byte) 0xAF, (byte) 0x27, (byte) 0xE9, (byte) 0x84, (byte) 0xEA, (byte) 0xD8, + (byte) 0x95, (byte) 0xAC, (byte) 0xE6, (byte) 0x2B, (byte) 0xD7, (byte) 0xDF, (byte) 0x4E, (byte) 0xE4, + (byte) 0x5A, (byte) 0x22, (byte) 0x40, (byte) 0x89, (byte) 0xF2, (byte) 0xCC, (byte) 0x15, (byte) 0x1A, + (byte) 0xF3, (byte) 0xCD, (byte) 0x17, (byte) 0x3F, (byte) 0xCE, (byte) 0x04, (byte) 0x74, (byte) 0xBC, + (byte) 0xB0, (byte) 0x4F, (byte) 0x38, (byte) 0x6A, (byte) 0x2C, (byte) 0xDC, (byte) 0xC0, (byte) 0xE0, + (byte) 0x03, (byte) 0x6B, (byte) 0xA2, (byte) 0x41, (byte) 0x9F, (byte) 0x54, (byte) 0x57, (byte) 0x92, + (byte) 0x62, (byte) 0xD4, (byte) 0x71, (byte) 0x00, (byte) 0xBE, (byte) 0x93, (byte) 0x19, (byte) 0x84, + (byte) 0xA3, (byte) 0xEF, (byte) 0xA0, (byte) 0x5B, (byte) 0xEC, (byte) 0xF1, (byte) 0x41, (byte) 0x57, + (byte) 0x4D, (byte) 0xC0, (byte) 0x79, (byte) 0xB3, (byte) 0xA9, (byte) 0x5C, (byte) 0x4A, (byte) 0x83, + (byte) 0xE6, (byte) 0xC4, (byte) 0x3F, (byte) 0x32, (byte) 0x14, (byte) 0xD6, (byte) 0xDF, (byte) 0x32, + (byte) 0xD5, (byte) 0x12, (byte) 0xDE, (byte) 0x19, (byte) 0x80, (byte) 0x85, (byte) 0xE5, (byte) 0x31, + (byte) 0xE6, (byte) 0x16, (byte) 0xB8, (byte) 0x3F, (byte) 0xD7, (byte) 0xDD, (byte) 0x9D, (byte) 0x1F, + (byte) 0x4E, (byte) 0x26, (byte) 0x07, (byte) 0xC3, (byte) 0x33, (byte) 0x3D, (byte) 0x07, (byte) 0xC5, + (byte) 0x5D, (byte) 0x10, (byte) 0x7D, (byte) 0x1D, (byte) 0x38, (byte) 0x93, (byte) 0x58, (byte) 0x71, + }); + + private static final BigInteger RSA_2048_primeExponentQ = new BigInteger(1, new byte[] { + (byte) 0xDB, (byte) 0x4F, (byte) 0xB5, (byte) 0x0F, (byte) 0x50, (byte) 0xDE, (byte) 0x8E, (byte) 0xDB, + (byte) 0x53, (byte) 0xFF, (byte) 0x34, (byte) 0xC8, (byte) 0x09, (byte) 0x31, (byte) 0x88, (byte) 0xA0, + (byte) 0x51, (byte) 0x28, (byte) 0x67, (byte) 0xDA, (byte) 0x2C, (byte) 0xCA, (byte) 0x04, (byte) 0x89, + (byte) 0x77, (byte) 0x59, (byte) 0xE5, (byte) 0x87, (byte) 0xC2, (byte) 0x44, (byte) 0x01, (byte) 0x0D, + (byte) 0xAF, (byte) 0x86, (byte) 0x64, (byte) 0xD5, (byte) 0x9E, (byte) 0x80, (byte) 0x83, (byte) 0xD1, + (byte) 0x6C, (byte) 0x16, (byte) 0x47, (byte) 0x89, (byte) 0x30, (byte) 0x1F, (byte) 0x67, (byte) 0xA9, + (byte) 0xF0, (byte) 0x78, (byte) 0x06, (byte) 0x0D, (byte) 0x83, (byte) 0x4A, (byte) 0x2A, (byte) 0xDB, + (byte) 0xD3, (byte) 0x67, (byte) 0x57, (byte) 0x5B, (byte) 0x68, (byte) 0xA8, (byte) 0xA8, (byte) 0x42, + (byte) 0xC2, (byte) 0xB0, (byte) 0x2A, (byte) 0x89, (byte) 0xB3, (byte) 0xF3, (byte) 0x1F, (byte) 0xCC, + (byte) 0xEC, (byte) 0x8A, (byte) 0x22, (byte) 0xFE, (byte) 0x39, (byte) 0x57, (byte) 0x95, (byte) 0xC5, + (byte) 0xC6, (byte) 0xC7, (byte) 0x42, (byte) 0x2B, (byte) 0x4E, (byte) 0x5D, (byte) 0x74, (byte) 0xA1, + (byte) 0xE9, (byte) 0xA8, (byte) 0xF3, (byte) 0x0E, (byte) 0x77, (byte) 0x59, (byte) 0xB9, (byte) 0xFC, + (byte) 0x2D, (byte) 0x63, (byte) 0x9C, (byte) 0x1F, (byte) 0x15, (byte) 0x67, (byte) 0x3E, (byte) 0x84, + (byte) 0xE9, (byte) 0x3A, (byte) 0x5E, (byte) 0xF1, (byte) 0x50, (byte) 0x6F, (byte) 0x43, (byte) 0x15, + (byte) 0x38, (byte) 0x3C, (byte) 0x38, (byte) 0xD4, (byte) 0x5C, (byte) 0xBD, (byte) 0x1B, (byte) 0x14, + (byte) 0x04, (byte) 0x8F, (byte) 0x47, (byte) 0x21, (byte) 0xDC, (byte) 0x82, (byte) 0x32, (byte) 0x61, + }); + + private static final BigInteger RSA_2048_crtCoefficient = new BigInteger(1, new byte[] { + (byte) 0xD8, (byte) 0x11, (byte) 0x45, (byte) 0x93, (byte) 0xAF, (byte) 0x41, (byte) 0x5F, (byte) 0xB6, + (byte) 0x12, (byte) 0xDB, (byte) 0xF1, (byte) 0x92, (byte) 0x37, (byte) 0x10, (byte) 0xD5, (byte) 0x4D, + (byte) 0x07, (byte) 0x48, (byte) 0x62, (byte) 0x05, (byte) 0xA7, (byte) 0x6A, (byte) 0x3B, (byte) 0x43, + (byte) 0x19, (byte) 0x49, (byte) 0x68, (byte) 0xC0, (byte) 0xDF, (byte) 0xF1, (byte) 0xF1, (byte) 0x1E, + (byte) 0xF0, (byte) 0xF6, (byte) 0x1A, (byte) 0x4A, (byte) 0x33, (byte) 0x7D, (byte) 0x5F, (byte) 0xD3, + (byte) 0x74, (byte) 0x1B, (byte) 0xBC, (byte) 0x96, (byte) 0x40, (byte) 0xE4, (byte) 0x47, (byte) 0xB8, + (byte) 0xB6, (byte) 0xB6, (byte) 0xC4, (byte) 0x7C, (byte) 0x3A, (byte) 0xC1, (byte) 0x20, (byte) 0x43, + (byte) 0x57, (byte) 0xD3, (byte) 0xB0, (byte) 0xC5, (byte) 0x5B, (byte) 0xA9, (byte) 0x28, (byte) 0x6B, + (byte) 0xDA, (byte) 0x73, (byte) 0xF6, (byte) 0x29, (byte) 0x29, (byte) 0x6F, (byte) 0x5F, (byte) 0xA9, + (byte) 0x14, (byte) 0x6D, (byte) 0x89, (byte) 0x76, (byte) 0x35, (byte) 0x7D, (byte) 0x3C, (byte) 0x75, + (byte) 0x1E, (byte) 0x75, (byte) 0x14, (byte) 0x86, (byte) 0x96, (byte) 0xA4, (byte) 0x0B, (byte) 0x74, + (byte) 0x68, (byte) 0x5C, (byte) 0x82, (byte) 0xCE, (byte) 0x30, (byte) 0x90, (byte) 0x2D, (byte) 0x63, + (byte) 0x9D, (byte) 0x72, (byte) 0x4F, (byte) 0xF2, (byte) 0x4D, (byte) 0x5E, (byte) 0x2E, (byte) 0x94, + (byte) 0x07, (byte) 0xEE, (byte) 0x34, (byte) 0xED, (byte) 0xED, (byte) 0x2E, (byte) 0x3B, (byte) 0x4D, + (byte) 0xF6, (byte) 0x5A, (byte) 0xA9, (byte) 0xBC, (byte) 0xFE, (byte) 0xB6, (byte) 0xDF, (byte) 0x28, + (byte) 0xD0, (byte) 0x7B, (byte) 0xA6, (byte) 0x90, (byte) 0x3F, (byte) 0x16, (byte) 0x57, (byte) 0x68, + }); + /** * Test data is PKCS#1 padded "Android.\n" which can be generated by: * echo "Android." | openssl rsautl -inkey rsa.key -sign | openssl rsautl -inkey rsa.key -raw -verify | recode ../x1 @@ -2119,6 +2326,417 @@ private Certificate certificateWithKeyUsage(int keyUsage) throws Exception { (byte) 0x4b, (byte) 0x98, (byte) 0x3f, (byte) 0xae, (byte) 0x20, (byte) 0xfd, (byte) 0x8a, (byte) 0x50, (byte) 0x73, (byte) 0xe4, }; + /* + * echo -n 'This is a test of OAEP' | xxd -p -i | sed 's/0x/(byte) 0x/g' + */ + public static final byte[] RSA_Vector2_Plaintext = new byte[] { + (byte) 0x54, (byte) 0x68, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x69, + (byte) 0x73, (byte) 0x20, (byte) 0x61, (byte) 0x20, (byte) 0x74, (byte) 0x65, + (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x6f, (byte) 0x66, (byte) 0x20, + (byte) 0x4f, (byte) 0x41, (byte) 0x45, (byte) 0x50 + }; + + /* + * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey rsakey.pem \ + * -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha1 -pkeyopt rsa_mgf1_md:sha1 \ + * | xxd -p -i | sed 's/0x/(byte) 0x/g' + */ + public static final byte[] RSA_Vector2_OAEP_SHA1_MGF1_SHA1 = new byte[] { + (byte) 0x53, (byte) 0x71, (byte) 0x84, (byte) 0x2e, (byte) 0x01, (byte) 0x74, + (byte) 0x82, (byte) 0xb3, (byte) 0x01, (byte) 0xac, (byte) 0x2b, (byte) 0xbd, + (byte) 0x40, (byte) 0xa7, (byte) 0x5b, (byte) 0x60, (byte) 0xf1, (byte) 0xde, + (byte) 0x54, (byte) 0x1d, (byte) 0x94, (byte) 0xc1, (byte) 0x10, (byte) 0x31, + (byte) 0x6f, (byte) 0xa3, (byte) 0xd8, (byte) 0x41, (byte) 0x2e, (byte) 0x82, + (byte) 0xad, (byte) 0x07, (byte) 0x6f, (byte) 0x25, (byte) 0x6c, (byte) 0xb5, + (byte) 0xef, (byte) 0xc6, (byte) 0xa6, (byte) 0xfb, (byte) 0xb1, (byte) 0x9d, + (byte) 0x75, (byte) 0x67, (byte) 0xb0, (byte) 0x97, (byte) 0x21, (byte) 0x3c, + (byte) 0x17, (byte) 0x04, (byte) 0xdc, (byte) 0x4e, (byte) 0x7e, (byte) 0x3f, + (byte) 0x5c, (byte) 0x13, (byte) 0x5e, (byte) 0x15, (byte) 0x0f, (byte) 0xe2, + (byte) 0xa7, (byte) 0x62, (byte) 0x6a, (byte) 0x08, (byte) 0xb1, (byte) 0xbc, + (byte) 0x2f, (byte) 0xcb, (byte) 0xb5, (byte) 0x96, (byte) 0x2d, (byte) 0xec, + (byte) 0x71, (byte) 0x4d, (byte) 0x59, (byte) 0x6e, (byte) 0x27, (byte) 0x85, + (byte) 0x87, (byte) 0x9b, (byte) 0xcc, (byte) 0x40, (byte) 0x32, (byte) 0x09, + (byte) 0x06, (byte) 0xe6, (byte) 0x7d, (byte) 0xdf, (byte) 0xeb, (byte) 0x2f, + (byte) 0xa8, (byte) 0x1c, (byte) 0x53, (byte) 0xdb, (byte) 0xa7, (byte) 0x48, + (byte) 0xf5, (byte) 0xbf, (byte) 0x2f, (byte) 0xbb, (byte) 0xee, (byte) 0xc7, + (byte) 0x55, (byte) 0x5e, (byte) 0xc4, (byte) 0x1c, (byte) 0x84, (byte) 0xed, + (byte) 0x97, (byte) 0x7e, (byte) 0xce, (byte) 0xa5, (byte) 0x69, (byte) 0x73, + (byte) 0xb3, (byte) 0xe0, (byte) 0x8c, (byte) 0x2a, (byte) 0xf2, (byte) 0xc7, + (byte) 0x65, (byte) 0xff, (byte) 0x10, (byte) 0xed, (byte) 0x25, (byte) 0xf0, + (byte) 0xf8, (byte) 0xda, (byte) 0x2f, (byte) 0x7f, (byte) 0xe0, (byte) 0x69, + (byte) 0xed, (byte) 0xb1, (byte) 0x0e, (byte) 0xcb, (byte) 0x43, (byte) 0xe4, + (byte) 0x31, (byte) 0xe6, (byte) 0x52, (byte) 0xfd, (byte) 0xa7, (byte) 0xe5, + (byte) 0x21, (byte) 0xd0, (byte) 0x67, (byte) 0x0a, (byte) 0xc1, (byte) 0xa1, + (byte) 0xb9, (byte) 0x04, (byte) 0xdb, (byte) 0x98, (byte) 0x4f, (byte) 0xf9, + (byte) 0x5c, (byte) 0x60, (byte) 0x4d, (byte) 0xac, (byte) 0x7a, (byte) 0x69, + (byte) 0xbd, (byte) 0x63, (byte) 0x0d, (byte) 0xb2, (byte) 0x01, (byte) 0x83, + (byte) 0xd7, (byte) 0x22, (byte) 0x5d, (byte) 0xed, (byte) 0xbd, (byte) 0x32, + (byte) 0x98, (byte) 0xd1, (byte) 0x4a, (byte) 0x2e, (byte) 0xb7, (byte) 0xb1, + (byte) 0x6d, (byte) 0x8a, (byte) 0x8f, (byte) 0xef, (byte) 0xc3, (byte) 0x89, + (byte) 0xdf, (byte) 0xa5, (byte) 0xac, (byte) 0xfb, (byte) 0x38, (byte) 0x61, + (byte) 0x32, (byte) 0xc5, (byte) 0x19, (byte) 0x83, (byte) 0x1f, (byte) 0x9c, + (byte) 0x45, (byte) 0x58, (byte) 0xdd, (byte) 0xa3, (byte) 0x57, (byte) 0xe4, + (byte) 0x91, (byte) 0xd2, (byte) 0x11, (byte) 0xf8, (byte) 0x96, (byte) 0x36, + (byte) 0x67, (byte) 0x99, (byte) 0x2b, (byte) 0x62, (byte) 0x21, (byte) 0xe3, + (byte) 0xa8, (byte) 0x5e, (byte) 0xa4, (byte) 0x2e, (byte) 0x0c, (byte) 0x29, + (byte) 0xf9, (byte) 0xcd, (byte) 0xfa, (byte) 0xbe, (byte) 0x3f, (byte) 0xd8, + (byte) 0xec, (byte) 0x6b, (byte) 0x32, (byte) 0xb3, (byte) 0x40, (byte) 0x4f, + (byte) 0x48, (byte) 0xe3, (byte) 0x14, (byte) 0x87, (byte) 0xa7, (byte) 0x5c, + (byte) 0xba, (byte) 0xdf, (byte) 0x0e, (byte) 0x64, (byte) 0xdc, (byte) 0xe2, + (byte) 0x51, (byte) 0xf4, (byte) 0x41, (byte) 0x25, (byte) 0x23, (byte) 0xc8, + (byte) 0x50, (byte) 0x1e, (byte) 0x9e, (byte) 0xb0 + }; + + /* + * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey rsakey.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha1 | xxd -p -i | sed 's/0x/(byte) 0x/g' + */ + public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1 = new byte[] { + (byte) 0x25, (byte) 0x9f, (byte) 0xc3, (byte) 0x69, (byte) 0xbc, (byte) 0x3f, + (byte) 0xe7, (byte) 0x9e, (byte) 0x76, (byte) 0xef, (byte) 0x6c, (byte) 0xd2, + (byte) 0x2b, (byte) 0x7b, (byte) 0xf0, (byte) 0xeb, (byte) 0xc2, (byte) 0x28, + (byte) 0x40, (byte) 0x4e, (byte) 0x9b, (byte) 0x2a, (byte) 0x4e, (byte) 0xa4, + (byte) 0x79, (byte) 0x66, (byte) 0xf1, (byte) 0x10, (byte) 0x96, (byte) 0x8c, + (byte) 0x58, (byte) 0x92, (byte) 0xb7, (byte) 0x70, (byte) 0xed, (byte) 0x3a, + (byte) 0xe0, (byte) 0x99, (byte) 0xd1, (byte) 0x80, (byte) 0x4b, (byte) 0x53, + (byte) 0x70, (byte) 0x9b, (byte) 0x51, (byte) 0xbf, (byte) 0xc1, (byte) 0x3a, + (byte) 0x70, (byte) 0xc5, (byte) 0x79, (byte) 0x21, (byte) 0x6e, (byte) 0xb3, + (byte) 0xf7, (byte) 0xa9, (byte) 0xe6, (byte) 0xcb, (byte) 0x70, (byte) 0xe4, + (byte) 0xf3, (byte) 0x4f, (byte) 0x45, (byte) 0xcf, (byte) 0xb7, (byte) 0x2b, + (byte) 0x38, (byte) 0xfd, (byte) 0x5d, (byte) 0x9a, (byte) 0x53, (byte) 0xc5, + (byte) 0x05, (byte) 0x74, (byte) 0x8d, (byte) 0x1d, (byte) 0x6e, (byte) 0x83, + (byte) 0xaa, (byte) 0x71, (byte) 0xc5, (byte) 0xe1, (byte) 0xa1, (byte) 0xa6, + (byte) 0xf3, (byte) 0xee, (byte) 0x5f, (byte) 0x9e, (byte) 0x4f, (byte) 0xe8, + (byte) 0x15, (byte) 0xd5, (byte) 0xa9, (byte) 0x1b, (byte) 0xa6, (byte) 0x41, + (byte) 0x2b, (byte) 0x18, (byte) 0x13, (byte) 0x20, (byte) 0x9f, (byte) 0x6b, + (byte) 0xf1, (byte) 0xd8, (byte) 0xf4, (byte) 0x87, (byte) 0xfa, (byte) 0x80, + (byte) 0xec, (byte) 0x0e, (byte) 0xa4, (byte) 0x4b, (byte) 0x24, (byte) 0x03, + (byte) 0x14, (byte) 0x25, (byte) 0xf2, (byte) 0x20, (byte) 0xfc, (byte) 0x52, + (byte) 0xf9, (byte) 0xd6, (byte) 0x7a, (byte) 0x4a, (byte) 0x45, (byte) 0x33, + (byte) 0xec, (byte) 0xde, (byte) 0x3c, (byte) 0x5b, (byte) 0xf2, (byte) 0xdc, + (byte) 0x8e, (byte) 0xc6, (byte) 0xb3, (byte) 0x26, (byte) 0xd3, (byte) 0x68, + (byte) 0xa7, (byte) 0xd8, (byte) 0x3a, (byte) 0xde, (byte) 0xa9, (byte) 0x25, + (byte) 0x1d, (byte) 0x42, (byte) 0x75, (byte) 0x66, (byte) 0x16, (byte) 0x29, + (byte) 0xad, (byte) 0x09, (byte) 0x74, (byte) 0x41, (byte) 0xbb, (byte) 0x45, + (byte) 0x39, (byte) 0x04, (byte) 0x7a, (byte) 0x93, (byte) 0xad, (byte) 0x1c, + (byte) 0xa6, (byte) 0x38, (byte) 0xf4, (byte) 0xac, (byte) 0xca, (byte) 0x5a, + (byte) 0xab, (byte) 0x92, (byte) 0x76, (byte) 0x26, (byte) 0x3c, (byte) 0xeb, + (byte) 0xda, (byte) 0xfc, (byte) 0x25, (byte) 0x93, (byte) 0x23, (byte) 0x01, + (byte) 0xe2, (byte) 0xac, (byte) 0x5e, (byte) 0x4c, (byte) 0xb7, (byte) 0xbc, + (byte) 0x5b, (byte) 0xaa, (byte) 0x14, (byte) 0xe9, (byte) 0xbf, (byte) 0x2d, + (byte) 0x3a, (byte) 0xdc, (byte) 0x2f, (byte) 0x6b, (byte) 0x4d, (byte) 0x0e, + (byte) 0x0a, (byte) 0x82, (byte) 0x3c, (byte) 0xd9, (byte) 0x32, (byte) 0xc1, + (byte) 0xc4, (byte) 0xa2, (byte) 0x46, (byte) 0x71, (byte) 0x10, (byte) 0x54, + (byte) 0x1a, (byte) 0xa6, (byte) 0xaa, (byte) 0x64, (byte) 0xe7, (byte) 0xc2, + (byte) 0xae, (byte) 0xbc, (byte) 0x3d, (byte) 0xa4, (byte) 0xa8, (byte) 0xd1, + (byte) 0xb7, (byte) 0x27, (byte) 0xef, (byte) 0x5f, (byte) 0xe7, (byte) 0xa7, + (byte) 0x5d, (byte) 0xa0, (byte) 0xcd, (byte) 0x57, (byte) 0xf1, (byte) 0xe0, + (byte) 0xd8, (byte) 0x42, (byte) 0x10, (byte) 0x77, (byte) 0xc3, (byte) 0xa7, + (byte) 0x1e, (byte) 0x0c, (byte) 0x37, (byte) 0x16, (byte) 0x11, (byte) 0x94, + (byte) 0x21, (byte) 0xf2, (byte) 0xca, (byte) 0x60, (byte) 0xce, (byte) 0xca, + (byte) 0x59, (byte) 0xf9, (byte) 0xe5, (byte) 0xe4 + }; + + /* + * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha1 -pkeyopt rsa_oaep_label:010203FFA00A | xxd -p -i | sed 's/0x/(byte) 0x/g' + */ + public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1_LABEL = new byte[] { + (byte) 0x80, (byte) 0xb1, (byte) 0xf2, (byte) 0xc2, (byte) 0x03, (byte) 0xc5, + (byte) 0xdf, (byte) 0xbd, (byte) 0xed, (byte) 0xfe, (byte) 0xe6, (byte) 0xff, + (byte) 0xd3, (byte) 0x38, (byte) 0x1e, (byte) 0x6d, (byte) 0xae, (byte) 0x47, + (byte) 0xfe, (byte) 0x19, (byte) 0xf9, (byte) 0x8c, (byte) 0xf1, (byte) 0x4d, + (byte) 0x18, (byte) 0x2b, (byte) 0x7e, (byte) 0x8e, (byte) 0x47, (byte) 0x39, + (byte) 0xa8, (byte) 0x04, (byte) 0xc4, (byte) 0x7d, (byte) 0x56, (byte) 0x03, + (byte) 0x15, (byte) 0x92, (byte) 0x18, (byte) 0xde, (byte) 0x56, (byte) 0xb3, + (byte) 0x01, (byte) 0x93, (byte) 0x16, (byte) 0xe3, (byte) 0xfa, (byte) 0xaa, + (byte) 0xf3, (byte) 0x73, (byte) 0x39, (byte) 0x26, (byte) 0xfb, (byte) 0xb0, + (byte) 0x18, (byte) 0x20, (byte) 0xdb, (byte) 0xa1, (byte) 0xbf, (byte) 0x31, + (byte) 0x22, (byte) 0xc8, (byte) 0x1d, (byte) 0xdb, (byte) 0xa0, (byte) 0x5a, + (byte) 0x22, (byte) 0xcd, (byte) 0x09, (byte) 0xb3, (byte) 0xcb, (byte) 0xa2, + (byte) 0x46, (byte) 0x14, (byte) 0x35, (byte) 0x66, (byte) 0xe8, (byte) 0xb8, + (byte) 0x07, (byte) 0x23, (byte) 0xc5, (byte) 0xae, (byte) 0xe6, (byte) 0xf1, + (byte) 0x7a, (byte) 0x8f, (byte) 0x5c, (byte) 0x44, (byte) 0x34, (byte) 0xbf, + (byte) 0xd6, (byte) 0xf8, (byte) 0x0c, (byte) 0xc7, (byte) 0x8d, (byte) 0xcd, + (byte) 0x23, (byte) 0x84, (byte) 0xbe, (byte) 0x9b, (byte) 0xbf, (byte) 0x9a, + (byte) 0x70, (byte) 0x0f, (byte) 0x18, (byte) 0xc0, (byte) 0x6f, (byte) 0x23, + (byte) 0x67, (byte) 0xf8, (byte) 0xbb, (byte) 0xce, (byte) 0xc2, (byte) 0x47, + (byte) 0x82, (byte) 0xa0, (byte) 0xa5, (byte) 0x60, (byte) 0xcd, (byte) 0x25, + (byte) 0xa5, (byte) 0x4b, (byte) 0xe4, (byte) 0x06, (byte) 0x7f, (byte) 0x46, + (byte) 0x62, (byte) 0x86, (byte) 0x94, (byte) 0xbc, (byte) 0x7f, (byte) 0xb0, + (byte) 0x2e, (byte) 0xc1, (byte) 0x8c, (byte) 0x6c, (byte) 0x58, (byte) 0x05, + (byte) 0x6f, (byte) 0x35, (byte) 0x76, (byte) 0xd3, (byte) 0xdf, (byte) 0xc0, + (byte) 0xdd, (byte) 0x66, (byte) 0xbe, (byte) 0xa1, (byte) 0x7e, (byte) 0x52, + (byte) 0xed, (byte) 0x81, (byte) 0x0e, (byte) 0x2d, (byte) 0x5b, (byte) 0x2b, + (byte) 0xe3, (byte) 0x52, (byte) 0x0e, (byte) 0x56, (byte) 0x9b, (byte) 0x05, + (byte) 0x72, (byte) 0xa8, (byte) 0xc8, (byte) 0x57, (byte) 0x22, (byte) 0x67, + (byte) 0x0e, (byte) 0x5f, (byte) 0x01, (byte) 0xf2, (byte) 0x69, (byte) 0x66, + (byte) 0x6a, (byte) 0x47, (byte) 0x4f, (byte) 0x78, (byte) 0xb3, (byte) 0x1e, + (byte) 0x7d, (byte) 0xce, (byte) 0xb3, (byte) 0x35, (byte) 0xdf, (byte) 0x23, + (byte) 0xac, (byte) 0xf8, (byte) 0x88, (byte) 0xa1, (byte) 0xde, (byte) 0x38, + (byte) 0x96, (byte) 0xfd, (byte) 0xa2, (byte) 0x5d, (byte) 0x09, (byte) 0x52, + (byte) 0x11, (byte) 0x2b, (byte) 0x21, (byte) 0xf0, (byte) 0x0d, (byte) 0x4c, + (byte) 0x15, (byte) 0xc3, (byte) 0x88, (byte) 0x2b, (byte) 0xf6, (byte) 0x2b, + (byte) 0xe3, (byte) 0xfd, (byte) 0x52, (byte) 0xf0, (byte) 0x09, (byte) 0x5c, + (byte) 0x4f, (byte) 0x5b, (byte) 0x8b, (byte) 0x84, (byte) 0x71, (byte) 0x72, + (byte) 0x8d, (byte) 0xaa, (byte) 0x6c, (byte) 0x55, (byte) 0xba, (byte) 0xe7, + (byte) 0x9c, (byte) 0xba, (byte) 0xbf, (byte) 0xf4, (byte) 0x09, (byte) 0x0a, + (byte) 0x60, (byte) 0xec, (byte) 0x53, (byte) 0xa4, (byte) 0x01, (byte) 0xa5, + (byte) 0xf2, (byte) 0x58, (byte) 0xab, (byte) 0x95, (byte) 0x68, (byte) 0x79, + (byte) 0x0b, (byte) 0xc3, (byte) 0xc4, (byte) 0x00, (byte) 0x68, (byte) 0x19, + (byte) 0xca, (byte) 0x07, (byte) 0x0d, (byte) 0x32 + }; + + /* + * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey rsakey.pem \ + * -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha224 -pkeyopt rsa_mgf1_md:sha224 \ + * | xxd -p -i | sed 's/0x/(byte) 0x/g' + */ + public static final byte[] RSA_Vector2_OAEP_SHA224_MGF1_SHA224 = new byte[] { + (byte) 0xae, (byte) 0xdd, (byte) 0xe6, (byte) 0xab, (byte) 0x00, (byte) 0xd6, + (byte) 0x1e, (byte) 0x7e, (byte) 0x85, (byte) 0x63, (byte) 0xab, (byte) 0x51, + (byte) 0x79, (byte) 0x92, (byte) 0xf1, (byte) 0xb9, (byte) 0x4f, (byte) 0x23, + (byte) 0xae, (byte) 0xf7, (byte) 0x1b, (byte) 0x5f, (byte) 0x10, (byte) 0x5b, + (byte) 0xa5, (byte) 0x15, (byte) 0x87, (byte) 0xa3, (byte) 0xbb, (byte) 0x26, + (byte) 0xfe, (byte) 0x7f, (byte) 0xc0, (byte) 0xa3, (byte) 0x67, (byte) 0x95, + (byte) 0xda, (byte) 0xc4, (byte) 0x6f, (byte) 0x6e, (byte) 0x08, (byte) 0x23, + (byte) 0x28, (byte) 0x0b, (byte) 0xdd, (byte) 0x29, (byte) 0x29, (byte) 0xdc, + (byte) 0xb0, (byte) 0x35, (byte) 0x16, (byte) 0x2e, (byte) 0x0f, (byte) 0xb9, + (byte) 0x1d, (byte) 0x90, (byte) 0x27, (byte) 0x68, (byte) 0xc7, (byte) 0x92, + (byte) 0x52, (byte) 0x8a, (byte) 0x1d, (byte) 0x48, (byte) 0x6a, (byte) 0x7d, + (byte) 0x0b, (byte) 0xf6, (byte) 0x35, (byte) 0xca, (byte) 0xe1, (byte) 0x57, + (byte) 0xdd, (byte) 0x36, (byte) 0x3b, (byte) 0x51, (byte) 0x45, (byte) 0x77, + (byte) 0x28, (byte) 0x4f, (byte) 0x98, (byte) 0xc0, (byte) 0xe0, (byte) 0xa7, + (byte) 0x51, (byte) 0x98, (byte) 0x84, (byte) 0x7a, (byte) 0x29, (byte) 0x05, + (byte) 0x9f, (byte) 0x60, (byte) 0x66, (byte) 0xf6, (byte) 0x83, (byte) 0xcd, + (byte) 0x03, (byte) 0x3e, (byte) 0x82, (byte) 0x0f, (byte) 0x57, (byte) 0x4b, + (byte) 0x27, (byte) 0x14, (byte) 0xf6, (byte) 0xc8, (byte) 0x5b, (byte) 0xed, + (byte) 0xc3, (byte) 0x77, (byte) 0x6f, (byte) 0xec, (byte) 0x0e, (byte) 0xae, + (byte) 0x59, (byte) 0xbe, (byte) 0x68, (byte) 0x76, (byte) 0x16, (byte) 0x17, + (byte) 0x77, (byte) 0xe2, (byte) 0xbd, (byte) 0xe0, (byte) 0x5a, (byte) 0x14, + (byte) 0xd9, (byte) 0xf4, (byte) 0x3f, (byte) 0x50, (byte) 0x31, (byte) 0xf0, + (byte) 0x0c, (byte) 0x82, (byte) 0x6c, (byte) 0xcc, (byte) 0x81, (byte) 0x84, + (byte) 0x3e, (byte) 0x63, (byte) 0x93, (byte) 0xe7, (byte) 0x12, (byte) 0x2d, + (byte) 0xc9, (byte) 0xa3, (byte) 0xe3, (byte) 0xce, (byte) 0xfd, (byte) 0xc7, + (byte) 0xe1, (byte) 0xef, (byte) 0xa4, (byte) 0x16, (byte) 0x5c, (byte) 0x60, + (byte) 0xb1, (byte) 0x80, (byte) 0x31, (byte) 0x15, (byte) 0x5c, (byte) 0x35, + (byte) 0x25, (byte) 0x0b, (byte) 0x89, (byte) 0xe4, (byte) 0x56, (byte) 0x74, + (byte) 0x8b, (byte) 0xaf, (byte) 0x8e, (byte) 0xe9, (byte) 0xe2, (byte) 0x37, + (byte) 0x17, (byte) 0xe6, (byte) 0x7b, (byte) 0x78, (byte) 0xd8, (byte) 0x2c, + (byte) 0x27, (byte) 0x52, (byte) 0x21, (byte) 0x96, (byte) 0xa0, (byte) 0x92, + (byte) 0x95, (byte) 0x64, (byte) 0xc3, (byte) 0x7f, (byte) 0x45, (byte) 0xfc, + (byte) 0x3d, (byte) 0x48, (byte) 0x4a, (byte) 0xd5, (byte) 0xa4, (byte) 0x0a, + (byte) 0x57, (byte) 0x07, (byte) 0x57, (byte) 0x95, (byte) 0x9f, (byte) 0x2f, + (byte) 0x75, (byte) 0x32, (byte) 0x2a, (byte) 0x4d, (byte) 0x64, (byte) 0xbd, + (byte) 0xb1, (byte) 0xe0, (byte) 0x46, (byte) 0x4f, (byte) 0xe8, (byte) 0x6c, + (byte) 0x4b, (byte) 0x77, (byte) 0xcc, (byte) 0x36, (byte) 0x87, (byte) 0x05, + (byte) 0x56, (byte) 0x9a, (byte) 0xe4, (byte) 0x2c, (byte) 0x43, (byte) 0xfd, + (byte) 0x34, (byte) 0x97, (byte) 0xf8, (byte) 0xd7, (byte) 0x91, (byte) 0xff, + (byte) 0x56, (byte) 0x86, (byte) 0x17, (byte) 0x49, (byte) 0x0a, (byte) 0x52, + (byte) 0xfb, (byte) 0xe5, (byte) 0x49, (byte) 0xdf, (byte) 0xc1, (byte) 0x28, + (byte) 0x9d, (byte) 0x85, (byte) 0x66, (byte) 0x9d, (byte) 0x1d, (byte) 0xa4, + (byte) 0x7e, (byte) 0x9a, (byte) 0x5b, (byte) 0x30 + }; + + /* + * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt \ + * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 \ + * | xxd -p -i | sed 's/0x/(byte) 0x/g' + */ + public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA256 = new byte[] { + (byte) 0x6a, (byte) 0x2b, (byte) 0xb2, (byte) 0xa3, (byte) 0x26, (byte) 0xa6, + (byte) 0x7a, (byte) 0x4a, (byte) 0x1f, (byte) 0xe5, (byte) 0xc8, (byte) 0x94, + (byte) 0x11, (byte) 0x1a, (byte) 0x92, (byte) 0x07, (byte) 0x0a, (byte) 0xf4, + (byte) 0x07, (byte) 0x0b, (byte) 0xd6, (byte) 0x37, (byte) 0xa5, (byte) 0x5d, + (byte) 0x16, (byte) 0x0a, (byte) 0x7d, (byte) 0x13, (byte) 0x27, (byte) 0x32, + (byte) 0x5a, (byte) 0xc3, (byte) 0x0d, (byte) 0x7a, (byte) 0x54, (byte) 0xfe, + (byte) 0x02, (byte) 0x28, (byte) 0xc6, (byte) 0x8e, (byte) 0x32, (byte) 0x7b, + (byte) 0x0a, (byte) 0x52, (byte) 0xf8, (byte) 0xe6, (byte) 0xab, (byte) 0x16, + (byte) 0x77, (byte) 0x7c, (byte) 0x53, (byte) 0xcd, (byte) 0xb0, (byte) 0xb6, + (byte) 0x90, (byte) 0xce, (byte) 0x7b, (byte) 0xa5, (byte) 0xdb, (byte) 0xab, + (byte) 0xfd, (byte) 0xf5, (byte) 0xbb, (byte) 0x49, (byte) 0x63, (byte) 0xb7, + (byte) 0xa8, (byte) 0x3e, (byte) 0x53, (byte) 0xf1, (byte) 0x00, (byte) 0x4d, + (byte) 0x72, (byte) 0x15, (byte) 0x34, (byte) 0xa8, (byte) 0x5b, (byte) 0x00, + (byte) 0x01, (byte) 0x75, (byte) 0xdc, (byte) 0xb6, (byte) 0xd1, (byte) 0xdf, + (byte) 0xcb, (byte) 0x93, (byte) 0xf3, (byte) 0x31, (byte) 0x04, (byte) 0x7e, + (byte) 0x48, (byte) 0x3e, (byte) 0xc9, (byte) 0xaf, (byte) 0xd7, (byte) 0xbd, + (byte) 0x9e, (byte) 0x73, (byte) 0x01, (byte) 0x79, (byte) 0xf8, (byte) 0xdc, + (byte) 0x46, (byte) 0x31, (byte) 0x55, (byte) 0x83, (byte) 0x21, (byte) 0xd1, + (byte) 0x19, (byte) 0x0b, (byte) 0x57, (byte) 0xf1, (byte) 0x06, (byte) 0xb9, + (byte) 0x32, (byte) 0x0e, (byte) 0x9d, (byte) 0x38, (byte) 0x53, (byte) 0x94, + (byte) 0x96, (byte) 0xd4, (byte) 0x6d, (byte) 0x18, (byte) 0xe2, (byte) 0xe3, + (byte) 0xcd, (byte) 0xfa, (byte) 0xfe, (byte) 0xb3, (byte) 0xe3, (byte) 0x27, + (byte) 0xd7, (byte) 0x45, (byte) 0xe8, (byte) 0x46, (byte) 0x6b, (byte) 0x06, + (byte) 0x0f, (byte) 0x5e, (byte) 0x24, (byte) 0x02, (byte) 0xef, (byte) 0xa2, + (byte) 0x69, (byte) 0xe6, (byte) 0x15, (byte) 0xb3, (byte) 0x8f, (byte) 0x71, + (byte) 0x97, (byte) 0x39, (byte) 0xfb, (byte) 0x32, (byte) 0xe0, (byte) 0xe5, + (byte) 0xac, (byte) 0x46, (byte) 0xb4, (byte) 0xe7, (byte) 0x3d, (byte) 0x89, + (byte) 0xba, (byte) 0xd9, (byte) 0x4c, (byte) 0x25, (byte) 0x97, (byte) 0xef, + (byte) 0xe6, (byte) 0x17, (byte) 0x23, (byte) 0x4e, (byte) 0xc8, (byte) 0xdb, + (byte) 0x18, (byte) 0x9b, (byte) 0xba, (byte) 0xb5, (byte) 0x7e, (byte) 0x19, + (byte) 0x4d, (byte) 0x95, (byte) 0x7d, (byte) 0x60, (byte) 0x1b, (byte) 0xa7, + (byte) 0x06, (byte) 0x1e, (byte) 0x99, (byte) 0x4a, (byte) 0xf2, (byte) 0x82, + (byte) 0x71, (byte) 0x62, (byte) 0x41, (byte) 0xa4, (byte) 0xa7, (byte) 0xdb, + (byte) 0x88, (byte) 0xb0, (byte) 0x4a, (byte) 0xc7, (byte) 0x3b, (byte) 0xce, + (byte) 0x91, (byte) 0x4f, (byte) 0xc7, (byte) 0xca, (byte) 0x6f, (byte) 0x89, + (byte) 0xac, (byte) 0x1a, (byte) 0x36, (byte) 0x84, (byte) 0x0c, (byte) 0x97, + (byte) 0xa0, (byte) 0x1a, (byte) 0x08, (byte) 0x6f, (byte) 0x70, (byte) 0xf3, + (byte) 0x94, (byte) 0xa0, (byte) 0x0f, (byte) 0x44, (byte) 0xdd, (byte) 0x86, + (byte) 0x9d, (byte) 0x2c, (byte) 0xac, (byte) 0x43, (byte) 0xed, (byte) 0xb8, + (byte) 0xa1, (byte) 0x66, (byte) 0xf3, (byte) 0xd3, (byte) 0x5c, (byte) 0xe5, + (byte) 0xe2, (byte) 0x4c, (byte) 0x7e, (byte) 0xda, (byte) 0x20, (byte) 0xbd, + (byte) 0x5a, (byte) 0x75, (byte) 0x12, (byte) 0x31, (byte) 0x23, (byte) 0x02, + (byte) 0xb5, (byte) 0x1f, (byte) 0x38, (byte) 0x98 + }; + + /* + * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt \ + * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha384 -pkeyopt rsa_mgf1_md:sha384 \ + * | xxd -p -i | sed 's/0x/(byte) 0x/g' + */ + public static final byte[] RSA_Vector2_OAEP_SHA384_MGF1_SHA384 = new byte[] { + (byte) 0xa1, (byte) 0xb3, (byte) 0x3b, (byte) 0x34, (byte) 0x69, (byte) 0x9e, + (byte) 0xd8, (byte) 0xa0, (byte) 0x37, (byte) 0x2c, (byte) 0xeb, (byte) 0xef, + (byte) 0xf2, (byte) 0xaf, (byte) 0xfa, (byte) 0x63, (byte) 0x5d, (byte) 0x88, + (byte) 0xac, (byte) 0x51, (byte) 0xd4, (byte) 0x7f, (byte) 0x85, (byte) 0xf0, + (byte) 0x5e, (byte) 0xb4, (byte) 0x81, (byte) 0x7c, (byte) 0x82, (byte) 0x4f, + (byte) 0x92, (byte) 0xf7, (byte) 0x77, (byte) 0x48, (byte) 0x4c, (byte) 0xb1, + (byte) 0x42, (byte) 0xb3, (byte) 0x0e, (byte) 0x94, (byte) 0xc8, (byte) 0x5a, + (byte) 0xae, (byte) 0xed, (byte) 0x8d, (byte) 0x51, (byte) 0x72, (byte) 0x6b, + (byte) 0xa9, (byte) 0xd4, (byte) 0x1e, (byte) 0xbe, (byte) 0x38, (byte) 0x2c, + (byte) 0xd0, (byte) 0x43, (byte) 0xae, (byte) 0xb4, (byte) 0x30, (byte) 0xa9, + (byte) 0x93, (byte) 0x47, (byte) 0xb5, (byte) 0x9d, (byte) 0x03, (byte) 0x92, + (byte) 0x25, (byte) 0x74, (byte) 0xed, (byte) 0xfa, (byte) 0xfe, (byte) 0xf1, + (byte) 0xba, (byte) 0x04, (byte) 0x3a, (byte) 0x4d, (byte) 0x6d, (byte) 0x9a, + (byte) 0x0d, (byte) 0x95, (byte) 0x02, (byte) 0xb0, (byte) 0xac, (byte) 0x77, + (byte) 0x11, (byte) 0x44, (byte) 0xeb, (byte) 0xd2, (byte) 0x02, (byte) 0x90, + (byte) 0xea, (byte) 0x2f, (byte) 0x68, (byte) 0x2a, (byte) 0x69, (byte) 0xcf, + (byte) 0x45, (byte) 0x34, (byte) 0xff, (byte) 0x00, (byte) 0xc6, (byte) 0x3c, + (byte) 0x0b, (byte) 0x2c, (byte) 0x5f, (byte) 0x8c, (byte) 0x2c, (byte) 0xbf, + (byte) 0xc2, (byte) 0x4b, (byte) 0x16, (byte) 0x07, (byte) 0x84, (byte) 0x74, + (byte) 0xf0, (byte) 0x7a, (byte) 0x01, (byte) 0x7e, (byte) 0x74, (byte) 0x01, + (byte) 0x88, (byte) 0xce, (byte) 0xda, (byte) 0xe4, (byte) 0x21, (byte) 0x89, + (byte) 0xfc, (byte) 0xac, (byte) 0x68, (byte) 0xdb, (byte) 0xfc, (byte) 0x5f, + (byte) 0x3f, (byte) 0x00, (byte) 0xd9, (byte) 0x32, (byte) 0x1d, (byte) 0xa5, + (byte) 0xec, (byte) 0x72, (byte) 0x46, (byte) 0x23, (byte) 0xe5, (byte) 0x7f, + (byte) 0x49, (byte) 0x0e, (byte) 0x3e, (byte) 0xf2, (byte) 0x2b, (byte) 0x16, + (byte) 0x52, (byte) 0x9f, (byte) 0x9d, (byte) 0x0c, (byte) 0xfe, (byte) 0xab, + (byte) 0xdd, (byte) 0x77, (byte) 0x77, (byte) 0x94, (byte) 0xa4, (byte) 0x92, + (byte) 0xa2, (byte) 0x41, (byte) 0x0d, (byte) 0x4b, (byte) 0x57, (byte) 0x80, + (byte) 0xd6, (byte) 0x74, (byte) 0x63, (byte) 0xd5, (byte) 0xbf, (byte) 0x5c, + (byte) 0xa0, (byte) 0xda, (byte) 0x3c, (byte) 0xe6, (byte) 0xbf, (byte) 0xa4, + (byte) 0xc3, (byte) 0xfb, (byte) 0x46, (byte) 0x3b, (byte) 0x73, (byte) 0x30, + (byte) 0x4b, (byte) 0x57, (byte) 0x27, (byte) 0x0c, (byte) 0x81, (byte) 0xde, + (byte) 0x8a, (byte) 0x01, (byte) 0xe5, (byte) 0x7e, (byte) 0xe0, (byte) 0x16, + (byte) 0x11, (byte) 0x24, (byte) 0x34, (byte) 0x22, (byte) 0x01, (byte) 0x9f, + (byte) 0xe6, (byte) 0xa9, (byte) 0xfb, (byte) 0xad, (byte) 0x55, (byte) 0x17, + (byte) 0x2a, (byte) 0x92, (byte) 0x87, (byte) 0xf3, (byte) 0x72, (byte) 0xc9, + (byte) 0x3d, (byte) 0xc9, (byte) 0x2e, (byte) 0x32, (byte) 0x8e, (byte) 0xbb, + (byte) 0xdc, (byte) 0x1b, (byte) 0xa7, (byte) 0x7b, (byte) 0x73, (byte) 0xd7, + (byte) 0xf4, (byte) 0xad, (byte) 0xa9, (byte) 0x3a, (byte) 0xf7, (byte) 0xa8, + (byte) 0x82, (byte) 0x92, (byte) 0x40, (byte) 0xd4, (byte) 0x51, (byte) 0x87, + (byte) 0xe1, (byte) 0xb7, (byte) 0x4f, (byte) 0x91, (byte) 0x75, (byte) 0x5b, + (byte) 0x03, (byte) 0x9d, (byte) 0xa1, (byte) 0xd4, (byte) 0x00, (byte) 0x05, + (byte) 0x79, (byte) 0x42, (byte) 0x93, (byte) 0x76 + }; + + /* + * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt \ + * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha512 -pkeyopt rsa_mgf1_md:sha512 \ + * | xxd -p -i | sed 's/0x/(byte) 0x/g' + */ + public static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512 = new byte[] { + (byte) 0x75, (byte) 0x0f, (byte) 0xf9, (byte) 0x21, (byte) 0xca, (byte) 0xcc, + (byte) 0x0e, (byte) 0x13, (byte) 0x9e, (byte) 0x38, (byte) 0xa4, (byte) 0xa7, + (byte) 0xee, (byte) 0x61, (byte) 0x6d, (byte) 0x56, (byte) 0xea, (byte) 0x36, + (byte) 0xeb, (byte) 0xec, (byte) 0xfa, (byte) 0x1a, (byte) 0xeb, (byte) 0x0c, + (byte) 0xb2, (byte) 0x58, (byte) 0x9d, (byte) 0xde, (byte) 0x47, (byte) 0x27, + (byte) 0x2d, (byte) 0xbd, (byte) 0x8b, (byte) 0xa7, (byte) 0xf1, (byte) 0x8b, + (byte) 0xba, (byte) 0x4c, (byte) 0xab, (byte) 0x39, (byte) 0x6a, (byte) 0x82, + (byte) 0x0d, (byte) 0xaf, (byte) 0x4c, (byte) 0xde, (byte) 0xdb, (byte) 0x5e, + (byte) 0xdb, (byte) 0x08, (byte) 0x98, (byte) 0x06, (byte) 0xc5, (byte) 0x99, + (byte) 0xb6, (byte) 0x6d, (byte) 0xbc, (byte) 0x5b, (byte) 0xf9, (byte) 0xe4, + (byte) 0x97, (byte) 0x0b, (byte) 0xba, (byte) 0xe3, (byte) 0x17, (byte) 0xa9, + (byte) 0x3c, (byte) 0x4b, (byte) 0x21, (byte) 0xd8, (byte) 0x29, (byte) 0xf8, + (byte) 0xa7, (byte) 0x1c, (byte) 0x15, (byte) 0xd7, (byte) 0xf6, (byte) 0xfc, + (byte) 0x53, (byte) 0x64, (byte) 0x97, (byte) 0x9e, (byte) 0x22, (byte) 0xb1, + (byte) 0x93, (byte) 0x26, (byte) 0x80, (byte) 0xdc, (byte) 0xaa, (byte) 0x1b, + (byte) 0xae, (byte) 0x69, (byte) 0x0f, (byte) 0x74, (byte) 0x3d, (byte) 0x61, + (byte) 0x80, (byte) 0x68, (byte) 0xb8, (byte) 0xaf, (byte) 0x63, (byte) 0x72, + (byte) 0x37, (byte) 0x4f, (byte) 0xf3, (byte) 0x29, (byte) 0x4a, (byte) 0x75, + (byte) 0x4f, (byte) 0x29, (byte) 0x40, (byte) 0x01, (byte) 0xd3, (byte) 0xc6, + (byte) 0x56, (byte) 0x1a, (byte) 0xaf, (byte) 0xc3, (byte) 0xb3, (byte) 0xd2, + (byte) 0xb9, (byte) 0x91, (byte) 0x35, (byte) 0x1b, (byte) 0x89, (byte) 0x4c, + (byte) 0x61, (byte) 0xa2, (byte) 0x8e, (byte) 0x6f, (byte) 0x12, (byte) 0x4a, + (byte) 0x10, (byte) 0xc2, (byte) 0xcc, (byte) 0xab, (byte) 0x51, (byte) 0xec, + (byte) 0x1b, (byte) 0xb5, (byte) 0xfe, (byte) 0x20, (byte) 0x16, (byte) 0xb2, + (byte) 0xc5, (byte) 0x0f, (byte) 0xe1, (byte) 0x6a, (byte) 0xb4, (byte) 0x6c, + (byte) 0x27, (byte) 0xd9, (byte) 0x42, (byte) 0xb9, (byte) 0xb6, (byte) 0x55, + (byte) 0xa8, (byte) 0xbc, (byte) 0x1c, (byte) 0x32, (byte) 0x54, (byte) 0x84, + (byte) 0xec, (byte) 0x1e, (byte) 0x95, (byte) 0xd8, (byte) 0xae, (byte) 0xca, + (byte) 0xc1, (byte) 0xad, (byte) 0x4c, (byte) 0x65, (byte) 0xd6, (byte) 0xc2, + (byte) 0x19, (byte) 0x66, (byte) 0xad, (byte) 0x9f, (byte) 0x55, (byte) 0x15, + (byte) 0xe1, (byte) 0x5d, (byte) 0x8f, (byte) 0xab, (byte) 0x18, (byte) 0x68, + (byte) 0x42, (byte) 0x7c, (byte) 0x48, (byte) 0xb7, (byte) 0x2c, (byte) 0xfd, + (byte) 0x1a, (byte) 0x07, (byte) 0xa1, (byte) 0x6a, (byte) 0xfb, (byte) 0x81, + (byte) 0xc6, (byte) 0x93, (byte) 0xbf, (byte) 0xa3, (byte) 0x5d, (byte) 0xfd, + (byte) 0xce, (byte) 0xf3, (byte) 0x17, (byte) 0x26, (byte) 0xf0, (byte) 0xda, + (byte) 0x0e, (byte) 0xd1, (byte) 0x86, (byte) 0x9d, (byte) 0x61, (byte) 0xd1, + (byte) 0x8a, (byte) 0xdb, (byte) 0x36, (byte) 0x39, (byte) 0x1c, (byte) 0xd4, + (byte) 0x99, (byte) 0x53, (byte) 0x30, (byte) 0x5a, (byte) 0x01, (byte) 0xf4, + (byte) 0xa0, (byte) 0xca, (byte) 0x94, (byte) 0x72, (byte) 0x3d, (byte) 0xe3, + (byte) 0x50, (byte) 0x95, (byte) 0xcb, (byte) 0xa9, (byte) 0x37, (byte) 0xeb, + (byte) 0x66, (byte) 0x21, (byte) 0x20, (byte) 0x2e, (byte) 0xf2, (byte) 0xfd, + (byte) 0xfa, (byte) 0x54, (byte) 0xbf, (byte) 0x17, (byte) 0x23, (byte) 0xbb, + (byte) 0x9e, (byte) 0x77, (byte) 0xe0, (byte) 0xaa + }; + + /* + * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha512 -pkeyopt rsa_mgf1_md:sha512 -pkeyopt rsa_oaep_label:010203FFA00A | xxd -p -i | sed 's/0x/(byte) 0x/g' + */ + public static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512_LABEL = new byte[] { + (byte) 0x31, (byte) 0x3b, (byte) 0x23, (byte) 0xcf, (byte) 0x40, (byte) 0xfe, + (byte) 0x15, (byte) 0x94, (byte) 0xd6, (byte) 0x81, (byte) 0x21, (byte) 0x69, + (byte) 0x8e, (byte) 0x58, (byte) 0xd5, (byte) 0x0f, (byte) 0xa8, (byte) 0x72, + (byte) 0x94, (byte) 0x13, (byte) 0xfe, (byte) 0xf9, (byte) 0xa1, (byte) 0x47, + (byte) 0x49, (byte) 0x91, (byte) 0xcb, (byte) 0x66, (byte) 0xe6, (byte) 0x5d, + (byte) 0x02, (byte) 0xad, (byte) 0xd4, (byte) 0x2f, (byte) 0x4f, (byte) 0xab, + (byte) 0xb7, (byte) 0x9e, (byte) 0xc0, (byte) 0xf0, (byte) 0x3d, (byte) 0x66, + (byte) 0x0e, (byte) 0x20, (byte) 0x82, (byte) 0x7f, (byte) 0x22, (byte) 0x8f, + (byte) 0x81, (byte) 0xba, (byte) 0x47, (byte) 0xc7, (byte) 0xaf, (byte) 0xb6, + (byte) 0x0e, (byte) 0x78, (byte) 0xe3, (byte) 0x30, (byte) 0xd7, (byte) 0x6c, + (byte) 0x81, (byte) 0xc2, (byte) 0x05, (byte) 0x7e, (byte) 0xe9, (byte) 0xac, + (byte) 0x8d, (byte) 0x45, (byte) 0x25, (byte) 0xe8, (byte) 0x26, (byte) 0x39, + (byte) 0x88, (byte) 0x64, (byte) 0x2e, (byte) 0xc6, (byte) 0xed, (byte) 0xd4, + (byte) 0xad, (byte) 0x94, (byte) 0xc8, (byte) 0x4e, (byte) 0x4a, (byte) 0x71, + (byte) 0x1e, (byte) 0x11, (byte) 0x14, (byte) 0x03, (byte) 0x56, (byte) 0x02, + (byte) 0x28, (byte) 0x32, (byte) 0x8f, (byte) 0xe2, (byte) 0x16, (byte) 0x4a, + (byte) 0x62, (byte) 0xa6, (byte) 0x9a, (byte) 0x8d, (byte) 0xf8, (byte) 0x33, + (byte) 0x35, (byte) 0xa2, (byte) 0xc7, (byte) 0x70, (byte) 0xcc, (byte) 0x26, + (byte) 0x1e, (byte) 0x4d, (byte) 0x9c, (byte) 0x4e, (byte) 0x2b, (byte) 0xe8, + (byte) 0xfd, (byte) 0x07, (byte) 0x33, (byte) 0x15, (byte) 0x53, (byte) 0x11, + (byte) 0x5c, (byte) 0x6f, (byte) 0x5d, (byte) 0x23, (byte) 0x7b, (byte) 0x3f, + (byte) 0x73, (byte) 0xff, (byte) 0xf4, (byte) 0xbe, (byte) 0x1f, (byte) 0xe6, + (byte) 0x5a, (byte) 0xb8, (byte) 0x2b, (byte) 0xd2, (byte) 0xbe, (byte) 0xa0, + (byte) 0x91, (byte) 0x5d, (byte) 0xca, (byte) 0x89, (byte) 0xb3, (byte) 0xce, + (byte) 0x0a, (byte) 0x2b, (byte) 0xce, (byte) 0xb9, (byte) 0xbe, (byte) 0x5d, + (byte) 0xb2, (byte) 0xc2, (byte) 0xd6, (byte) 0xa9, (byte) 0xbc, (byte) 0x37, + (byte) 0xed, (byte) 0x9a, (byte) 0xba, (byte) 0x35, (byte) 0xf8, (byte) 0x6e, + (byte) 0x63, (byte) 0x76, (byte) 0xd1, (byte) 0x12, (byte) 0xf5, (byte) 0x89, + (byte) 0xf0, (byte) 0x13, (byte) 0x86, (byte) 0xe7, (byte) 0x1b, (byte) 0x94, + (byte) 0xcb, (byte) 0xc8, (byte) 0x5c, (byte) 0x4c, (byte) 0x1b, (byte) 0x8a, + (byte) 0x2d, (byte) 0x6b, (byte) 0x24, (byte) 0x1a, (byte) 0x38, (byte) 0x14, + (byte) 0x77, (byte) 0x49, (byte) 0xe5, (byte) 0x08, (byte) 0x25, (byte) 0xe4, + (byte) 0xa6, (byte) 0xcf, (byte) 0x62, (byte) 0xfd, (byte) 0x66, (byte) 0x28, + (byte) 0xf0, (byte) 0x3a, (byte) 0x9c, (byte) 0x31, (byte) 0xef, (byte) 0x48, + (byte) 0x2a, (byte) 0xd3, (byte) 0x3e, (byte) 0x29, (byte) 0xfa, (byte) 0x18, + (byte) 0x8f, (byte) 0xd6, (byte) 0xaa, (byte) 0x1d, (byte) 0x10, (byte) 0xcd, + (byte) 0x35, (byte) 0x25, (byte) 0x92, (byte) 0x48, (byte) 0xa0, (byte) 0x2c, + (byte) 0xc1, (byte) 0x31, (byte) 0xeb, (byte) 0x47, (byte) 0x5b, (byte) 0x22, + (byte) 0x52, (byte) 0x7c, (byte) 0xf5, (byte) 0xec, (byte) 0x76, (byte) 0x90, + (byte) 0x94, (byte) 0x58, (byte) 0xd9, (byte) 0xd6, (byte) 0xe0, (byte) 0x0a, + (byte) 0x3f, (byte) 0x09, (byte) 0x98, (byte) 0x03, (byte) 0xc5, (byte) 0x07, + (byte) 0x8f, (byte) 0x89, (byte) 0x1e, (byte) 0x62, (byte) 0x2c, (byte) 0xea, + (byte) 0x17, (byte) 0x0a, (byte) 0x2e, (byte) 0x68 + }; public void testRSA_ECB_NoPadding_Private_OnlyDoFinal_Success() throws Exception { for (String provider : RSA_PROVIDERS) { @@ -2127,11 +2745,7 @@ public void testRSA_ECB_NoPadding_Private_OnlyDoFinal_Success() throws Exception } private void testRSA_ECB_NoPadding_Private_OnlyDoFinal_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus, - RSA_2048_privateExponent); - - final PrivateKey privKey = kf.generatePrivate(keySpec); + final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2158,11 +2772,7 @@ public void testRSA_ECB_NoPadding_Private_UpdateThenEmptyDoFinal_Success() throw } private void testRSA_ECB_NoPadding_Private_UpdateThenEmptyDoFinal_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus, - RSA_2048_privateExponent); - - final PrivateKey privKey = kf.generatePrivate(keySpec); + final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2193,11 +2803,7 @@ public void testRSA_ECB_NoPadding_Private_SingleByteUpdateThenEmptyDoFinal_Succe private void testRSA_ECB_NoPadding_Private_SingleByteUpdateThenEmptyDoFinal_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus, - RSA_2048_privateExponent); - - final PrivateKey privKey = kf.generatePrivate(keySpec); + final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2231,10 +2837,7 @@ public void testRSA_ECB_NoPadding_Private_OnlyDoFinalWithOffset_Success() throws } private void testRSA_ECB_NoPadding_Private_OnlyDoFinalWithOffset_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus, - RSA_2048_privateExponent); - final PrivateKey privKey = kf.generatePrivate(keySpec); + final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2268,10 +2871,7 @@ public void testRSA_ECB_NoPadding_Public_OnlyDoFinal_Success() throws Exception } private void testRSA_ECB_NoPadding_Public_OnlyDoFinal_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent); - - final PublicKey privKey = kf.generatePublic(keySpec); + final PublicKey pubKey = (PublicKey) getEncryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2280,11 +2880,11 @@ private void testRSA_ECB_NoPadding_Public_OnlyDoFinal_Success(String provider) t * distinction made here. It's all keyed off of what kind of key you're * using. ENCRYPT_MODE and DECRYPT_MODE are the same. */ - c.init(Cipher.ENCRYPT_MODE, privKey); + c.init(Cipher.ENCRYPT_MODE, pubKey); byte[] encrypted = c.doFinal(RSA_Vector1_Encrypt_Private); assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, RSA_2048_Vector1, encrypted); - c.init(Cipher.DECRYPT_MODE, privKey); + c.init(Cipher.DECRYPT_MODE, pubKey); encrypted = c.doFinal(RSA_Vector1_Encrypt_Private); assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, RSA_2048_Vector1, encrypted); } @@ -2296,10 +2896,7 @@ public void testRSA_ECB_NoPadding_Public_OnlyDoFinalWithOffset_Success() throws } private void testRSA_ECB_NoPadding_Public_OnlyDoFinalWithOffset_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent); - - final PublicKey pubKey = kf.generatePublic(keySpec); + final PublicKey pubKey = (PublicKey) getEncryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2334,10 +2931,7 @@ public void testRSA_ECB_NoPadding_Public_UpdateThenEmptyDoFinal_Success() throws } private void testRSA_ECB_NoPadding_Public_UpdateThenEmptyDoFinal_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent); - - final PublicKey privKey = kf.generatePublic(keySpec); + final PublicKey pubKey = (PublicKey) getEncryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2346,12 +2940,12 @@ private void testRSA_ECB_NoPadding_Public_UpdateThenEmptyDoFinal_Success(String * distinction made here. It's all keyed off of what kind of key you're * using. ENCRYPT_MODE and DECRYPT_MODE are the same. */ - c.init(Cipher.ENCRYPT_MODE, privKey); + c.init(Cipher.ENCRYPT_MODE, pubKey); c.update(RSA_Vector1_Encrypt_Private); byte[] encrypted = c.doFinal(); assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, RSA_2048_Vector1, encrypted); - c.init(Cipher.DECRYPT_MODE, privKey); + c.init(Cipher.DECRYPT_MODE, pubKey); c.update(RSA_Vector1_Encrypt_Private); encrypted = c.doFinal(); assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, RSA_2048_Vector1, encrypted); @@ -2366,10 +2960,7 @@ public void testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Succes private void testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent); - - final PublicKey privKey = kf.generatePublic(keySpec); + final PublicKey pubKey = (PublicKey) getEncryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2378,7 +2969,7 @@ private void testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Succe * distinction made here. It's all keyed off of what kind of key you're * using. ENCRYPT_MODE and DECRYPT_MODE are the same. */ - c.init(Cipher.ENCRYPT_MODE, privKey); + c.init(Cipher.ENCRYPT_MODE, pubKey); int i; for (i = 0; i < RSA_Vector1_Encrypt_Private.length / 2; i++) { c.update(RSA_Vector1_Encrypt_Private, i, 1); @@ -2386,7 +2977,7 @@ private void testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Succe byte[] encrypted = c.doFinal(RSA_Vector1_Encrypt_Private, i, RSA_2048_Vector1.length - i); assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, RSA_2048_Vector1, encrypted); - c.init(Cipher.DECRYPT_MODE, privKey); + c.init(Cipher.DECRYPT_MODE, pubKey); for (i = 0; i < RSA_Vector1_Encrypt_Private.length / 2; i++) { c.update(RSA_Vector1_Encrypt_Private, i, 1); } @@ -2401,10 +2992,7 @@ public void testRSA_ECB_NoPadding_Public_TooSmall_Success() throws Exception { } private void testRSA_ECB_NoPadding_Public_TooSmall_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent); - - final PublicKey privKey = kf.generatePublic(keySpec); + final PublicKey pubKey = (PublicKey) getEncryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2413,12 +3001,12 @@ private void testRSA_ECB_NoPadding_Public_TooSmall_Success(String provider) thro * distinction made here. It's all keyed off of what kind of key you're * using. ENCRYPT_MODE and DECRYPT_MODE are the same. */ - c.init(Cipher.ENCRYPT_MODE, privKey); + c.init(Cipher.ENCRYPT_MODE, pubKey); byte[] encrypted = c.doFinal(TooShort_Vector); assertTrue("Encrypted should match expected", Arrays.equals(RSA_Vector1_ZeroPadded_Encrypted, encrypted)); - c.init(Cipher.DECRYPT_MODE, privKey); + c.init(Cipher.DECRYPT_MODE, pubKey); encrypted = c.doFinal(TooShort_Vector); assertTrue("Encrypted should match expected", Arrays.equals(RSA_Vector1_ZeroPadded_Encrypted, encrypted)); @@ -2431,11 +3019,7 @@ public void testRSA_ECB_NoPadding_Private_TooSmall_Success() throws Exception { } private void testRSA_ECB_NoPadding_Private_TooSmall_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus, - RSA_2048_privateExponent); - - final PrivateKey privKey = kf.generatePrivate(keySpec); + final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2481,11 +3065,7 @@ public void testRSA_ECB_NoPadding_Private_CombinedUpdateAndDoFinal_TooBig_Failur private void testRSA_ECB_NoPadding_Private_CombinedUpdateAndDoFinal_TooBig_Failure(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus, - RSA_2048_privateExponent); - - final PrivateKey privKey = kf.generatePrivate(keySpec); + final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2516,11 +3096,7 @@ public void testRSA_ECB_NoPadding_Private_UpdateInAndOutPlusDoFinal_TooBig_Failu private void testRSA_ECB_NoPadding_Private_UpdateInAndOutPlusDoFinal_TooBig_Failure(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus, - RSA_2048_privateExponent); - - final PrivateKey privKey = kf.generatePrivate(keySpec); + final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2552,11 +3128,7 @@ public void testRSA_ECB_NoPadding_Private_OnlyDoFinal_TooBig_Failure() throws Ex } private void testRSA_ECB_NoPadding_Private_OnlyDoFinal_TooBig_Failure(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus, - RSA_2048_privateExponent); - - final PrivateKey privKey = kf.generatePrivate(keySpec); + final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); @@ -2601,10 +3173,7 @@ private void testRSA_ECB_NoPadding_GetBlockSize_Success(String provider) throws } } - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus, - RSA_2048_publicExponent); - final PublicKey pubKey = kf.generatePublic(pubKeySpec); + final PublicKey pubKey = (PublicKey) getEncryptKey("RSA"); c.init(Cipher.ENCRYPT_MODE, pubKey); assertEquals(getExpectedBlockSize("RSA", Cipher.ENCRYPT_MODE, provider), c.getBlockSize()); } @@ -2631,10 +3200,7 @@ public void testRSA_ECB_NoPadding_GetOutputSize_Success() throws Exception { } private void testRSA_ECB_NoPadding_GetOutputSize_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus, - RSA_2048_publicExponent); - final PublicKey pubKey = kf.generatePublic(pubKeySpec); + final PublicKey pubKey = (PublicKey) getEncryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); c.init(Cipher.ENCRYPT_MODE, pubKey); @@ -2652,10 +3218,7 @@ public void testRSA_ECB_NoPadding_GetIV_Success() throws Exception { } private void testRSA_ECB_NoPadding_GetIV_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus, - RSA_2048_publicExponent); - final PublicKey pubKey = kf.generatePublic(pubKeySpec); + final PublicKey pubKey = (PublicKey) getEncryptKey("RSA"); Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); assertNull("ECB mode has no IV and should be null", c.getIV()); @@ -2672,11 +3235,6 @@ public void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success() throws Ex } private void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success(String provider) throws Exception { - KeyFactory kf = KeyFactory.getInstance("RSA"); - RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus, - RSA_2048_publicExponent); - final PublicKey pubKey = kf.generatePublic(pubKeySpec); - Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); assertNull("Parameters should be null", c.getParameters()); } @@ -2685,67 +3243,77 @@ private void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success(String pro * Test vector generation: * openssl rand -hex 16 | sed 's/\(..\)/(byte) 0x\1, /g' */ - private static final byte[] DES_112_KEY = new byte[] { + private static final SecretKeySpec DES_112_KEY = new SecretKeySpec(new byte[] { (byte) 0x6b, (byte) 0xb3, (byte) 0x85, (byte) 0x1c, (byte) 0x3d, (byte) 0x50, (byte) 0xd4, (byte) 0x95, (byte) 0x39, (byte) 0x48, (byte) 0x77, (byte) 0x30, (byte) 0x1a, (byte) 0xd7, (byte) 0x86, (byte) 0x57, - }; + }, "DESede"); /* * Test vector generation: * openssl rand -hex 24 | sed 's/\(..\)/(byte) 0x\1, /g' */ - private static final byte[] DES_168_KEY = new byte[] { + private static final SecretKeySpec DES_168_KEY = new SecretKeySpec(new byte[] { (byte) 0xfe, (byte) 0xd4, (byte) 0xd7, (byte) 0xc9, (byte) 0x8a, (byte) 0x13, (byte) 0x6a, (byte) 0xa8, (byte) 0x5a, (byte) 0xb8, (byte) 0x19, (byte) 0xb8, (byte) 0xcf, (byte) 0x3c, (byte) 0x5f, (byte) 0xe0, (byte) 0xa2, (byte) 0xf7, (byte) 0x7b, (byte) 0x65, (byte) 0x43, (byte) 0xc0, (byte) 0xc4, (byte) 0xe1, - }; + }, "DESede"); + + /* + * Test vector generation: + * openssl rand -hex 5 | sed 's/\(..\)/(byte) 0x\1, /g' + */ + private static final SecretKeySpec ARC4_40BIT_KEY = new SecretKeySpec(new byte[] { + (byte) 0x9c, (byte) 0xc8, (byte) 0xb9, (byte) 0x94, (byte) 0x98, + }, "ARC4"); + + /* + * Test vector generation: + * openssl rand -hex 24 | sed 's/\(..\)/(byte) 0x\1, /g' + */ + private static final SecretKeySpec ARC4_128BIT_KEY = new SecretKeySpec(new byte[] { + (byte) 0xbc, (byte) 0x0a, (byte) 0x3c, (byte) 0xca, (byte) 0xb5, (byte) 0x42, + (byte) 0xfa, (byte) 0x5d, (byte) 0x86, (byte) 0x5b, (byte) 0x44, (byte) 0x87, + (byte) 0x83, (byte) 0xd8, (byte) 0xcb, (byte) 0xd4, + }, "ARC4"); /* * Test vector generation: * openssl rand -hex 16 * echo '3d4f8970b1f27537f40a39298a41555f' | sed 's/\(..\)/(byte) 0x\1, /g' */ - private static final byte[] AES_128_KEY = new byte[] { + private static final SecretKeySpec AES_128_KEY = new SecretKeySpec(new byte[] { (byte) 0x3d, (byte) 0x4f, (byte) 0x89, (byte) 0x70, (byte) 0xb1, (byte) 0xf2, (byte) 0x75, (byte) 0x37, (byte) 0xf4, (byte) 0x0a, (byte) 0x39, (byte) 0x29, (byte) 0x8a, (byte) 0x41, (byte) 0x55, (byte) 0x5f, - }; + }, "AES"); /* * Test key generation: * openssl rand -hex 24 * echo '5a7a3d7e40b64ed996f7afa15f97fd595e27db6af428e342' | sed 's/\(..\)/(byte) 0x\1, /g' */ - private static final byte[] AES_192_KEY = new byte[] { + private static final SecretKeySpec AES_192_KEY = new SecretKeySpec(new byte[] { (byte) 0x5a, (byte) 0x7a, (byte) 0x3d, (byte) 0x7e, (byte) 0x40, (byte) 0xb6, (byte) 0x4e, (byte) 0xd9, (byte) 0x96, (byte) 0xf7, (byte) 0xaf, (byte) 0xa1, (byte) 0x5f, (byte) 0x97, (byte) 0xfd, (byte) 0x59, (byte) 0x5e, (byte) 0x27, (byte) 0xdb, (byte) 0x6a, (byte) 0xf4, (byte) 0x28, (byte) 0xe3, (byte) 0x42, - }; + }, "AES"); /* * Test key generation: * openssl rand -hex 32 * echo 'ec53c6d51d2c4973585fb0b8e51cd2e39915ff07a1837872715d6121bf861935' | sed 's/\(..\)/(byte) 0x\1, /g' */ - private static final byte[] AES_256_KEY = new byte[] { + private static final SecretKeySpec AES_256_KEY = new SecretKeySpec(new byte[] { (byte) 0xec, (byte) 0x53, (byte) 0xc6, (byte) 0xd5, (byte) 0x1d, (byte) 0x2c, (byte) 0x49, (byte) 0x73, (byte) 0x58, (byte) 0x5f, (byte) 0xb0, (byte) 0xb8, (byte) 0xe5, (byte) 0x1c, (byte) 0xd2, (byte) 0xe3, (byte) 0x99, (byte) 0x15, (byte) 0xff, (byte) 0x07, (byte) 0xa1, (byte) 0x83, (byte) 0x78, (byte) 0x72, (byte) 0x71, (byte) 0x5d, (byte) 0x61, (byte) 0x21, (byte) 0xbf, (byte) 0x86, (byte) 0x19, (byte) 0x35, - }; - - private static final String[] AES_MODES = new String[] { - "AES/ECB", - "AES/CBC", - "AES/CFB", - "AES/CTR", - "AES/OFB", - }; + }, "AES"); /* * Test vector generation: @@ -2757,6 +3325,7 @@ private void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success(String pro (byte) 0x73, (byte) 0x21 }; + /* * Test vector generation: take DES_Plaintext1 and PKCS #5 pad it manually (it's not hard). */ @@ -2802,6 +3371,39 @@ private void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success(String pro (byte) 0x27, (byte) 0xB0, (byte) 0xED, (byte) 0x47 }; + + /* + * Test vector generation: + * echo -n 'Plaintext for arc4' | recode ../x1 | sed 's/0x/(byte) 0x/g' + */ + private static final byte[] ARC4_Plaintext1 = new byte[] { + (byte) 0x50, (byte) 0x6C, (byte) 0x61, (byte) 0x69, (byte) 0x6E, (byte) 0x74, + (byte) 0x65, (byte) 0x78, (byte) 0x74, (byte) 0x20, (byte) 0x66, (byte) 0x6F, + (byte) 0x72, (byte) 0x20, (byte) 0x61, (byte) 0x72, (byte) 0x63, (byte) 0x34 + }; + + /* + * Test vector generation: + * echo -n 'Plaintext for arc4' | openssl enc -rc4-40 -K 9cc8b99498 | recode ../x1 \ + * | sed 's/0x/(byte) 0x/g' + */ + private static final byte[] ARC4_Plaintext1_Encrypted_With_ARC4_40Bit_Key = new byte[] { + (byte) 0x63, (byte) 0xF7, (byte) 0x11, (byte) 0x90, (byte) 0x63, (byte) 0xEF, + (byte) 0x5E, (byte) 0xB3, (byte) 0x93, (byte) 0xB3, (byte) 0x46, (byte) 0x3F, + (byte) 0x1B, (byte) 0x02, (byte) 0x53, (byte) 0x9B, (byte) 0xD9, (byte) 0xE0 + }; + + /* + * Test vector generation: + * echo -n 'Plaintext for arc4' | openssl enc -rc4 -K bc0a3ccab542fa5d865b448783d8cbd4 \ + * | recode ../x1 | sed 's/0x/(byte) 0x/g' + */ + private static final byte[] ARC4_Plaintext1_Encrypted_With_ARC4_128Bit_Key = new byte[] { + (byte) 0x25, (byte) 0x14, (byte) 0xA9, (byte) 0x72, (byte) 0x4D, (byte) 0xA9, + (byte) 0xF6, (byte) 0xA7, (byte) 0x2F, (byte) 0xB7, (byte) 0x0D, (byte) 0x60, + (byte) 0x09, (byte) 0xBE, (byte) 0x41, (byte) 0x9B, (byte) 0x32, (byte) 0x2B + }; + /* * Test vector creation: * echo -n 'Hello, world!' | recode ../x1 | sed 's/0x/(byte) 0x/g' @@ -2835,11 +3437,11 @@ private void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success(String pro /* * Taken from BoringSSL test vectors. */ - private static final byte[] AES_128_GCM_TestVector_1_Key = new byte[] { + private static final SecretKeySpec AES_128_GCM_TestVector_1_Key = new SecretKeySpec(new byte[] { (byte) 0xca, (byte) 0xbd, (byte) 0xcf, (byte) 0x54, (byte) 0x1a, (byte) 0xeb, (byte) 0xf9, (byte) 0x17, (byte) 0xba, (byte) 0xc0, (byte) 0x19, (byte) 0xf1, (byte) 0x39, (byte) 0x25, (byte) 0xd2, (byte) 0x67, - }; + }, "AES"); /* * Taken from BoringSSL test vectors. @@ -3006,11 +3608,11 @@ private void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success(String pro private static class CipherTestParam { public final String transformation; - public final byte[] key; + public final AlgorithmParameterSpec spec; - public final String keyAlgorithm; + public final Key encryptKey; - public final byte[] iv; + public final Key decryptKey; public final byte[] aad; @@ -3020,16 +3622,41 @@ private static class CipherTestParam { public final byte[] plaintextPadded; - public CipherTestParam(String transformation, String keyAlgorithm, byte[] key, byte[] iv, - byte[] aad, byte[] plaintext, byte[] plaintextPadded, byte[] ciphertext) { + public final boolean isStreamCipher; + + public CipherTestParam(String transformation, AlgorithmParameterSpec spec, Key encryptKey, + Key decryptKey, byte[] aad, byte[] plaintext, byte[] plaintextPadded, + byte[] ciphertext, boolean isStreamCipher) { this.transformation = transformation.toUpperCase(Locale.ROOT); - this.keyAlgorithm = keyAlgorithm; - this.key = key; - this.iv = iv; + this.spec = spec; + this.encryptKey = encryptKey; + this.decryptKey = decryptKey; this.aad = aad; this.plaintext = plaintext; this.plaintextPadded = plaintextPadded; this.ciphertext = ciphertext; + this.isStreamCipher = isStreamCipher; + } + + public CipherTestParam(String transformation, AlgorithmParameterSpec spec, Key key, + byte[] aad, byte[] plaintext, byte[] plaintextPadded, byte[] ciphertext, + boolean isStreamCipher) { + this(transformation, spec, key, key, aad, plaintext, plaintextPadded, ciphertext, + isStreamCipher); + } + + public CipherTestParam(String transformation, AlgorithmParameterSpec spec, Key key, + byte[] aad, byte[] plaintext, byte[] plaintextPadded, byte[] ciphertext) { + this(transformation, spec, key, aad, plaintext, plaintextPadded, ciphertext, + false /* isStreamCipher */); + } + } + + private static class OAEPCipherTestParam extends CipherTestParam { + public OAEPCipherTestParam(String transformation, OAEPParameterSpec spec, + PublicKey encryptKey, PrivateKey decryptKey, byte[] plaintext, byte[] ciphertext) { + super(transformation, spec, encryptKey, decryptKey, null, plaintext, plaintext, ciphertext, + false); } } @@ -3037,9 +3664,8 @@ public CipherTestParam(String transformation, String keyAlgorithm, byte[] key, b static { DES_CIPHER_TEST_PARAMS.add(new CipherTestParam( "DESede/CBC/PKCS5Padding", - "DESede", + new IvParameterSpec(DES_IV1), DES_112_KEY, - DES_IV1, null, DES_Plaintext1, DES_Plaintext1_PKCS5_Padded, @@ -3047,9 +3673,8 @@ public CipherTestParam(String transformation, String keyAlgorithm, byte[] key, b )); DES_CIPHER_TEST_PARAMS.add(new CipherTestParam( "DESede/CBC/PKCS5Padding", - "DESede", + new IvParameterSpec(DES_IV1), DES_168_KEY, - DES_IV1, null, DES_Plaintext1, DES_Plaintext1_PKCS5_Padded, @@ -3057,44 +3682,81 @@ public CipherTestParam(String transformation, String keyAlgorithm, byte[] key, b )); } + private static List ARC4_CIPHER_TEST_PARAMS = new ArrayList(); + static { + ARC4_CIPHER_TEST_PARAMS.add(new CipherTestParam( + "ARC4", + null, + ARC4_40BIT_KEY, + null, // aad + ARC4_Plaintext1, + null, // padded + ARC4_Plaintext1_Encrypted_With_ARC4_40Bit_Key, + true /*isStreamCipher */ + )); + ARC4_CIPHER_TEST_PARAMS.add(new CipherTestParam( + "ARC4", + null, + ARC4_128BIT_KEY, + null, // aad + ARC4_Plaintext1, + null, // padded + ARC4_Plaintext1_Encrypted_With_ARC4_128Bit_Key, + true /*isStreamCipher */ + )); + } + private static List CIPHER_TEST_PARAMS = new ArrayList(); static { - CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS5Padding", "AES", AES_128_KEY, + CIPHER_TEST_PARAMS.add(new CipherTestParam( + "AES/ECB/PKCS5Padding", null, + AES_128_KEY, null, AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext, AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded, AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted)); // PKCS#5 is assumed to be equivalent to PKCS#7 -- same test vectors are thus used for both. - CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS7Padding", "AES", AES_128_KEY, + CIPHER_TEST_PARAMS.add(new CipherTestParam( + "AES/ECB/PKCS7Padding", null, + AES_128_KEY, null, AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext, AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded, AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted)); - CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/GCM/NOPADDING", - "AES", + CIPHER_TEST_PARAMS.add(new CipherTestParam( + "AES/GCM/NOPADDING", + new GCMParameterSpec( + (AES_128_GCM_TestVector_1_Encrypted.length - + AES_128_GCM_TestVector_1_Plaintext.length) * 8, + AES_128_GCM_TestVector_1_IV), AES_128_GCM_TestVector_1_Key, - AES_128_GCM_TestVector_1_IV, AES_128_GCM_TestVector_1_AAD, AES_128_GCM_TestVector_1_Plaintext, AES_128_GCM_TestVector_1_Plaintext, AES_128_GCM_TestVector_1_Encrypted)); if (IS_UNLIMITED) { - CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CTR/NoPadding", "AES", AES_192_KEY, - AES_192_CTR_NoPadding_TestVector_1_IV, + CIPHER_TEST_PARAMS.add(new CipherTestParam( + "AES/CTR/NoPadding", + new IvParameterSpec(AES_192_CTR_NoPadding_TestVector_1_IV), + AES_192_KEY, null, AES_192_CTR_NoPadding_TestVector_1_Plaintext, AES_192_CTR_NoPadding_TestVector_1_Plaintext, AES_192_CTR_NoPadding_TestVector_1_Ciphertext)); - CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS5Padding", "AES", AES_256_KEY, - AES_256_CBC_PKCS5Padding_TestVector_1_IV, + CIPHER_TEST_PARAMS.add(new CipherTestParam( + "AES/CBC/PKCS5Padding", + new IvParameterSpec(AES_256_CBC_PKCS5Padding_TestVector_1_IV), + AES_256_KEY, null, AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext, AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded, AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext)); - CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS7Padding", "AES", AES_256_KEY, - AES_256_CBC_PKCS5Padding_TestVector_1_IV, + CIPHER_TEST_PARAMS.add(new CipherTestParam( + "AES/CBC/PKCS7Padding", + new IvParameterSpec(AES_256_CBC_PKCS5Padding_TestVector_1_IV), + AES_256_KEY, null, AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext, AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded, @@ -3102,6 +3764,59 @@ public CipherTestParam(String transformation, String keyAlgorithm, byte[] key, b } } + private static final List RSA_OAEP_CIPHER_TEST_PARAMS = new ArrayList(); + static { + addRsaOaepTest("SHA-1", MGF1ParameterSpec.SHA1, RSA_Vector2_OAEP_SHA1_MGF1_SHA1); + addRsaOaepTest("SHA-256", MGF1ParameterSpec.SHA1, RSA_Vector2_OAEP_SHA256_MGF1_SHA1); + addRsaOaepTest("SHA-224", MGF1ParameterSpec.SHA224, RSA_Vector2_OAEP_SHA224_MGF1_SHA224); + addRsaOaepTest("SHA-256", MGF1ParameterSpec.SHA256, RSA_Vector2_OAEP_SHA256_MGF1_SHA256); + addRsaOaepTest("SHA-384", MGF1ParameterSpec.SHA384, RSA_Vector2_OAEP_SHA384_MGF1_SHA384); + addRsaOaepTest("SHA-512", MGF1ParameterSpec.SHA512, RSA_Vector2_OAEP_SHA512_MGF1_SHA512); + addRsaOaepTest("SHA-256", MGF1ParameterSpec.SHA1, RSA_Vector2_OAEP_SHA256_MGF1_SHA1_LABEL, + new byte[] { 0x01, 0x02, 0x03, (byte) 0xFF, (byte) 0xA0, 0x0A }); + addRsaOaepTest("SHA-512", MGF1ParameterSpec.SHA512, RSA_Vector2_OAEP_SHA512_MGF1_SHA512_LABEL, + new byte[] { 0x01, 0x02, 0x03, (byte) 0xFF, (byte) 0xA0, 0x0A }); + } + + private static void addRsaOaepTest(String digest, MGF1ParameterSpec mgf1Spec, byte[] vector) { + addRsaOaepTest(digest, mgf1Spec, vector, null); + } + + private static void addRsaOaepTest(String digest, MGF1ParameterSpec mgf1Spec, byte[] vector, byte[] label) { + final PSource pSource; + if (label == null) { + pSource = PSource.PSpecified.DEFAULT; + } else { + pSource = new PSource.PSpecified(label); + } + + if (mgf1Spec.getDigestAlgorithm().equals(digest) && label == null) { + RSA_OAEP_CIPHER_TEST_PARAMS.add(new OAEPCipherTestParam( + "RSA/ECB/OAEPWith" + digest + "AndMGF1Padding", + null, + (PublicKey) getEncryptKey("RSA"), + (PrivateKey) getDecryptKey("RSA"), + RSA_Vector2_Plaintext, + vector)); + } + + RSA_OAEP_CIPHER_TEST_PARAMS.add(new OAEPCipherTestParam( + "RSA/ECB/OAEPWith" + digest + "AndMGF1Padding", + new OAEPParameterSpec(digest, "MGF1", mgf1Spec, pSource), + (PublicKey) getEncryptKey("RSA"), + (PrivateKey) getDecryptKey("RSA"), + RSA_Vector2_Plaintext, + vector)); + + RSA_OAEP_CIPHER_TEST_PARAMS.add(new OAEPCipherTestParam( + "RSA/ECB/OAEPPadding", + new OAEPParameterSpec(digest, "MGF1", mgf1Spec, pSource), + (PublicKey) getEncryptKey("RSA"), + (PrivateKey) getDecryptKey("RSA"), + RSA_Vector2_Plaintext, + vector)); + } + public void testCipher_Success() throws Exception { for (String provider : AES_PROVIDERS) { testCipher_Success(provider); @@ -3109,6 +3824,10 @@ public void testCipher_Success() throws Exception { testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired( DES_CIPHER_TEST_PARAMS); + testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired( + ARC4_CIPHER_TEST_PARAMS); + testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired( + RSA_OAEP_CIPHER_TEST_PARAMS); } /** @@ -3121,19 +3840,39 @@ private void testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequ ByteArrayOutputStream errBuffer = new ByteArrayOutputStream(); PrintStream out = new PrintStream(errBuffer); for (CipherTestParam testVector : testVectors) { - Provider[] providers = Security.getProviders("Cipher." + testVector.transformation); - if ((providers == null) || (providers.length == 0)) { + ArrayList providers = new ArrayList<>(); + + Provider[] providerArray = Security.getProviders("Cipher." + testVector.transformation); + if (providerArray != null) { + Collections.addAll(providers, providerArray); + } + + if (testVector.transformation.indexOf('/') > 0) { + Provider[] baseTransformProviderArray = Security.getProviders("Cipher." + + testVector.transformation.substring( + 0, testVector.transformation.indexOf('/'))); + if (baseTransformProviderArray != null) { + Collections.addAll(providers, baseTransformProviderArray); + } + } + + if (providers.isEmpty()) { out.append("No providers offer " + testVector.transformation + "\n"); continue; } + for (Provider provider : providers) { + // Do not test AndroidKeyStore's Signature. It needs an AndroidKeyStore-specific key. + // It's OKish not to test AndroidKeyStore's Signature here because it's tested + // by cts/tests/test/keystore. + if (provider.getName().startsWith("AndroidKeyStore")) { + continue; + } + try { checkCipher(testVector, provider.getName()); } catch (Throwable e) { - out.append("Error encountered checking " + testVector.transformation - + ", keySize=" + (testVector.key.length * 8) + " with provider " - + provider.getName() + "\n"); - e.printStackTrace(out); + logTestFailure(out, provider.getName(), testVector, e); } } } @@ -3149,12 +3888,8 @@ private void testCipher_Success(String provider) throws Exception { for (CipherTestParam p : CIPHER_TEST_PARAMS) { try { checkCipher(p, provider); - } catch (Exception e) { - out.append("Error encountered checking " + p.transformation + ", keySize=" - + (p.key.length * 8) - + " with provider " + provider + "\n"); - - e.printStackTrace(out); + } catch (Throwable e) { + logTestFailure(out, provider, p, e); } } out.flush(); @@ -3163,34 +3898,84 @@ private void testCipher_Success(String provider) throws Exception { } } - private void checkCipher(CipherTestParam p, String provider) throws Exception { - SecretKey key = new SecretKeySpec(p.key, p.keyAlgorithm); - Cipher c = Cipher.getInstance(p.transformation, provider); + private void logTestFailure(PrintStream logStream, String provider, CipherTestParam params, + Throwable e) { + logStream.append("Error encountered checking " + params.transformation); - AlgorithmParameterSpec spec = null; - if (p.iv != null) { - if (isAEAD(p.transformation)) { - spec = new GCMParameterSpec((p.ciphertext.length - p.plaintext.length) * 8, p.iv); - } else { - spec = new IvParameterSpec(p.iv); + if (params.encryptKey instanceof SecretKey) { + logStream.append(", keySize=" + (params.encryptKey.getEncoded().length * 8)); + } + + if (params.spec instanceof OAEPParameterSpec) { + OAEPParameterSpec oaepSpec = (OAEPParameterSpec) params.spec; + logStream.append(", OAEPSpec{digest=" + oaepSpec.getDigestAlgorithm() + ", mgfAlg=" + + oaepSpec.getMGFAlgorithm()); + if (oaepSpec.getMGFParameters() instanceof MGF1ParameterSpec) { + MGF1ParameterSpec mgf1Spec = (MGF1ParameterSpec) oaepSpec.getMGFParameters(); + logStream.append(", mgf1Hash=" + mgf1Spec.getDigestAlgorithm()); } + logStream.append(", pSource="); + PSource pSource = oaepSpec.getPSource(); + logStream.append(pSource.getAlgorithm()); + if (pSource.getAlgorithm().equals("PSpecified")) { + logStream.append(":{"); + logStream.append(Arrays.toString(((PSource.PSpecified) pSource).getValue())); + logStream.append('}'); + } + logStream.append('}'); } - c.init(Cipher.ENCRYPT_MODE, key, spec); + logStream.append(" with provider " + provider + "\n"); + e.printStackTrace(logStream); + } + + private void checkCipher(CipherTestParam p, String provider) throws Exception { + Cipher c = Cipher.getInstance(p.transformation, provider); + + c.init(Cipher.ENCRYPT_MODE, p.encryptKey, p.spec); + + // This doesn't quite work on OAEPPadding unless it's the default case, + // because its size depends on the message digest algorithms used. + if (!p.transformation.endsWith("OAEPPADDING")) { + assertEquals(p.transformation + " getBlockSize() ENCRYPT_MODE", + getExpectedBlockSize(p.transformation, Cipher.ENCRYPT_MODE, provider), + c.getBlockSize()); + } + assertTrue(p.transformation + " getOutputSize(0) ENCRYPT_MODE", + getExpectedOutputSize(p.transformation, Cipher.ENCRYPT_MODE, provider) <= c + .getOutputSize(0)); if (p.aad != null) { c.updateAAD(p.aad); } final byte[] actualCiphertext = c.doFinal(p.plaintext); - assertEquals(p.transformation + " " + provider, Arrays.toString(p.ciphertext), - Arrays.toString(actualCiphertext)); + if (!isRandomizedEncryption(p.transformation)) { + assertEquals(p.transformation + " " + provider, Arrays.toString(p.ciphertext), + Arrays.toString(actualCiphertext)); + } c = Cipher.getInstance(p.transformation, provider); - c.init(Cipher.ENCRYPT_MODE, key, spec); + c.init(Cipher.ENCRYPT_MODE, p.encryptKey, p.spec); + if (!(p instanceof OAEPCipherTestParam) || p.spec != null) { + assertCorrectAlgorithmParameters(provider, p.transformation, p.spec, c.getParameters()); + } + byte[] emptyCipherText = c.doFinal(); assertNotNull(emptyCipherText); - c.init(Cipher.DECRYPT_MODE, key, spec); + c.init(Cipher.DECRYPT_MODE, p.decryptKey, p.spec); + + assertEquals(p.transformation + " getBlockSize() DECRYPT_MODE", + getExpectedBlockSize(p.transformation, Cipher.DECRYPT_MODE, provider), + c.getBlockSize()); + + // This doesn't quite work on OAEPPadding unless it's the default case, + // because its size depends on the message digest algorithms used. + if (!p.transformation.endsWith("OAEPPADDING")) { + assertTrue(p.transformation + " getOutputSize(0) DECRYPT_MODE", + getExpectedOutputSize(p.transformation, Cipher.DECRYPT_MODE, provider) <= c + .getOutputSize(0)); + } if (!isAEAD(p.transformation)) { try { @@ -3203,17 +3988,23 @@ private void checkCipher(CipherTestParam p, String provider) throws Exception { try { byte[] emptyPlainText = c.doFinal(emptyCipherText); assertEquals(Arrays.toString(new byte[0]), Arrays.toString(emptyPlainText)); - } catch (AEADBadTagException e) { + } catch (AEADBadTagException maybe) { if (!"AndroidOpenSSL".equals(provider) || !isAEAD(p.transformation)) { - throw e; + throw maybe; + } + } catch (BadPaddingException maybe) { + // BC's OAEP has a bug where it doesn't support decrypt of a zero-length plaintext + if (!("BC".equals(provider) && p.transformation.contains("OAEP"))) { + throw maybe; } } - // empty decrypt - { - if (!isAEAD(p.transformation) + // decrypt an empty ciphertext; not valid for RSA + if (!p.transformation.contains("OAEP")) { + if ((!isAEAD(p.transformation) && (StandardNames.IS_RI || provider.equals("AndroidOpenSSL") || - (provider.equals("BC") && p.transformation.contains("/CTR/")))) { + (provider.equals("BC") && p.transformation.contains("/CTR/")))) + || p.transformation.equals("ARC4")) { assertEquals(Arrays.toString(new byte[0]), Arrays.toString(c.doFinal())); @@ -3252,7 +4043,7 @@ private void checkCipher(CipherTestParam p, String provider) throws Exception { } // Cipher might be in unspecified state from failures above. - c.init(Cipher.DECRYPT_MODE, key, spec); + c.init(Cipher.DECRYPT_MODE, p.decryptKey, p.spec); // .doFinal(input) { @@ -3301,10 +4092,11 @@ private void checkCipher(CipherTestParam p, String provider) throws Exception { Arrays.toString(Arrays.copyOfRange(actualPlaintext, 1, p.plaintext.length + 1))); } - if (!p.transformation.endsWith("NOPADDING")) { + if (!p.isStreamCipher && !p.transformation.endsWith("NOPADDING") + && !isRandomizedEncryption(p.transformation)) { Cipher cNoPad = Cipher.getInstance( getCipherTransformationWithNoPadding(p.transformation), provider); - cNoPad.init(Cipher.DECRYPT_MODE, key, spec); + cNoPad.init(Cipher.DECRYPT_MODE, p.decryptKey, p.spec); if (p.aad != null) { c.updateAAD(p.aad); @@ -3323,11 +4115,11 @@ private void checkCipher(CipherTestParam p, String provider) throws Exception { // Wrap it c = Cipher.getInstance(p.transformation, provider); - c.init(Cipher.WRAP_MODE, key, spec); + c.init(Cipher.WRAP_MODE, p.encryptKey, p.spec); byte[] cipherText = c.wrap(sk); // Unwrap it - c.init(Cipher.UNWRAP_MODE, key, spec); + c.init(Cipher.UNWRAP_MODE, p.decryptKey, p.spec); Key decryptedKey = c.unwrap(cipherText, sk.getAlgorithm(), Cipher.SECRET_KEY); assertEquals( @@ -3465,10 +4257,7 @@ private void testCipher_ShortBlock_Failure(String provider) throws Exception { try { checkCipher_ShortBlock_Failure(p, provider); } catch (Exception e) { - out.append("Error encountered checking " + p.transformation + ", keySize=" - + (p.key.length * 8) - + " with provider " + provider + "\n"); - e.printStackTrace(out); + logTestFailure(out, provider, p, e); } } out.flush(); @@ -3477,10 +4266,79 @@ private void testCipher_ShortBlock_Failure(String provider) throws Exception { } } + public void testCipher_DoFinal_wrapMode_Failure() throws Exception { + checkCipher_DoFinal_invalidMode_Failure(Cipher.WRAP_MODE); + } + + public void testCipher_DoFinal_unwrapMode_Failure() throws Exception { + checkCipher_DoFinal_invalidMode_Failure(Cipher.UNWRAP_MODE); + } + + /** + * Helper for testing that Cipher.doFinal() throws IllegalStateException when + * initialized in modes other than DECRYPT or ENCRYPT. + */ + private static void checkCipher_DoFinal_invalidMode_Failure(int opmode) throws Exception { + String msg = String.format(Locale.US, + "doFinal() should throw IllegalStateException [mode=%d]", opmode); + int bs = createAesCipher(opmode).getBlockSize(); + assertEquals(16, bs); // check test is set up correctly + try { + createAesCipher(opmode).doFinal(); + fail(msg); + } catch (IllegalStateException expected) { + } + + try { + createAesCipher(opmode).doFinal(new byte[0]); + fail(msg); + } catch (IllegalStateException expected) { + } + + try { + createAesCipher(opmode).doFinal(new byte[2 * bs], 0, bs); + fail(msg); + } catch (IllegalStateException expected) { + } + + try { + createAesCipher(opmode).doFinal(new byte[2 * bs], 0, bs, new byte[2 * bs], 0); + fail(msg); + } catch (IllegalStateException expected) { + } + } + + public void testCipher_Update_wrapMode_Failure() throws Exception { + checkCipher_Update_invalidMode_Failure(Cipher.WRAP_MODE); + } + + public void testCipher_Update_unwrapMode_Failure() throws Exception { + checkCipher_Update_invalidMode_Failure(Cipher.UNWRAP_MODE); + } + + /** + * Helper for testing that Cipher.update() throws IllegalStateException when + * initialized in modes other than DECRYPT or ENCRYPT. + */ + private static void checkCipher_Update_invalidMode_Failure(int opmode) throws Exception { + String msg = "update() should throw IllegalStateException [mode=" + opmode + "]"; + int bs = createAesCipher(opmode).getBlockSize(); + assertEquals(16, bs); // check test is set up correctly + assertIllegalStateException(msg, () -> createAesCipher(opmode).update(new byte[0])); + assertIllegalStateException(msg, () -> createAesCipher(opmode).update(new byte[2 * bs])); + assertIllegalStateException(msg, () -> createAesCipher(opmode).update( + new byte[2 * bs] /* input */, bs /* inputOffset */, 0 /* inputLen */)); + try { + createAesCipher(opmode).update(new byte[2*bs] /* input */, 0 /* inputOffset */, + 2 * bs /* inputLen */, new byte[2 * bs] /* output */, 0 /* outputOffset */); + fail(msg); + } catch (IllegalStateException expected) { + } + } + public void testCipher_Update_WithZeroLengthInput_ReturnsNull() throws Exception { - SecretKey key = new SecretKeySpec(AES_128_KEY, "AES"); Cipher c = Cipher.getInstance("AES/ECB/NoPadding"); - c.init(Cipher.ENCRYPT_MODE, key); + c.init(Cipher.ENCRYPT_MODE, AES_128_KEY); assertNull(c.update(new byte[0])); assertNull(c.update(new byte[c.getBlockSize() * 2], 0, 0)); @@ -3488,6 +4346,63 @@ public void testCipher_Update_WithZeroLengthInput_ReturnsNull() throws Exception assertNull(c.update(new byte[c.getBlockSize() * 2], 16, 0)); } + public void testCipher_Wrap_decryptMode_Failure() throws Exception { + checkCipher_Wrap_invalidMode_Failure(Cipher.DECRYPT_MODE); + } + + public void testCipher_Wrap_encryptMode_Failure() throws Exception { + checkCipher_Wrap_invalidMode_Failure(Cipher.ENCRYPT_MODE); + } + + public void testCipher_Wrap_unwrapMode_Failure() throws Exception { + checkCipher_Wrap_invalidMode_Failure(Cipher.UNWRAP_MODE); + } + + /** + * Helper for testing that Cipher.wrap() throws IllegalStateException when + * initialized in modes other than WRAP. + */ + private static void checkCipher_Wrap_invalidMode_Failure(int opmode) throws Exception { + KeyGenerator kg = KeyGenerator.getInstance("AES"); + kg.init(128); + SecretKey key = kg.generateKey(); + Cipher cipher = createAesCipher(opmode); + try { + cipher.wrap(key); + fail("wrap() should throw IllegalStateException [mode=" + opmode + "]"); + } catch (IllegalStateException expected) { + } + } + + public void testCipher_Unwrap_decryptMode_Failure() throws Exception { + checkCipher_Unwrap_invalidMode_Failure(Cipher.DECRYPT_MODE); + } + + public void testCipher_Unwrap_encryptMode_Failure() throws Exception { + checkCipher_Unwrap_invalidMode_Failure(Cipher.ENCRYPT_MODE); + } + + public void testCipher_Unwrap_wrapMode_Failure() throws Exception { + checkCipher_Unwrap_invalidMode_Failure(Cipher.WRAP_MODE); + } + + /** + * Helper for testing that Cipher.unwrap() throws IllegalStateException when + * initialized in modes other than UNWRAP. + */ + private static void checkCipher_Unwrap_invalidMode_Failure(int opmode) throws Exception { + KeyGenerator kg = KeyGenerator.getInstance("AES"); + kg.init(128); + SecretKey key = kg.generateKey(); + Cipher cipher = createAesCipher(opmode); + byte[] wrappedKey = createAesCipher(Cipher.WRAP_MODE).wrap(key); + try { + cipher.unwrap(wrappedKey, key.getAlgorithm(), Cipher.PRIVATE_KEY); + fail("unwrap() should throw IllegalStateException [mode=" + opmode + "]"); + } catch (IllegalStateException expected) { + } + } + private void checkCipher_ShortBlock_Failure(CipherTestParam p, String provider) throws Exception { // Do not try to test ciphers with no padding already. String noPaddingTransform = getCipherTransformationWithNoPadding(p.transformation); @@ -3495,7 +4410,6 @@ private void checkCipher_ShortBlock_Failure(CipherTestParam p, String provider) return; } - SecretKey key = new SecretKeySpec(p.key, "AES"); Cipher c = Cipher.getInstance( getCipherTransformationWithNoPadding(p.transformation), provider); if (c.getBlockSize() == 0) { @@ -3503,7 +4417,7 @@ private void checkCipher_ShortBlock_Failure(CipherTestParam p, String provider) } if (!p.transformation.endsWith("NOPADDING")) { - c.init(Cipher.ENCRYPT_MODE, key); + c.init(Cipher.ENCRYPT_MODE, p.encryptKey); try { c.doFinal(new byte[] { 0x01, 0x02, 0x03 }); fail("Should throw IllegalBlockSizeException on wrong-sized block; transform=" @@ -3543,9 +4457,8 @@ public void testAES_ECB_PKCS5Padding_ShortBuffer_Failure() throws Exception { } private void testAES_ECB_PKCS5Padding_ShortBuffer_Failure(String provider) throws Exception { - SecretKey key = new SecretKeySpec(AES_128_KEY, "AES"); Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding", provider); - c.init(Cipher.ENCRYPT_MODE, key); + c.init(Cipher.ENCRYPT_MODE, AES_128_KEY); final byte[] fragmentOutput = c.update(AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext); if (fragmentOutput != null) { @@ -3597,10 +4510,9 @@ public void testAES_ECB_NoPadding_IncrementalUpdate_Success() throws Exception { } private void testAES_ECB_NoPadding_IncrementalUpdate_Success(String provider) throws Exception { - SecretKey key = new SecretKeySpec(AES_128_KEY, "AES"); Cipher c = Cipher.getInstance("AES/ECB/NoPadding", provider); assertEquals(provider, c.getProvider().getName()); - c.init(Cipher.ENCRYPT_MODE, key); + c.init(Cipher.ENCRYPT_MODE, AES_128_KEY); for (int i = 0; i < AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded.length - 1; i++) { final byte[] outputFragment = c.update(AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded, i, 1); @@ -3631,12 +4543,11 @@ public void testAES_ECB_NoPadding_IvParameters_Failure() throws Exception { } private void testAES_ECB_NoPadding_IvParameters_Failure(String provider) throws Exception { - SecretKey key = new SecretKeySpec(AES_128_KEY, "AES"); Cipher c = Cipher.getInstance("AES/ECB/NoPadding", provider); AlgorithmParameterSpec spec = new IvParameterSpec(AES_IV_ZEROES); try { - c.init(Cipher.ENCRYPT_MODE, key, spec); + c.init(Cipher.ENCRYPT_MODE, AES_128_KEY, spec); fail("Should not accept an IV in ECB mode; provider=" + provider); } catch (InvalidAlgorithmParameterException expected) { } @@ -3708,6 +4619,39 @@ public void testRC4_MultipleKeySizes() throws Exception { } } + public void testAES_keyConstrained() throws Exception { + Provider[] providers = Security.getProviders(); + for (Provider p : providers) { + for (Provider.Service s : p.getServices()) { + if (s.getType().equals("Cipher")) { + if (s.getAlgorithm().startsWith("AES_128/")) { + Cipher c = Cipher.getInstance(s.getAlgorithm(), p); + assertTrue(s.getAlgorithm(), checkAES_keyConstraint(c, 128)); + assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 192)); + assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 256)); + } else if (s.getAlgorithm().startsWith("AES_256/")) { + Cipher c = Cipher.getInstance(s.getAlgorithm(), p); + assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 128)); + assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 192)); + assertTrue(s.getAlgorithm(), checkAES_keyConstraint(c, 256)); + } + } + } + } + } + + private boolean checkAES_keyConstraint(Cipher c, int keySize) throws Exception { + KeyGenerator kg = KeyGenerator.getInstance(getBaseAlgorithm(c.getAlgorithm())); + kg.init(keySize); + SecretKey key = kg.generateKey(); + try { + c.init(Cipher.ENCRYPT_MODE, key); + return true; + } catch (InvalidKeyException e) { + return false; + } + } + /** * Several exceptions can be thrown by init. Check that in this case we throw the right one, * as the error could fall under the umbrella of other exceptions. @@ -3904,17 +4848,27 @@ public void test_AESGCMNoPadding_Reuse_Success() throws Exception { assertEquals(Arrays.toString(c1.doFinal()), Arrays.toString(c2.doFinal())); - // .doFinal should also reset the state, so check that as well. + // .doFinal should also not allow reuse without re-initialization byte[] aad2 = new byte[] { 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, }; + try { + c1.updateAAD(aad2); + fail("Should not allow updateAAD without re-initialization"); + } catch (IllegalStateException expected) { + } - Cipher c3 = Cipher.getInstance("AES/GCM/NoPadding"); - c3.init(Cipher.ENCRYPT_MODE, key, spec); + try { + c1.update(new byte[8]); + fail("Should not allow update without re-initialization"); + } catch (IllegalStateException expected) { + } - c1.updateAAD(aad2); - c3.updateAAD(aad2); - assertEquals(Arrays.toString(c1.doFinal()), Arrays.toString(c3.doFinal())); + try { + c1.doFinal(); + fail("Should not allow doFinal without re-initialization"); + } catch (IllegalStateException expected) { + } } /** @@ -3985,6 +4939,29 @@ public void test_PBKDF2WITHHMACSHA1_SKFactory_and_PBEAESCBC_Cipher_withIV() thro assertEquals(Arrays.toString(plaintext), Arrays.toString(cipher.doFinal(ciphertext))); } + private static Cipher createAesCipher(int opmode) { + try { + final Cipher c = Cipher.getInstance("AES/ECB/NoPadding"); + c.init(opmode, AES_128_KEY); + return c; + } catch (Exception e) { + fail("Unexpected Exception: " + e.getMessage()); + return null; // unreachable + } + } + + /** + * Asserts that running the given runnable results in an IllegalStateException + */ + private static void assertIllegalStateException(String failureMessage, Runnable runnable) { + try { + runnable.run(); + fail(failureMessage); + } catch (IllegalStateException expected) { + // expected + } + } + /** * http://b/29038928 * If in a second call to init the current spi doesn't support the new specified key, look for diff --git a/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java b/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java index 9281b4399..809c290af 100644 --- a/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java +++ b/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java @@ -37,6 +37,7 @@ public MockProvider(String name) { public void testKeyAgreement_getInstance_SuppliedProviderNotRegistered_Success() throws Exception { Provider mockProvider = new MockProvider("MockProvider") { + @Override public void setup() { put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName()); } @@ -52,6 +53,7 @@ public void setup() { public void testKeyAgreement_getInstance_DoesNotSupportKeyClass_Success() throws Exception { Provider mockProvider = new MockProvider("MockProvider") { + @Override public void setup() { put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName()); put("KeyAgreement.FOO SupportedKeyClasses", "none"); @@ -76,6 +78,7 @@ public void setup() { public void testKeyAgreement_init_DoesNotSupportKeyClass_throwsInvalidKeyException() throws Exception { Provider mockProvider = new MockProvider("MockProvider") { + @Override public void setup() { put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName()); put("KeyAgreement.FOO SupportedKeyClasses", "none"); diff --git a/luni/src/test/java/libcore/javax/crypto/MacTest.java b/luni/src/test/java/libcore/javax/crypto/MacTest.java index 314a56482..61da2bbe2 100644 --- a/luni/src/test/java/libcore/javax/crypto/MacTest.java +++ b/luni/src/test/java/libcore/javax/crypto/MacTest.java @@ -16,13 +16,17 @@ package libcore.javax.crypto; -import junit.framework.TestCase; +import static java.nio.charset.StandardCharsets.UTF_8; import java.security.InvalidKeyException; import java.security.Provider; import java.security.Security; - +import java.util.Arrays; import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import junit.framework.TestCase; public class MacTest extends TestCase { private static abstract class MockProvider extends Provider { @@ -42,6 +46,7 @@ public MockProvider(String name) { public void testMac_init_DoesNotSupportKeyClass_throwsInvalidKeyException() throws Exception { Provider mockProvider = new MockProvider("MockProvider") { + @Override public void setup() { put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName()); put("Mac.FOO SupportedKeyClasses", "none"); @@ -59,4 +64,71 @@ public void setup() { Security.removeProvider(mockProvider.getName()); } } + + /** + * Aliases used to be wrong due to a typo. + * http://b/31114355 + */ + public void testMac_correctAlias() throws Exception { + Provider androidOpenSSLProvider = Security.getProvider("AndroidOpenSSL"); + assertEquals("HmacSHA224", androidOpenSSLProvider.get("Alg.Alias.Mac.1.2.840.113549.2.8")); + assertEquals("HmacSHA256", androidOpenSSLProvider.get("Alg.Alias.Mac.1.2.840.113549.2.9")); + } + + // Known answers from the SunJCE provider using the code below. Run with + // vogar --classpath sunjce_provider.jar + // + // secretKeyFactory = SecretKeyFactory.getInstance("PBEWithHmacSHA" + shaVariant + "AndAES_128", + // new com.sun.crypto.provider.SunJCE()); + // pbeKeySpec = new PBEKeySpec(password); + // + // secretKey = secretKeyFactory.generateSecret(pbeKeySpec); + // mac = Mac.getInstance("PBEWITHHMACSHA" + shaVariant, new com.sun.crypto.provider.SunJCE()); + // mac.init(secretKey, new PBEParameterSpec(salt, iterationCount)); + // byte[] sunResult = mac.doFinal(plaintext); + private final byte[][] SUN_JCA_KNOWN_ANSWERS_FOR_SHA_VARIANTS = { + { 44, -78, -97, -109, -125, 49, 68, 58, -9, -99, -27, -122, 58, 27, 7, 45, 87, -92, + -74, 64 }, + { 59, -13, 28, 53, 79, -79, -127, 117, 3, -23, -75, -127, -44, -47, -43, 28, 76, -114, + -110, 26, 59, 70, -91, 19, -52, 36, -64, -54 }, + { 88, 54, -105, -122, 14, 73, -40, -43, 52, -21, -33, -103, 32, 81, 115, 53, 111, 78, + 32, -108, 71, -74, -84, 125, 80, 13, -35, -36, 27, 56, 32, 104 }, + { -83, 60, -92, 44, -58, 86, -121, 104, 114, -67, 14, 80, 84, -48, -14, 38, 14, -62, + -96, 118, 53, -59, -33, -90, 85, -110, 105, -119, -81, 57, 43, -66, 99, 106, 35, + -16, -115, 29, -56, -52, -39, 102, -1, -90, 110, -52, 48, -32}, + { -22, -69, -77, 11, -14, -128, -121, 5, 48, 18, 107, -22, 64, -45, 18, 60, 24, -42, + -67, 111, 110, -99, -19, 14, -21, -43, 26, 68, -40, 82, 123, 39, 115, 34, 6, + -67, 27, -73, -63, -56, -39, 65, -75, -14, -5, -94, -8, 126, -44, -97, 95, 31, + 61, 123, -17, 14, 117, 71, -45, 53, -76, -91, 91, -121} + }; + + /** + * Test that BC has the same results as the SunJCA provider for + */ + public void test_PBEWITHHMACSHA_Variants() throws Exception { + byte[] plaintext = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 }; + byte[] salt = "saltsalt".getBytes(UTF_8); + char[] password = "password".toCharArray(); + int iterationCount = 100; + int[] shaVariants = { 1, 224, 256, 384, 512 }; + + for (int shaVariantIndex = 0; shaVariantIndex < shaVariants.length; shaVariantIndex++) { + int shaVariant = shaVariants[shaVariantIndex]; + SecretKeyFactory secretKeyFactory = + SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA" + shaVariant, "BC"); + PBEKeySpec pbeKeySpec = new PBEKeySpec(password, + salt, + iterationCount, + // Key depending on block size! + (shaVariant < 384) ? 64 : 128); + SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec); + Mac mac = Mac.getInstance("PBEWITHHMACSHA" + shaVariant, "BC"); + mac.init(secretKey); + byte[] bcResult = mac.doFinal(plaintext); + assertEquals( + Arrays.toString(SUN_JCA_KNOWN_ANSWERS_FOR_SHA_VARIANTS[shaVariantIndex]), + Arrays.toString(bcResult)); + } + } } diff --git a/luni/src/test/java/libcore/javax/crypto/MockMacSpi.java b/luni/src/test/java/libcore/javax/crypto/MockMacSpi.java index 0edeba7a3..d3ff9d5ea 100644 --- a/luni/src/test/java/libcore/javax/crypto/MockMacSpi.java +++ b/luni/src/test/java/libcore/javax/crypto/MockMacSpi.java @@ -26,7 +26,6 @@ import javax.crypto.BadPaddingException; import javax.crypto.MacSpi; -import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; diff --git a/luni/src/test/java/libcore/javax/crypto/SecretKeyFactoryTest.java b/luni/src/test/java/libcore/javax/crypto/SecretKeyFactoryTest.java index ef5adc96d..0878bac94 100644 --- a/luni/src/test/java/libcore/javax/crypto/SecretKeyFactoryTest.java +++ b/luni/src/test/java/libcore/javax/crypto/SecretKeyFactoryTest.java @@ -16,6 +16,8 @@ package libcore.javax.crypto; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.util.Arrays; @@ -171,7 +173,7 @@ public void test_PBKDF2_rfc3211_192() throws Exception { public void test_PBKDF2_b8312059() throws Exception { char[] password = "\u0141\u0142".toCharArray(); - byte[] salt = "salt".getBytes(); + byte[] salt = "salt".getBytes(UTF_8); int iterations = 4096; int keyLength = 160; byte[] expected_utf8 = new byte[] { diff --git a/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParameterGeneratorTestDH.java b/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParameterGeneratorTestDH.java index 77d81dbe2..aed6ce87b 100644 --- a/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParameterGeneratorTestDH.java +++ b/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParameterGeneratorTestDH.java @@ -25,6 +25,7 @@ public AlgorithmParameterGeneratorTestDH() { super("DH", new AlgorithmParameterKeyAgreementHelper("DH")); } + @Override public void testAlgorithmParameterGenerator() throws Exception { super.testAlgorithmParameterGenerator(); } diff --git a/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParametersTestDH.java b/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParametersTestDH.java index f8a5b5d13..6e62c03d1 100644 --- a/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParametersTestDH.java +++ b/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParametersTestDH.java @@ -61,6 +61,7 @@ public AlgorithmParametersTestDH() { } // Broken Test: Suffers from DH slowness, disabling for now + @Override public void testAlgorithmParameters() throws Exception { super.testAlgorithmParameters(); } diff --git a/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParametersTestPBES2.java b/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParametersTestPBES2.java new file mode 100644 index 000000000..b94b72f78 --- /dev/null +++ b/luni/src/test/java/libcore/javax/crypto/spec/AlgorithmParametersTestPBES2.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2016 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 + */ +package libcore.javax.crypto.spec; + +import java.security.AlgorithmParameters; +import java.security.Key; +import java.util.Arrays; + +import junit.framework.TestCase; + +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +public class AlgorithmParametersTestPBES2 extends TestCase { + + private static final int[] KEY_SIZES = { 128, 256 }; + int[] SHA_VARIANTS = { 1, 224, 256, 384, 512 }; + + private static final PBEParameterSpec TEST_PBE_PARAMETER_SPEC = new PBEParameterSpec( + new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, // salt + 34, // iterationCount + new IvParameterSpec(new byte[] { + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55 // IV + })); + + // For SHA variants other than SHA1, known answers generated with: + // + // AlgorithmParameters ap = AlgorithmParameters.getInstance( + // "PBEWithHmacSHA" + shaVariant + "AndAES_" + keySize, + // new com.sun.crypto.provider.SunJCE()); + // AlgorithmParameterSpec spec = TEST_PBE_PARAMETER_SPEC; + // ap.init(spec); + // System.out.println("Encoded: " + Arrays.toString(ap.getEncoded())); + // + // For SHA1, the RI does encode the prf (SHA1) although it is the default one, and thus it + // shouldn't be explicitly encoded, according to DER. Checked with an ASN1 decoder that the + // only difference between our encoding and the RI's one is that SHA1 is not explicitly + // encoded. + private static final byte[][] GET_ENCODED_KNOWN_ANSWERS = new byte[][] { + // PBEWithHmacSHA1AndAES_128 + { 48, 75, 6, 9, 42, -122, 72, -122, -9, 13, 1, 5, 13, 48, 62, 48, 29, 6, 9, 42, -122, 72, + -122, -9, 13, 1, 5, 12, 48, 16, 4, 8, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 34, 2, 1, 16, + 48, 29, 6, 9, 96, -122, 72, 1, 101, 3, 4, 1, 2, 4, 16, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55 }, + // PBEWithHmacSHA224AndAES_128 + { 48, 89, 6, 9, 42, -122, 72, -122, -9, 13, 1, 5, 13, 48, 76, 48, 43, 6, 9, 42, -122, 72, + -122, -9, 13, 1, 5, 12, 48, 30, 4, 8, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 34, 2, 1, 16, + 48, 12, 6, 8, 42, -122, 72, -122, -9, 13, 2, 8, 5, 0, 48, 29, 6, 9, 96, -122, 72, 1, + 101, 3, 4, 1, 2, 4, 16, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55 }, + // PBEWithHmacSHA256AndAES_128 + { 48, 89, 6, 9, 42, -122, 72, -122, -9, 13, 1, 5, 13, 48, 76, 48, 43, 6, 9, 42, -122, 72, + -122, -9, 13, 1, 5, 12, 48, 30, 4, 8, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 34, 2, 1, 16, + 48, 12, 6, 8, 42, -122, 72, -122, -9, 13, 2, 9, 5, 0, 48, 29, 6, 9, 96, -122, 72, 1, + 101, 3, 4, 1, 2, 4, 16, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55 }, + // PBEWithHmacSHA384AndAES_128 + { 48, 89, 6, 9, 42, -122, 72, -122, -9, 13, 1, 5, 13, 48, 76, 48, 43, 6, 9, 42, -122, 72, + -122, -9, 13, 1, 5, 12, 48, 30, 4, 8, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 34, 2, 1, 16, + 48, 12, 6, 8, 42, -122, 72, -122, -9, 13, 2, 10, 5, 0, 48, 29, 6, 9, 96, -122, 72, + 1, 101, 3, 4, 1, 2, 4, 16, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55 }, + // PBEWithHmacSHA512AndAES_128 + { 48, 89, 6, 9, 42, -122, 72, -122, -9, 13, 1, 5, 13, 48, 76, 48, 43, 6, 9, 42, -122, 72, + -122, -9, 13, 1, 5, 12, 48, 30, 4, 8, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 34, 2, 1, 16, + 48, 12, 6, 8, 42, -122, 72, -122, -9, 13, 2, 11, 5, 0, 48, 29, 6, 9, 96, -122, 72, + 1, 101, 3, 4, 1, 2, 4, 16, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55 }, + // PBEWithHmacSHA1AndAES_256 + { 48, 75, 6, 9, 42, -122, 72, -122, -9, 13, 1, 5, 13, 48, 62, 48, 29, 6, 9, 42, -122, 72, + -122, -9, 13, 1, 5, 12, 48, 16, 4, 8, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 34, 2, 1, 32, + 48, 29, 6, 9, 96, -122, 72, 1, 101, 3, 4, 1, 42, 4, 16, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55}, + // PBEWithHmacSHA224AndAES_256 + { 48, 89, 6, 9, 42, -122, 72, -122, -9, 13, 1, 5, 13, 48, 76, 48, 43, 6, 9, 42, -122, 72, + -122, -9, 13, 1, 5, 12, 48, 30, 4, 8, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 34, 2, 1, 32, + 48, 12, 6, 8, 42, -122, 72, -122, -9, 13, 2, 8, 5, 0, 48, 29, 6, 9, 96, -122, 72, 1, + 101, 3, 4, 1, 42, 4, 16, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55 }, + // PBEWithHmacSHA256AndAES_256 + { 48, 89, 6, 9, 42, -122, 72, -122, -9, 13, 1, 5, 13, 48, 76, 48, 43, 6, 9, 42, -122, 72, + -122, -9, 13, 1, 5, 12, 48, 30, 4, 8, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 34, 2, 1, 32, + 48, 12, 6, 8, 42, -122, 72, -122, -9, 13, 2, 9, 5, 0, 48, 29, 6, 9, 96, -122, 72, 1, + 101, 3, 4, 1, 42, 4, 16, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55 }, + // PBEWithHmacSHA384AndAES_256 + { 48, 89, 6, 9, 42, -122, 72, -122, -9, 13, 1, 5, 13, 48, 76, 48, 43, 6, 9, 42, -122, 72, + -122, -9, 13, 1, 5, 12, 48, 30, 4, 8, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 34, 2, 1, 32, + 48, 12, 6, 8, 42, -122, 72, -122, -9, 13, 2, 10, 5, 0, 48, 29, 6, 9, 96, -122, 72, + 1, 101, 3, 4, 1, 42, 4, 16, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55 }, + // PBEWithHmacSHA512AndAES_256 + { 48, 89, 6, 9, 42, -122, 72, -122, -9, 13, 1, 5, 13, 48, 76, 48, 43, 6, 9, 42, -122, 72, + -122, -9, 13, 1, 5, 12, 48, 30, 4, 8, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 34, 2, 1, 32, + 48, 12, 6, 8, 42, -122, 72, -122, -9, 13, 2, 11, 5, 0, 48, 29, 6, 9, 96, -122, 72, + 1, 101, 3, 4, 1, 42, 4, 16, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55 } }; + + // Known answers obtained with: + // String algorithmName = "PBEWithHmacSHA" + shaVariant + "AndAES_" + keySize; + // SecretKeyFactory skf = SecretKeyFactory.getInstance( + // algorithmName, sunProvider); + // Key key = skf.generateSecret(pbeKS); + // Cipher c = Cipher.getInstance(algorithmName, sunProvider); + // c.init(Cipher.ENCRYPT_MODE, key, TEST_PBE_PARAMETER_SPEC); + // byte[] encrypted = c.doFinal(plaintext); + private static final byte[][] ENCRYPT_KNOWN_ANSWERS = new byte[][]{ + // PBEWithHmacSHA1AndAES_128 + { -42, -84, 94, -75, -84, 12, -83, -100, -76, -58, -78, 82, -78, 11, -70, 67, 92, 111, -90, + -75, 43, 31, 16, 47, -81, -127, -65, -127, -41, 121, 77, -90, -15, 3, 5, -12, 66, + 37, -80, 107, -99, 106, -59, -79, -32, -7, -27, 110 }, + { 121, 36, -19, 19, -89, 95, -15, 50, -62, 36, -23, -78, -7, 43, 79, -31, 17, 88, -35, -89, + -11, 108, -8, -64, 77, 57, 64, 36, 70, -102, -65, 77, 74, 20, 9, -121, -9, 69, -115, + 21, 32, -22, -107, -75, -76, 111, 79, 99 }, + // PBEWithHmacSHA224AndAES_128 + { 1, 60, 84, 10, 3, 110, 3, -112, 27, 126, 59, -63, -34, 117, 83, -67, -115, 117, -23, 57, + -70, -126, 57, -84, -3, -102, -87, 98, 77, 10, -19, -41, 20, -95, 53, -112, -48, 22, + 22, -99, -71, -88, -111, -87, 3, -126, -83, 64 }, + // PBEWithHmacSHA256AndAES_128 + { -102, -122, 69, -75, -11, -61, -13, 82, 122, -97, 112, -27, 61, 22, -28, -34, -66, -47, + 123, 104, 20, -115, 83, -33, -38, 65, -101, -128, -20, -34, 95, 53, -69, 11, -79, + -78, 37, 2, -81, 126, 97, -10, -69, -56, 89, -22, -25, -72 }, + // PBEWithHmacSHA384AndAES_128 + { -67, -57, -99, -6, 102, 87, -111, 18, 63, 7, -99, 32, -110, -24, 44, -94, -24, 101, 39, + 115, 24, 20, -31, 126, 99, -113, -40, 27, 79, -48, 98, 84, 67, -51, 115, -21, -118, + 9, 11, -117, -25, -73, -106, 36, -28, -18, 96, 16 }, + // PBEWithHmacSHA512AndAES_128 + { -72, 97, -41, -80, 0, 39, -121, 107, -89, -72, -103, -52, 100, 126, -89, -59, -4, 73, + -116, 0, 69, 95, 23, -25, 67, -81, -23, 1, 18, 57, -73, 89, 79, 124, -128, -113, 12, + 78, 14, 12, 64, 112, -105, -6, 13, -112, 26, -92 }, + // PBEWithHmacSHA1AndAES_256 + { 98, 125, 91, -63, 96, 15, -103, -127, 70, -73, -25, 40, -126, 116, 15, -102, -108, 117, + -111, -65, -24, -114, 90, -126, -115, 15, -86, -72, -109, 47, 39, -102, -52, 123, + -4, -50, -99, 33, 92, 32, -110, -6, -2, -114, -116, 2, -16, -106 }, + // PBEWithHmacSHA256AndAES_256 + { 126, -32, 53, 14, -13, 26, 127, -23, -38, -56, 66, 1, 45, -128, -16, -99, -34, -31, -49, + 126, 120, -47, -39, -108, -12, 16, 16, -127, 64, -64, 75, 53, 41, 51, 53, -37, -95, + 3, -87, -100, 103, -55, 30, 5, 29, 8, 93, 123 }, + // PBEWithHmacSHA384AndAES_256 + { 68, 99, -46, -114, 37, -29, 59, -80, 16, 113, 116, 97, -9, -36, -32, 8, 59, -124, -73, + -66, -105, -57, 41, -78, 86, -128, 90, 51, -29, 108, -24, -62, 87, 94, -87, -5, 126, + 95, -101, -39, -126, -76, 77, -2, 44, -70, -70, -88 }, + // PBEWithHmacSHA512AndAES_256 + { -93, -114, 1, -58, 117, -41, -114, 58, 56, 108, -16, -57, -36, -76, 92, -65, 100, 119, -9, + 8, -93, 113, -3, -85, -31, -26, 20, 115, -45, -56, 30, 106, -16, -66, 4, -53, 2, + -113, 8, -116, -38, 0, 126, -87, 61, -32, 57, -35} + }; + + public void testGetEncoded_knownAnswers() throws Exception { + int i = 0; + for (int keySize : KEY_SIZES) { + for (int shaVariant : SHA_VARIANTS) { + AlgorithmParameters ap = AlgorithmParameters.getInstance( + "PBEWithHmacSHA"+ shaVariant + "AndAES_" + keySize, "BC"); + ap.init(TEST_PBE_PARAMETER_SPEC); + assertEquals( + Arrays.toString(GET_ENCODED_KNOWN_ANSWERS[i]), + Arrays.toString(ap.getEncoded())); + i++; + } + } + } + + public void test_encodeAndDecode() throws Exception { + AlgorithmParameters ap = AlgorithmParameters.getInstance( + "PBEWithHmacSHA224AndAES_128", "BC"); + ap.init(TEST_PBE_PARAMETER_SPEC); + AlgorithmParameters ap2 = AlgorithmParameters.getInstance( + "PBEWithHmacSHA224AndAES_128", "BC"); + ap2.init(ap.getEncoded()); + PBEParameterSpec encodedSpec = ap2.getParameterSpec(PBEParameterSpec.class); + assertEquals(Arrays.toString(TEST_PBE_PARAMETER_SPEC.getSalt()), + Arrays.toString(encodedSpec.getSalt())); + assertEquals(TEST_PBE_PARAMETER_SPEC.getIterationCount(), encodedSpec.getIterationCount()); + assertTrue(encodedSpec.getParameterSpec() instanceof IvParameterSpec); + assertEquals( + Arrays.toString( + ((IvParameterSpec) TEST_PBE_PARAMETER_SPEC.getParameterSpec()).getIV()), + Arrays.toString(((IvParameterSpec) encodedSpec.getParameterSpec()).getIV())); + } + + public void test_encryptWithAlgorithmParameters() throws Exception { + byte[] plaintext = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 , 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 }; + PBEKeySpec pbeKS = new PBEKeySpec("aaaaa".toCharArray()); + int i = 0; + for (int keySize : KEY_SIZES) { + for (int shaVariant : SHA_VARIANTS) { + String algorithmName = "PBEWithHmacSHA" + shaVariant + "AndAES_" + keySize; + AlgorithmParameters ap = AlgorithmParameters.getInstance(algorithmName, "BC"); + ap.init(TEST_PBE_PARAMETER_SPEC); + SecretKeyFactory skf = SecretKeyFactory.getInstance( + algorithmName, "BC"); + Key key = skf.generateSecret(pbeKS); + Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC"); + c.init(Cipher.ENCRYPT_MODE, key, ap); + byte[] encrypted = c.doFinal(plaintext); + assertEquals( + Arrays.toString(ENCRYPT_KNOWN_ANSWERS[i]), + Arrays.toString(encrypted)); + c.init(Cipher.DECRYPT_MODE, key, ap); + byte[] decrypted = c.doFinal(encrypted); + assertEquals( + Arrays.toString(plaintext), + Arrays.toString(decrypted)); + i++; + } + } + } + + public void test_correctNames() throws Exception { + for (int keySize : KEY_SIZES) { + for (int shaVariant : SHA_VARIANTS) { + String algorithmName = "PBEWithHmacSHA" + shaVariant + "AndAES_" + keySize; + AlgorithmParameters ap = AlgorithmParameters.getInstance(algorithmName, "BC"); + ap.init(TEST_PBE_PARAMETER_SPEC); + assertTrue(ap.toString().matches("(?i:.*hmacsha" + shaVariant + ".*)")); + assertTrue(ap.toString().matches("(?i:.*aes" + +keySize + ".*)")); + } + } + } +} diff --git a/luni/src/test/java/libcore/javax/crypto/spec/KeyPairGeneratorTestDH.java b/luni/src/test/java/libcore/javax/crypto/spec/KeyPairGeneratorTestDH.java index 1ef4c8cb7..5a74d685a 100644 --- a/luni/src/test/java/libcore/javax/crypto/spec/KeyPairGeneratorTestDH.java +++ b/luni/src/test/java/libcore/javax/crypto/spec/KeyPairGeneratorTestDH.java @@ -26,6 +26,7 @@ public KeyPairGeneratorTestDH() { } // Broken Test: Takes ages due to DH computations. Disabling for now. + @Override public void testKeyPairGenerator() throws Exception { super.testKeyPairGenerator(); } diff --git a/luni/src/test/java/libcore/javax/net/ServerSocketFactoryTest.java b/luni/src/test/java/libcore/javax/net/ServerSocketFactoryTest.java index 77996dd30..bf544b166 100644 --- a/luni/src/test/java/libcore/javax/net/ServerSocketFactoryTest.java +++ b/luni/src/test/java/libcore/javax/net/ServerSocketFactoryTest.java @@ -42,6 +42,10 @@ public void testCreateServerSocketWithPort() throws IOException { testSocket(serverSocket, 50); } + // This test may fail on kernel versions between 4.4 and 4.9, due to a kernel implementation + // detail change. Backporting the following kernel change will fix the behavior. + // http://b/31960002 + // https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=5ea8ea2cb7f1d0db15762c9b0bb9e7330425a071 public void testCreateServerSocketWithPortNoBacklog() throws IOException { ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(0, 1); testSocket(serverSocket, 1); @@ -102,14 +106,14 @@ private void assertBacklog(int specifiedBacklog, InetSocketAddress serverAddress } fail("Failed to exhaust backlog after " + max + " connections!"); } catch (IOException expected) { + } finally { + for (Socket socket : backlog) { + socket.close(); + } } System.out.println("backlog peaked at " + peak); - for (Socket socket : backlog) { - socket.close(); - } - /* * In 4.5 of UNIX Network Programming, Stevens says: * "Berkeley-derived implementations add a fudge factor to the diff --git a/luni/src/test/java/libcore/javax/net/ssl/KeyManagerFactoryTest.java b/luni/src/test/java/libcore/javax/net/ssl/KeyManagerFactoryTest.java index b99a3f110..cd693c965 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/KeyManagerFactoryTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/KeyManagerFactoryTest.java @@ -26,7 +26,6 @@ import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Arrays; -import java.util.Locale; import java.util.Set; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; @@ -42,6 +41,7 @@ public class KeyManagerFactoryTest extends TestCase { private TestKeyStore testKeyStore; + @Override protected void setUp() throws Exception { // note the rare usage of DSA keys here in addition to RSA testKeyStore = new TestKeyStore.Builder() diff --git a/luni/src/test/java/libcore/javax/net/ssl/KeyStoreBuilderParametersTest.java b/luni/src/test/java/libcore/javax/net/ssl/KeyStoreBuilderParametersTest.java index 5b8f6e6c9..d73e4a3ce 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/KeyStoreBuilderParametersTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/KeyStoreBuilderParametersTest.java @@ -47,6 +47,7 @@ public void test_init_Builder() { public void test_init_List_null() { try { new KeyStoreBuilderParameters((List) null); + fail(); } catch (NullPointerException expected) { } } diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java index 533849c3f..9c8fe9103 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java @@ -16,6 +16,7 @@ package libcore.javax.net.ssl; +import java.io.Closeable; import java.security.InvalidAlgorithmParameterException; import java.security.KeyManagementException; import java.security.KeyStore; @@ -28,7 +29,6 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; -import libcore.io.IoUtils; import libcore.java.security.StandardNames; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; @@ -57,6 +57,7 @@ public void test_SSLContext_getDefault() throws Exception { assertNotNull(sslContext); try { sslContext.init(null, null, null); + fail(); } catch (KeyManagementException expected) { } } @@ -64,6 +65,7 @@ public void test_SSLContext_getDefault() throws Exception { public void test_SSLContext_setDefault() throws Exception { try { SSLContext.setDefault(null); + fail(); } catch (NullPointerException expected) { } @@ -188,7 +190,7 @@ private static void assertEnabledCipherSuites( assertContentsInOrder( expectedCipherSuites, sslSocket.getSSLParameters().getCipherSuites()); } finally { - IoUtils.closeQuietly(sslSocket); + closeQuietly(sslSocket); } SSLServerSocket sslServerSocket = @@ -197,7 +199,7 @@ private static void assertEnabledCipherSuites( assertContentsInOrder( expectedCipherSuites, sslServerSocket.getEnabledCipherSuites()); } finally { - IoUtils.closeQuietly(sslSocket); + closeQuietly(sslSocket); } } @@ -580,6 +582,14 @@ public void test_SSLContextTest_TestSSLContext_create() { testContext.close(); } + public void test_SSLContext_SSLv3Unsupported() throws Exception { + try { + SSLContext context = SSLContext.getInstance("SSLv3"); + fail("SSLv3 should not be supported"); + } catch (NoSuchAlgorithmException expected) { + } + } + private static void assertContentsInOrder(List expected, String... actual) { if (expected.size() != actual.length) { fail("Unexpected length. Expected len <" + expected.size() @@ -591,4 +601,13 @@ private static void assertContentsInOrder(List expected, String... actua + ">, actual <" + Arrays.asList(actual) + ">" ); } } + + private static final void closeQuietly(Closeable socket) { + if (socket != null) { + try { + socket.close(); + } catch (Exception ignored) { + } + } + } } diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java index 8990f62c9..d9a7b4fad 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java @@ -16,6 +16,8 @@ package libcore.javax.net.ssl; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; @@ -124,7 +126,7 @@ private void test_SSLEngine_getSupportedCipherSuites_connect(TestKeyStore testKe new PSKKeyManagerProxy() { @Override protected SecretKey getKey(String identityHint, String identity, SSLEngine engine) { - return new SecretKeySpec("Just an arbitrary key".getBytes(), "RAW"); + return new SecretKeySpec("Just an arbitrary key".getBytes(UTF_8), "RAW"); } }); TestSSLContext c = TestSSLContext.createWithAdditionalKeyManagers( @@ -220,13 +222,12 @@ void beforeBeginHandshake(SSLEngine client, SSLEngine server) { assertConnected(pair); boolean needsRecordSplit = - ("TLS".equalsIgnoreCase(c.clientContext.getProtocol()) - || "SSLv3".equalsIgnoreCase(c.clientContext.getProtocol())) + "TLS".equalsIgnoreCase(c.clientContext.getProtocol()) && cipherSuite.contains("_CBC_"); - assertSendsCorrectly("This is the client. Hello!".getBytes(), + assertSendsCorrectly("This is the client. Hello!".getBytes(UTF_8), pair.client, pair.server, needsRecordSplit); - assertSendsCorrectly("This is the server. Hi!".getBytes(), + assertSendsCorrectly("This is the server. Hi!".getBytes(UTF_8), pair.server, pair.client, needsRecordSplit); } finally { if (pair != null) { @@ -819,11 +820,12 @@ public void test_SSLEngine_Multiple_Thread_Success() throws Exception { final CountDownLatch startUpSync = new CountDownLatch(2); ExecutorService executor = Executors.newFixedThreadPool(2); Future client = executor.submit(new Callable() { + @Override public Void call() throws Exception { startUpSync.countDown(); for (int i = 0; i < NUM_STRESS_ITERATIONS; i++) { - assertSendsCorrectly("This is the client. Hello!".getBytes(), + assertSendsCorrectly("This is the client. Hello!".getBytes(UTF_8), pair.client, pair.server, false); } @@ -831,11 +833,12 @@ public Void call() throws Exception { } }); Future server = executor.submit(new Callable() { + @Override public Void call() throws Exception { startUpSync.countDown(); for (int i = 0; i < NUM_STRESS_ITERATIONS; i++) { - assertSendsCorrectly("This is the server. Hi!".getBytes(), + assertSendsCorrectly("This is the server. Hi!".getBytes(UTF_8), pair.server, pair.client, false); } diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java index d2c0f4880..04f925255 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java @@ -63,4 +63,29 @@ public void testSetEnabledProtocolsStoresCopy() throws Exception { array[0] = "Modified after having been set"; assertEquals(originalFirstElement, socket.getEnabledProtocols()[0]); } + + // We modified the toString() of SSLServerSocket, and it's based on the output + // of ServerSocket.toString(), so we want to make sure that a change in + // ServerSocket.toString() doesn't cause us to output nonsense. + public void testToString() throws Exception { + // The actual implementation from a security provider might do something + // special for its toString(), so we create our own implementation + SSLServerSocket socket = new SSLServerSocket() { + @Override public String[] getEnabledCipherSuites() { return new String[0]; } + @Override public void setEnabledCipherSuites(String[] strings) { } + @Override public String[] getSupportedCipherSuites() { return new String[0]; } + @Override public String[] getSupportedProtocols() { return new String[0]; } + @Override public String[] getEnabledProtocols() { return new String[0]; } + @Override public void setEnabledProtocols(String[] strings) { } + @Override public void setNeedClientAuth(boolean b) { } + @Override public boolean getNeedClientAuth() { return false; } + @Override public void setWantClientAuth(boolean b) { } + @Override public boolean getWantClientAuth() { return false; } + @Override public void setUseClientMode(boolean b) { } + @Override public boolean getUseClientMode() { return false; } + @Override public void setEnableSessionCreation(boolean b) { } + @Override public boolean getEnableSessionCreation() { return false; } + }; + assertTrue(socket.toString().startsWith("SSLServerSocket[")); + } } diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java index bc2b626c4..5691dfb3a 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java @@ -242,6 +242,7 @@ public void test_SSLSession_getValue() { TestSSLSessions s = TestSSLSessions.create(); try { s.invalid.getValue(null); + fail(); } catch (IllegalArgumentException expected) { } assertNull(s.invalid.getValue("BOGUS")); diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java index 3fe3ac38b..1ddeacb37 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java @@ -187,6 +187,9 @@ public void test_SSLSocketFactory_getDefault_cacheInvalidate() throws Exception } } + /** + * Should only run on Android. + */ private String resetSslProvider() { String origProvider = Security.getProperty(SSL_PROPERTY); @@ -195,7 +198,8 @@ private String resetSslProvider() { field_secprops.setAccessible(true); Properties secprops = (Properties) field_secprops.get(null); secprops.remove(SSL_PROPERTY); - Security.increaseVersion(); + Method m_increaseVersion = Security.class.getDeclaredMethod("increaseVersion"); + m_increaseVersion.invoke(null); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Could not clear security provider", e); diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java index d17e32bad..bec7b2dcf 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java @@ -16,7 +16,10 @@ package libcore.javax.net.ssl; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.ByteArrayInputStream; +import java.io.Closeable; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; @@ -24,19 +27,38 @@ import java.io.OutputStream; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Method; +import java.math.BigInteger; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.Signature; +import java.security.SignatureException; +import java.security.SignatureSpi; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.security.interfaces.ECKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.RSAKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -47,7 +69,13 @@ import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; import javax.crypto.spec.SecretKeySpec; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; @@ -73,13 +101,13 @@ import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import junit.framework.TestCase; -import libcore.io.IoUtils; -import libcore.io.Streams; import libcore.java.security.StandardNames; import libcore.java.security.TestKeyStore; import libcore.tlswire.handshake.CipherSuite; import libcore.tlswire.handshake.ClientHello; import libcore.tlswire.handshake.CompressionMethod; +import libcore.tlswire.handshake.EllipticCurve; +import libcore.tlswire.handshake.EllipticCurvesHelloExtension; import libcore.tlswire.handshake.HandshakeMessage; import libcore.tlswire.handshake.HelloExtension; import libcore.tlswire.handshake.ServerNameHelloExtension; @@ -123,14 +151,14 @@ private void test_SSLSocket_getSupportedCipherSuites_connect(TestKeyStore testKe String clientToServerString = "this is sent from the client to the server..."; String serverToClientString = "... and this from the server to the client"; - byte[] clientToServer = clientToServerString.getBytes(); - byte[] serverToClient = serverToClientString.getBytes(); + byte[] clientToServer = clientToServerString.getBytes(UTF_8); + byte[] serverToClient = serverToClientString.getBytes(UTF_8); KeyManager pskKeyManager = PSKKeyManagerProxy.getConscryptPSKKeyManager( new PSKKeyManagerProxy() { @Override protected SecretKey getKey(String identityHint, String identity, Socket socket) { - return new SecretKeySpec("Just an arbitrary key".getBytes(), "RAW"); + return new SecretKeySpec("Just an arbitrary key".getBytes(UTF_8), "RAW"); } }); TestSSLContext c = TestSSLContext.createWithAdditionalKeyManagers( @@ -181,13 +209,13 @@ protected SecretKey getKey(String identityHint, String identity, Socket socket) // Check that the client can read the message sent by the server server.getOutputStream().write(serverToClient); byte[] clientFromServer = new byte[serverToClient.length]; - Streams.readFully(client.getInputStream(), clientFromServer); + readFully(client.getInputStream(), clientFromServer); assertEquals(serverToClientString, new String(clientFromServer)); // Check that the server can read the message sent by the client client.getOutputStream().write(clientToServer); byte[] serverFromClient = new byte[clientToServer.length]; - Streams.readFully(server.getInputStream(), serverFromClient); + readFully(server.getInputStream(), serverFromClient); assertEquals(clientToServerString, new String(serverFromClient)); // Check that the server and the client cannot read anything else @@ -531,6 +559,7 @@ public void test_SSLSocket_HandshakeCompletedListener() throws Exception { executor.shutdown(); final boolean[] handshakeCompletedListenerCalled = new boolean[1]; client.addHandshakeCompletedListener(new HandshakeCompletedListener() { + @Override public void handshakeCompleted(HandshakeCompletedEvent event) { try { SSLSession session = event.getSession(); @@ -653,6 +682,7 @@ public void test_SSLSocket_HandshakeCompletedListener_RuntimeException() throws }); executor.shutdown(); client.addHandshakeCompletedListener(new HandshakeCompletedListener() { + @Override public void handshakeCompleted(HandshakeCompletedEvent event) { throw expectedException; } @@ -893,6 +923,347 @@ public void test_SSLSocket_clientAuth_bogusAlias() throws Exception { c.close(); } + public void test_SSLSocket_clientAuth_OpaqueKey_RSA() throws Exception { + run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientCertificate()); + } + + public void test_SSLSocket_clientAuth_OpaqueKey_EC_RSA() throws Exception { + run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientEcRsaCertificate()); + } + + public void test_SSLSocket_clientAuth_OpaqueKey_EC_EC() throws Exception { + run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientEcEcCertificate()); + } + + private void run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore keyStore) throws Exception { + try { + Security.insertProviderAt(new OpaqueProvider(), 1); + + final TestSSLContext c = TestSSLContext.create(keyStore, TestKeyStore.getServer()); + SSLContext clientContext = SSLContext.getInstance("TLS"); + final X509KeyManager delegateKeyManager = (X509KeyManager) c.clientKeyManagers[0]; + X509KeyManager keyManager = new X509KeyManager() { + @Override + public String chooseClientAlias(String[] keyType, Principal[] issuers, + Socket socket) { + return delegateKeyManager.chooseClientAlias(keyType, issuers, socket); + } + + @Override + public String chooseServerAlias(String keyType, Principal[] issuers, + Socket socket) { + return delegateKeyManager.chooseServerAlias(keyType, issuers, socket); + } + + @Override + public X509Certificate[] getCertificateChain(String alias) { + return delegateKeyManager.getCertificateChain(alias); + } + + @Override + public String[] getClientAliases(String keyType, Principal[] issuers) { + return delegateKeyManager.getClientAliases(keyType, issuers); + } + + @Override + public String[] getServerAliases(String keyType, Principal[] issuers) { + return delegateKeyManager.getServerAliases(keyType, issuers); + } + + @Override + public PrivateKey getPrivateKey(String alias) { + PrivateKey privKey = delegateKeyManager.getPrivateKey(alias); + if (privKey instanceof RSAPrivateKey) { + return new OpaqueDelegatingRSAPrivateKey((RSAPrivateKey) privKey); + } else if (privKey instanceof ECPrivateKey) { + return new OpaqueDelegatingECPrivateKey((ECPrivateKey) privKey); + } else { + return null; + } + } + }; + clientContext.init(new KeyManager[] { + keyManager + }, new TrustManager[] { + c.clientTrustManager + }, null); + SSLSocket client = (SSLSocket) clientContext.getSocketFactory().createSocket(c.host, + c.port); + final SSLSocket server = (SSLSocket) c.serverSocket.accept(); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future future = executor.submit(new Callable() { + @Override + public Void call() throws Exception { + server.setNeedClientAuth(true); + server.startHandshake(); + return null; + } + }); + executor.shutdown(); + client.startHandshake(); + assertNotNull(client.getSession().getLocalCertificates()); + TestKeyStore.assertChainLength(client.getSession().getLocalCertificates()); + TestSSLContext.assertClientCertificateChain(c.clientTrustManager, + client.getSession().getLocalCertificates()); + future.get(); + client.close(); + server.close(); + c.close(); + } finally { + Security.removeProvider(OpaqueProvider.NAME); + } + } + + @SuppressWarnings("serial") + public static class OpaqueProvider extends Provider { + public static final String NAME = "OpaqueProvider"; + + public OpaqueProvider() { + super(NAME, 1.0, "test provider"); + + put("Signature.NONEwithRSA", OpaqueSignatureSpi.RSA.class.getName()); + put("Signature.NONEwithECDSA", OpaqueSignatureSpi.ECDSA.class.getName()); + put("Cipher.RSA/ECB/NoPadding", OpaqueCipherSpi.class.getName()); + } + } + + protected static class OpaqueSignatureSpi extends SignatureSpi { + private final String algorithm; + + private Signature delegate; + + protected OpaqueSignatureSpi(String algorithm) { + this.algorithm = algorithm; + } + + public final static class RSA extends OpaqueSignatureSpi { + public RSA() { + super("NONEwithRSA"); + } + } + + public final static class ECDSA extends OpaqueSignatureSpi { + public ECDSA() { + super("NONEwithECDSA"); + } + } + + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + fail("Cannot verify"); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + DelegatingPrivateKey opaqueKey = (DelegatingPrivateKey) privateKey; + try { + delegate = Signature.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new InvalidKeyException(e); + } + delegate.initSign(opaqueKey.getDelegate()); + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + delegate.update(b); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { + delegate.update(b, off, len); + } + + @Override + protected byte[] engineSign() throws SignatureException { + return delegate.sign(); + } + + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + return delegate.verify(sigBytes); + } + + @SuppressWarnings("deprecation") + @Override + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + delegate.setParameter(param, value); + } + + @SuppressWarnings("deprecation") + @Override + protected Object engineGetParameter(String param) throws InvalidParameterException { + return delegate.getParameter(param); + } + } + + public static class OpaqueCipherSpi extends CipherSpi { + private Cipher delegate; + + public OpaqueCipherSpi() { + } + + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + fail(); + } + + @Override + protected void engineSetPadding(String padding) throws NoSuchPaddingException { + fail(); + } + + @Override + protected int engineGetBlockSize() { + return delegate.getBlockSize(); + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return delegate.getOutputSize(inputLen); + } + + @Override + protected byte[] engineGetIV() { + return delegate.getIV(); + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return delegate.getParameters(); + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + getCipher(); + delegate.init(opmode, key, random); + } + + protected void getCipher() throws InvalidKeyException { + try { + delegate = Cipher.getInstance("RSA/ECB/NoPadding"); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new InvalidKeyException(e); + } + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + getCipher(); + delegate.init(opmode, key, params, random); + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + getCipher(); + delegate.init(opmode, key, params, random); + } + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + return delegate.update(input, inputOffset, inputLen); + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) throws ShortBufferException { + return delegate.update(input, inputOffset, inputLen, output, outputOffset); + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + return delegate.update(input, inputOffset, inputLen); + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + return delegate.doFinal(input, inputOffset, inputLen, output, outputOffset); + } + } + + private interface DelegatingPrivateKey { + PrivateKey getDelegate(); + } + + @SuppressWarnings("serial") + private static class OpaqueDelegatingECPrivateKey + implements ECKey, PrivateKey, DelegatingPrivateKey { + private final ECPrivateKey delegate; + + public OpaqueDelegatingECPrivateKey(ECPrivateKey delegate) { + this.delegate = delegate; + } + + @Override + public PrivateKey getDelegate() { + return delegate; + } + + @Override + public String getAlgorithm() { + return delegate.getAlgorithm(); + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return null; + } + + @Override + public ECParameterSpec getParams() { + return delegate.getParams(); + } + } + + @SuppressWarnings("serial") + private static class OpaqueDelegatingRSAPrivateKey + implements RSAKey, PrivateKey, DelegatingPrivateKey { + private final RSAPrivateKey delegate; + + public OpaqueDelegatingRSAPrivateKey(RSAPrivateKey delegate) { + this.delegate = delegate; + } + + @Override + public String getAlgorithm() { + return delegate.getAlgorithm(); + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return null; + } + + @Override + public BigInteger getModulus() { + return delegate.getModulus(); + } + + @Override + public PrivateKey getDelegate() { + return delegate; + } + } + public void test_SSLSocket_TrustManagerRuntimeException() throws Exception { TestSSLContext c = TestSSLContext.create(); SSLContext clientContext = SSLContext.getInstance("TLS"); @@ -1106,6 +1477,7 @@ public void test_SSLSocket_close() throws Exception { // ...so are a lot of other operations... HandshakeCompletedListener l = new HandshakeCompletedListener () { + @Override public void handshakeCompleted(HandshakeCompletedEvent e) {} }; client.addHandshakeCompletedListener(l); @@ -1383,10 +1755,10 @@ public void test_SSLSocket_setSoWriteTimeout() throws Exception { // Reflection is used so this can compile on the RI String expectedClassName = "com.android.org.conscrypt.OpenSSLSocketImpl"; - Class actualClass = client.getClass(); + Class actualClass = client.getClass(); assertEquals(expectedClassName, actualClass.getName()); Method setSoWriteTimeout = actualClass.getMethod("setSoWriteTimeout", - new Class[] { Integer.TYPE }); + new Class[] { Integer.TYPE }); setSoWriteTimeout.invoke(client, 1); @@ -1693,6 +2065,31 @@ public void run(SSLSocketFactory sslSocketFactory) throws Exception { }, getSSLSocketFactoriesToTest()); } + public void test_SSLSocket_ClientHello_supportedCurves() throws Exception { + ForEachRunner.runNamed(new ForEachRunner.Callback() { + @Override + public void run(SSLSocketFactory sslSocketFactory) throws Exception { + ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory); + + EllipticCurvesHelloExtension ecExtension = (EllipticCurvesHelloExtension) + clientHello.findExtensionByType(HelloExtension.TYPE_ELLIPTIC_CURVES); + final String[] supportedCurves; + if (ecExtension == null) { + supportedCurves = new String[0]; + } else { + assertTrue(ecExtension.wellFormed); + supportedCurves = new String[ecExtension.supported.size()]; + for (int i = 0; i < ecExtension.supported.size(); i++) { + EllipticCurve curve = ecExtension.supported.get(i); + supportedCurves[i] = curve.toString(); + } + } + + StandardNames.assertDefaultEllipticCurves(supportedCurves); + } + }, getSSLSocketFactoriesToTest()); + } + public void test_SSLSocket_ClientHello_clientProtocolVersion() throws Exception { ForEachRunner.runNamed(new ForEachRunner.Callback() { @Override @@ -1804,7 +2201,7 @@ public byte[] call() throws Exception { } return Arrays.copyOf(buffer, bytesRead); } finally { - IoUtils.closeQuietly(socket); + closeQuietly(socket); } } }); @@ -1832,7 +2229,7 @@ public Void call() throws Exception { } catch (IOException expected) {} return null; } finally { - IoUtils.closeQuietly(client); + closeQuietly(client); } } }); @@ -1841,9 +2238,9 @@ public Void call() throws Exception { return readFirstReceivedChunkFuture.get(10, TimeUnit.SECONDS); } finally { executorService.shutdownNow(); - IoUtils.closeQuietly(listeningSocket); - IoUtils.closeQuietly(sockets[0]); - IoUtils.closeQuietly(sockets[1]); + closeQuietly(listeningSocket); + closeQuietly(sockets[0]); + closeQuietly(sockets[1]); if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) { fail("Timed out while waiting for the test to shut down"); } @@ -1926,6 +2323,7 @@ public void test_SSLSocket_sendsTlsFallbackScsv_Fallback_Success() throws Except ExecutorService executor = Executors.newFixedThreadPool(2); Future s = executor.submit(new Callable() { + @Override public Void call() throws Exception { server.setEnabledProtocols(new String[] { "TLSv1.2" }); server.setEnabledCipherSuites(serverCipherSuites); @@ -1934,6 +2332,7 @@ public Void call() throws Exception { } }); Future c = executor.submit(new Callable() { + @Override public Void call() throws Exception { client.setEnabledProtocols(new String[] { "TLSv1.2" }); client.setEnabledCipherSuites(clientCipherSuites); @@ -1964,15 +2363,17 @@ public void test_SSLSocket_sendsNoTlsFallbackScsv_Fallback_Success() throws Exce ExecutorService executor = Executors.newFixedThreadPool(2); Future s = executor.submit(new Callable() { + @Override public Void call() throws Exception { - server.setEnabledProtocols(new String[] { "TLSv1", "SSLv3" }); + server.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1" }); server.startHandshake(); return null; } }); Future c = executor.submit(new Callable() { + @Override public Void call() throws Exception { - client.setEnabledProtocols(new String[] { "SSLv3" }); + client.setEnabledProtocols(new String[] { "TLSv1.1" }); client.startHandshake(); return null; } @@ -2007,8 +2408,9 @@ public void test_SSLSocket_sendsTlsFallbackScsv_InappropriateFallback_Failure() ExecutorService executor = Executors.newFixedThreadPool(2); Future s = executor.submit(new Callable() { + @Override public Void call() throws Exception { - server.setEnabledProtocols(new String[] { "TLSv1", "SSLv3" }); + server.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1" }); server.setEnabledCipherSuites(serverCipherSuites); try { server.startHandshake(); @@ -2022,8 +2424,9 @@ public Void call() throws Exception { } }); Future c = executor.submit(new Callable() { + @Override public Void call() throws Exception { - client.setEnabledProtocols(new String[] { "SSLv3" }); + client.setEnabledProtocols(new String[] { "TLSv1" }); client.setEnabledCipherSuites(clientCipherSuites); try { client.startHandshake(); @@ -2056,6 +2459,7 @@ public void test_SSLSocket_ClientGetsAlertDuringHandshake_HasGoodExceptionMessag ExecutorService executor = Executors.newFixedThreadPool(2); Future c = executor.submit(new Callable() { + @Override public Void call() throws Exception { try { client.startHandshake(); @@ -2068,6 +2472,7 @@ public Void call() throws Exception { } }); Future s = executor.submit(new Callable() { + @Override public Void call() throws Exception { // Wait until the client sends something. byte[] scratch = new byte[8192]; @@ -2107,6 +2512,7 @@ public void test_SSLSocket_ServerGetsAlertDuringHandshake_HasGoodExceptionMessag ExecutorService executor = Executors.newFixedThreadPool(2); Future s = executor.submit(new Callable() { + @Override public Void call() throws Exception { try { server.startHandshake(); @@ -2119,6 +2525,7 @@ public Void call() throws Exception { } }); Future c = executor.submit(new Callable() { + @Override public Void call() throws Exception { // Send bogus ClientHello: // TLSv1.2 Record Layer: Handshake Protocol: Client Hello @@ -2192,6 +2599,54 @@ public Void call() throws Exception { context.close(); } + public void test_SSLSocket_SSLv3Unsupported() throws Exception { + TestSSLContext context = TestSSLContext.create(); + + final SSLSocket client = (SSLSocket) + context.clientContext.getSocketFactory().createSocket(); + + // For app compatibility, SSLv3 is stripped out when setting only. + client.setEnabledProtocols(new String[] {"SSLv3"}); + assertEquals(0, client.getEnabledProtocols().length); + + try { + client.setEnabledProtocols(new String[] {"SSL"}); + fail("SSLSocket should not support SSL protocol"); + } catch (IllegalArgumentException expected) { + } + } + + // We modified the toString() of SSLSocket, and it's based on the output + // of Socket.toString(), so we want to make sure that a change in + // Socket.toString() doesn't cause us to output nonsense. + public void test_SSLSocket_toString() throws Exception { + // The actual implementation from a security provider might do something + // special for its toString(), so we create our own implementation + SSLSocket socket = new SSLSocket() { + @Override public String[] getSupportedCipherSuites() { return new String[0]; } + @Override public String[] getEnabledCipherSuites() { return new String[0]; } + @Override public void setEnabledCipherSuites(String[] strings) { } + @Override public String[] getSupportedProtocols() { return new String[0]; } + @Override public String[] getEnabledProtocols() { return new String[0]; } + @Override public void setEnabledProtocols(String[] strings) { } + @Override public SSLSession getSession() { return null; } + @Override public void addHandshakeCompletedListener( + HandshakeCompletedListener handshakeCompletedListener) { } + @Override public void removeHandshakeCompletedListener( + HandshakeCompletedListener handshakeCompletedListener) { } + @Override public void startHandshake() throws IOException { } + @Override public void setUseClientMode(boolean b) { } + @Override public boolean getUseClientMode() { return false; } + @Override public void setNeedClientAuth(boolean b) { } + @Override public boolean getNeedClientAuth() { return false; } + @Override public void setWantClientAuth(boolean b) { } + @Override public boolean getWantClientAuth() { return false; } + @Override public void setEnableSessionCreation(boolean b) { } + @Override public boolean getEnableSessionCreation() { return false; } + }; + assertTrue(socket.toString().startsWith("SSLSocket[")); + } + /** * Not run by default by JUnit, but can be run by Vogar by * specifying it explicitly (or with main method below) @@ -2217,6 +2672,28 @@ public void stress_test_TestSSLSocketPair_create() { } } + private static final void readFully(InputStream in, byte[] dst) throws IOException { + int offset = 0; + int byteCount = dst.length; + while (byteCount > 0) { + int bytesRead = in.read(dst, offset, byteCount); + if (bytesRead < 0) { + throw new EOFException(); + } + offset += bytesRead; + byteCount -= bytesRead; + } + } + + private static final void closeQuietly(Closeable socket) { + if (socket != null) { + try { + socket.close(); + } catch (Exception ignored) { + } + } + } + public static void main (String[] args) { new SSLSocketTest().stress_test_TestSSLSocketPair_create(); } diff --git a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java index 4f5d65880..5471b1f5d 100644 --- a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java +++ b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java @@ -120,6 +120,30 @@ public void testExceptionsForWrongDNs() { expectExceptionInDNConstructor("l=\\g0"); } + public void testNegativeLen() { + try { + X500Principal p = new X500Principal(new byte[]{ + 0x30, // DerValue.tag_Sequence read in DerValue#getSequence + 9, // Length of the vector. read in readVector. + // DerInputStream.getLength will just return this as 10 & 0x80 == 0 + -1, // Tag of the first value in the sequencevalue. Convenient so that it + // doesn't hold DerIndefLenConverter.isEOC() + (byte) 0x80, // Encoding in indefinite form + -1, // Second tag to be read by DerIndefLenConverter + (byte) 0x84, // Second length byte to be read, 0x80 means long form, 4 bytes + (byte) 0xff, // Length to be read by DerIndefLenConverter, -6, will move the + // buffer position to the second tag + (byte) 0xff, + (byte) 0xff, + (byte) -6, + 0, // Needed as otherwise it's detected that there's nothing after + // the length + }); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + } + private void expectExceptionInDNConstructor(String dn) { try { X500Principal principal = new X500Principal(dn); diff --git a/luni/src/test/java/libcore/net/MimeUtilsTest.java b/luni/src/test/java/libcore/net/MimeUtilsTest.java index ff2263275..ac0c0172d 100644 --- a/luni/src/test/java/libcore/net/MimeUtilsTest.java +++ b/luni/src/test/java/libcore/net/MimeUtilsTest.java @@ -18,8 +18,6 @@ import junit.framework.TestCase; -import libcore.net.MimeUtils; - public class MimeUtilsTest extends TestCase { public void test_15715370() { assertEquals("audio/flac", MimeUtils.guessMimeTypeFromExtension("flac")); @@ -52,4 +50,30 @@ public void testCommon() { public void test_18390752() { assertEquals("jpg", MimeUtils.guessExtensionFromMimeType("image/jpeg")); } + + public void test_30207891() { + assertTrue(MimeUtils.hasMimeType("IMAGE/PNG")); + assertTrue(MimeUtils.hasMimeType("IMAGE/png")); + assertFalse(MimeUtils.hasMimeType("")); + assertEquals("png", MimeUtils.guessExtensionFromMimeType("IMAGE/PNG")); + assertEquals("png", MimeUtils.guessExtensionFromMimeType("IMAGE/png")); + assertNull(MimeUtils.guessMimeTypeFromExtension("")); + assertNull(MimeUtils.guessMimeTypeFromExtension("doesnotexist")); + assertTrue(MimeUtils.hasExtension("PNG")); + assertTrue(MimeUtils.hasExtension("PnG")); + assertFalse(MimeUtils.hasExtension("")); + assertFalse(MimeUtils.hasExtension(".png")); + assertEquals("image/png", MimeUtils.guessMimeTypeFromExtension("PNG")); + assertEquals("image/png", MimeUtils.guessMimeTypeFromExtension("PnG")); + assertNull(MimeUtils.guessMimeTypeFromExtension(".png")); + assertNull(MimeUtils.guessMimeTypeFromExtension("")); + assertNull(MimeUtils.guessExtensionFromMimeType("doesnotexist")); + } + + public void test_30793548() { + assertEquals("video/3gpp", MimeUtils.guessMimeTypeFromExtension("3gpp")); + assertEquals("video/3gpp", MimeUtils.guessMimeTypeFromExtension("3gp")); + assertEquals("video/3gpp2", MimeUtils.guessMimeTypeFromExtension("3gpp2")); + assertEquals("video/3gpp2", MimeUtils.guessMimeTypeFromExtension("3g2")); + } } diff --git a/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java b/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java index 7a57ac19d..da85c7dca 100644 --- a/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java +++ b/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java @@ -28,8 +28,9 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.ErrorManager; @@ -214,6 +215,7 @@ public void testCleartextTrafficPolicyWithLoggingSocketHandler() throws Exceptio logger.publish(record); assertNull(mockErrorManager.getMostRecentException()); server.assertDataTransmittedByClient(); + logger.close(); } // Assert that client does not transmit any data when cleartext traffic is not permitted. @@ -235,8 +237,8 @@ public void testCleartextTrafficPolicyWithLoggingSocketHandler() throws Exceptio private static class CapturingServerSocket implements Closeable { private final ServerSocket mSocket; private final int mPort; - private final Thread mListeningThread; - private final FutureTask mFirstChunkReceivedFuture; + private final ExecutorService executor; + private final Future mFirstChunkReceivedFuture; /** * Constructs a new socket listening on a local port. @@ -252,32 +254,29 @@ public CapturingServerSocket() throws IOException { public CapturingServerSocket(final byte[] replyOnConnect) throws IOException { mSocket = new ServerSocket(0); mPort = mSocket.getLocalPort(); - mFirstChunkReceivedFuture = new FutureTask(new Callable() { - @Override - public byte[] call() throws Exception { - try (Socket client = mSocket.accept()) { - // Reply (if requested) - if (replyOnConnect != null) { - client.getOutputStream().write(replyOnConnect); - client.getOutputStream().flush(); - } - - // Read request - byte[] buf = new byte[64 * 1024]; - int chunkSize = client.getInputStream().read(buf); - if (chunkSize == -1) { - // Connection closed without any data received - return new byte[0]; - } - // Received some data - return Arrays.copyOf(buf, chunkSize); - } finally { - IoUtils.closeQuietly(mSocket); + Callable callable = () -> { + try (Socket client = mSocket.accept()) { + // Reply (if requested) + if (replyOnConnect != null) { + client.getOutputStream().write(replyOnConnect); + client.getOutputStream().flush(); } + + // Read request + byte[] buf = new byte[64 * 1024]; + int chunkSize = client.getInputStream().read(buf); + if (chunkSize == -1) { + // Connection closed without any data received + return new byte[0]; + } + // Received some data + return Arrays.copyOf(buf, chunkSize); + } finally { + IoUtils.closeQuietly(mSocket); } - }); - mListeningThread = new Thread(mFirstChunkReceivedFuture); - mListeningThread.start(); + }; + executor = Executors.newSingleThreadExecutor(); + mFirstChunkReceivedFuture = executor.submit(callable); } public int getPort() { @@ -291,12 +290,12 @@ public Future getFirstReceivedChunkFuture() { @Override public void close() { IoUtils.closeQuietly(mSocket); - mListeningThread.interrupt(); + executor.shutdown(); } private void assertDataTransmittedByClient() throws Exception { - byte[] firstChunkFromClient = getFirstReceivedChunkFuture().get(2, TimeUnit.SECONDS); + byte[] firstChunkFromClient = getFirstReceivedChunkFuture().get(4, TimeUnit.SECONDS); if ((firstChunkFromClient == null) || (firstChunkFromClient.length == 0)) { fail("Client did not transmit any data to server"); } @@ -306,7 +305,7 @@ private void assertNoDataTransmittedByClient() throws Exception { byte[] firstChunkFromClient; try { - firstChunkFromClient = getFirstReceivedChunkFuture().get(2, TimeUnit.SECONDS); + firstChunkFromClient = getFirstReceivedChunkFuture().get(4, TimeUnit.SECONDS); } catch (TimeoutException expected) { return; } @@ -359,5 +358,10 @@ public boolean isCleartextTrafficPermitted(String hostname) { return isCleartextTrafficPermitted(); } + + @Override + public boolean isCertificateTransparencyVerificationRequired(String hostname) { + return false; + } } } diff --git a/luni/src/test/java/libcore/sun/invoke/util/VerifyAccessTest.java b/luni/src/test/java/libcore/sun/invoke/util/VerifyAccessTest.java new file mode 100644 index 000000000..0c0275458 --- /dev/null +++ b/luni/src/test/java/libcore/sun/invoke/util/VerifyAccessTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.sun.invoke.util; + + +import junit.framework.TestCase; +import sun.invoke.util.VerifyAccess; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.Locale; +import java.util.Vector; + + +public class VerifyAccessTest extends TestCase { + public void testIsClassAccessible() { + // Always returns false when allowedModes == 0. Note that the "modes" allowed here + // are different from the ones used in MethodHandles. + assertFalse(VerifyAccess.isClassAccessible(Inner1.class, Inner2.class, 0)); + + // Classes in the same package are accessible when Lookup.PACKAGE is specified. + assertTrue(VerifyAccess.isClassAccessible(Inner1.class, Inner2.class, + MethodHandles.Lookup.PACKAGE)); + assertTrue(VerifyAccess.isClassAccessible(Inner1.class, Sibling.class, + MethodHandles.Lookup.PACKAGE)); + + // Public classes are always accessible. + assertTrue(VerifyAccess.isClassAccessible(String.class, Inner1.class, + MethodHandles.Lookup.PACKAGE)); + } + + public static class Inner1 { + } + + public static class Inner2 { + } + + public void testIsSamePackageMember() { + assertTrue(VerifyAccess.isSamePackageMember(Inner1.class, Inner2.class)); + assertTrue(VerifyAccess.isSamePackageMember(Inner1.class, VerifyAccessTest.class)); + + assertFalse(VerifyAccess.isSamePackageMember(Sibling.class, Inner1.class)); + } + + public void testIsSamePackage() { + // Both classes are in package java.util. + assertTrue(VerifyAccess.isSamePackage(Vector.class, List.class)); + // Make sure this works for inner classes. + assertTrue(VerifyAccess.isSamePackage(Vector.class, Locale.Builder.class)); + // Differing packages: java.lang vs java.util. + assertFalse(VerifyAccess.isSamePackage(Vector.class, String.class)); + + try { + VerifyAccess.isSamePackage(String[].class, List.class); + fail(); + } catch (IllegalArgumentException expected) { + } + } +} + +class Sibling { +} diff --git a/luni/src/test/java/libcore/sun/security/pkcs/PKCS9AttributeTest.java b/luni/src/test/java/libcore/sun/security/pkcs/PKCS9AttributeTest.java new file mode 100644 index 000000000..3195b02de --- /dev/null +++ b/luni/src/test/java/libcore/sun/security/pkcs/PKCS9AttributeTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.sun.security.pkcs; + +import junit.framework.TestCase; + +import sun.security.pkcs.PKCS9Attribute; +import sun.security.util.DerValue; + +public class PKCS9AttributeTest extends TestCase { + // Before rev/f9224fb49890, the unstructuredName attributes supported only IA5 strings. They + // support printable strings as well. + // See https://bugs.openjdk.java.net/browse/JDK-8016916 + void testUnstructuredNameWithPrintableString() throws Exception { + // SEQUENCE + // OBJECT IDENTIFIER1.2.840.113549.1.9.2 (unstructuredName) + // SET(1 elem) + // PrintableString requestTestWithExt + byte[] unstructuredNamePkcs9Attribute = { + 0x30, 0x21, 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, + 0x01, 0x09, 0x02, 0x31, 0x14, 0x13, 0x12, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x54, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x45, 0x78, 0x74}; + new PKCS9Attribute(new DerValue(unstructuredNamePkcs9Attribute)); + } +} + diff --git a/luni/src/test/java/libcore/sun/security/x509/AlgorithmIdTest.java b/luni/src/test/java/libcore/sun/security/x509/AlgorithmIdTest.java new file mode 100644 index 000000000..b20816f6c --- /dev/null +++ b/luni/src/test/java/libcore/sun/security/x509/AlgorithmIdTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.sun.security.x509; + +import junit.framework.TestCase; + +import java.util.function.Function; + +import sun.security.util.ObjectIdentifier; +import sun.security.x509.AlgorithmId; + + +public class AlgorithmIdTest extends TestCase { + + public void test_get_String() throws Exception { + assertEquals("1.3.14.3.2.26", AlgorithmId.get("SHA-1").getOID().toString()); + assertEquals("1.3.14.3.2.26", AlgorithmId.get("SHA1").getOID().toString()); + assertEquals("2.16.840.1.101.3.4.2.4", AlgorithmId.get("SHA-224").getOID().toString()); + + // Would throw NoSuchAlgorithmException in N + assertEquals("2.16.840.1.101.3.4.2.4", AlgorithmId.get("SHA224").getOID().toString()); + + assertEquals("2.16.840.1.101.3.4.2.1", AlgorithmId.get("SHA-256").getOID().toString()); + + // Would throw NoSuchAlgorithmException in N + assertEquals("2.16.840.1.101.3.4.2.1", AlgorithmId.get("SHA256").getOID().toString()); + + assertEquals( + "2.16.840.1.101.3.4.3.1", AlgorithmId.get("SHA224WithDSA").getOID().toString()); + assertEquals( + "2.16.840.1.101.3.4.3.2", AlgorithmId.get("SHA256WithDSA").getOID().toString()); + // Case is irrelevant. + assertEquals( + "2.16.840.1.101.3.4.3.1", AlgorithmId.get("sHA224withDSA").getOID().toString()); + assertEquals( + "2.16.840.1.101.3.4.3.2", AlgorithmId.get("sHA256withDSA").getOID().toString()); + + // Used to be 2.16.840.1.101.3.4.42 until N because BouncyCastle accepts this alias. It + // started with a typo they once had and for compatibility they still support it. Since we + // scan the aliases, we were picking it as the canonical OID for AES. See: + // http://www.docjar.org/html/api/org/bouncycastle/jce/provider/symmetric/AESMappings.java.html + assertEquals("2.16.840.1.101.3.4.1", AlgorithmId.get("AES").getOID().toString()); + assertEquals("1.3.132.1.12", AlgorithmId.get("ECDH").getOID().toString()); + } + + public void test_getName() throws Exception { + // Was "SHA" in N + assertEquals("SHA-1", getOidName("1.3.14.3.2.26")); + assertEquals("SHA-224", getOidName("2.16.840.1.101.3.4.2.4")); + // Was "SHA256" in N + assertEquals("SHA-256", getOidName("2.16.840.1.101.3.4.2.1")); + // Were SHA224WITHDSA, etc in N + assertEquals("SHA224withDSA", getOidName("2.16.840.1.101.3.4.3.1")); + assertEquals("SHA256withDSA", getOidName("2.16.840.1.101.3.4.3.2")); + assertEquals("SHA224withRSA", getOidName("1.2.840.113549.1.1.14")); + + assertEquals("AES", getOidName("2.16.840.1.101.3.4.1")); + // AES is also the result of 2.16.840.1.101.3.4.42 because BouncyCastle accepts this alias. + // It started with a typo they once had and for compatibility they still support it. Since + // we scan the aliases, we were picking it. See: + // http://www.docjar.org/html/api/org/bouncycastle/jce/provider/symmetric/AESMappings.java.html + assertEquals("AES", getOidName("2.16.840.1.101.3.4.42")); + + // ECDH not present before and in N + assertEquals("ECDH", getOidName("1.3.132.1.12")); + } + + private String getOidName(String oid) throws Exception { + return new AlgorithmId(new ObjectIdentifier(oid)).getName(); + } +} \ No newline at end of file diff --git a/luni/src/test/java/libcore/sun/security/x509/KeyUsageExtensionTest.java b/luni/src/test/java/libcore/sun/security/x509/KeyUsageExtensionTest.java new file mode 100644 index 000000000..07f9626af --- /dev/null +++ b/luni/src/test/java/libcore/sun/security/x509/KeyUsageExtensionTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.sun.security.x509; + + +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.function.Function; + +import sun.security.x509.KeyUsageExtension; + +public class KeyUsageExtensionTest extends TestCase { + /** + * The logic for toString was changed in rev/04cda5b7a3c1. The expected result is the same + * before and after the change. + */ + public void testToString() throws Exception { + String prefix = "ObjectId: 2.5.29.15 Criticality=true\n" + + "KeyUsage [\n"; + + String[] parts = new String[] { + " DigitalSignature\n", + " Non_repudiation\n", + " Key_Encipherment\n", + " Data_Encipherment\n", + " Key_Agreement\n", + " Key_CertSign\n", + " Crl_Sign\n", + " Encipher_Only\n", + " Decipher_Only\n" + }; + + String suffix = "]\n"; + Function objectCreator = byteArray -> { + try { + return new KeyUsageExtension(byteArray); + } catch (IOException e) { + throw new RuntimeException(e); + } + }; + Utils.test_toString_bitArrayBasedClass(parts, objectCreator, prefix, suffix); + } +} diff --git a/luni/src/test/java/libcore/sun/security/x509/NetscapeCertTypeExtensionTest.java b/luni/src/test/java/libcore/sun/security/x509/NetscapeCertTypeExtensionTest.java new file mode 100644 index 000000000..f463dee49 --- /dev/null +++ b/luni/src/test/java/libcore/sun/security/x509/NetscapeCertTypeExtensionTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 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 + */ +/* + * Copyright (C) 2016 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 + */ + +package libcore.sun.security.x509; + + +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.function.Function; + +import sun.security.x509.NetscapeCertTypeExtension; + +public class NetscapeCertTypeExtensionTest extends TestCase { + /** + * The logic for toString was changed in rev/04cda5b7a3c1. The expected result is the same + * before and after the change. + */ + public void testToString() throws Exception { + String prefix = "ObjectId: 2.16.840.1.113730.1.1 Criticality=true\n" + + "NetscapeCertType [\n"; + + String[] parts = new String[] { + " SSL client\n", + " SSL server\n", + " S/MIME\n", + " Object Signing\n", + "", // Note: byte 4 is reserved. + " SSL CA\n", + " S/MIME CA\n", + " Object Signing CA", + }; + + String suffix = "]\n"; + Function objectCreator = byteArray -> { + try { + return new NetscapeCertTypeExtension(byteArray); + } catch (IOException e) { + throw new RuntimeException(e); + } + }; + Utils.test_toString_bitArrayBasedClass(parts, objectCreator, prefix, suffix); + } +} \ No newline at end of file diff --git a/luni/src/test/java/libcore/sun/security/x509/ReasonFlagsTest.java b/luni/src/test/java/libcore/sun/security/x509/ReasonFlagsTest.java new file mode 100644 index 000000000..074f24bae --- /dev/null +++ b/luni/src/test/java/libcore/sun/security/x509/ReasonFlagsTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 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 + */ + +package libcore.sun.security.x509; + +import junit.framework.TestCase; +import java.util.function.Function; +import sun.security.x509.ReasonFlags; + + +public class ReasonFlagsTest extends TestCase { + /** + * The logic for toString was changed in rev/04cda5b7a3c1. The expected result is the same + * before and after the change. + */ + public void testToString() throws Exception { + String prefix = "Reason Flags [\n"; + + String[] parts = new String[] { + " Unused\n", + " Key Compromise\n", + " CA Compromise\n", + " Affiliation_Changed\n", + " Superseded\n", + " Cessation Of Operation\n", + " Certificate Hold\n", + " Privilege Withdrawn\n", + " AA Compromise\n" + }; + + String suffix = "]\n"; + Function objectCreator = byteArray -> new ReasonFlags(byteArray); + Utils.test_toString_bitArrayBasedClass(parts, objectCreator, prefix, suffix); + } +} + diff --git a/luni/src/test/java/libcore/util/NativeAllocationRegistryTest.java b/luni/src/test/java/libcore/util/NativeAllocationRegistryTest.java index af45bfa51..99be1fdc0 100644 --- a/luni/src/test/java/libcore/util/NativeAllocationRegistryTest.java +++ b/luni/src/test/java/libcore/util/NativeAllocationRegistryTest.java @@ -87,9 +87,8 @@ public long allocate() { // Verify most of the allocations have been freed. long nativeBytes = getNumNativeBytesAllocated(); - assertTrue("Native bytes allocated (" + nativeBytes + ")" - + " exceeds max memory (" + max + ")", - getNumNativeBytesAllocated() < max); + assertTrue("Excessive native bytes still allocated (" + nativeBytes + ")" + + " given max memory of (" + max + ")", nativeBytes < 2 * max); } public void testNativeAllocationAllocatorAndSharedRegistry() { diff --git a/luni/src/test/java/libcore/util/TimeZoneDataFilesTest.java b/luni/src/test/java/libcore/util/TimeZoneDataFilesTest.java new file mode 100644 index 000000000..efba9000d --- /dev/null +++ b/luni/src/test/java/libcore/util/TimeZoneDataFilesTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 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. + */ + +package libcore.util; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TimeZoneDataFilesTest { + + @Test + public void getTimeZoneFilePaths() { + String[] paths = TimeZoneDataFiles.getTimeZoneFilePaths("foo"); + assertEquals(2, paths.length); + + assertTrue(paths[0].contains("/misc/zoneinfo/current/")); + assertTrue(paths[0].endsWith("foo")); + + assertTrue(paths[1].contains("/usr/share/zoneinfo/")); + assertTrue(paths[1].endsWith("foo")); + } + + // http://b/34867424 + @Test + public void generateIcuDataPath_includesTimeZoneOverride() { + String icuDataPath = System.getProperty("android.icu.impl.ICUBinary.dataPath"); + assertEquals(icuDataPath, TimeZoneDataFiles.generateIcuDataPath()); + + String[] paths = icuDataPath.split(":"); + assertEquals(2, paths.length); + + assertTrue(paths[0].contains("/misc/zoneinfo/current/icu")); + assertTrue(paths[1].contains("/usr/icu")); + } +} diff --git a/luni/src/test/java/libcore/util/ZoneInfoDBTest.java b/luni/src/test/java/libcore/util/ZoneInfoDBTest.java index a90bb8e23..e6ec4df0d 100644 --- a/luni/src/test/java/libcore/util/ZoneInfoDBTest.java +++ b/luni/src/test/java/libcore/util/ZoneInfoDBTest.java @@ -18,68 +18,201 @@ import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.RandomAccessFile; -import java.util.TimeZone; + +import libcore.tzdata.testing.ZoneInfoTestHelper; + +import static libcore.util.ZoneInfoDB.TzData.SIZEOF_INDEX_ENTRY; public class ZoneInfoDBTest extends junit.framework.TestCase { // The base tzdata file, always present on a device. - private static final String TZDATA_IN_ROOT = - System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata"; + private static final String SYSTEM_TZDATA_FILE = + TimeZoneDataFiles.getSystemTimeZoneFile(ZoneInfoDB.TZDATA_FILE); // An empty override file should fall back to the default file. - public void testEmptyOverrideFile() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + public void testLoadTzDataWithFallback_emptyOverrideFile() throws Exception { + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(SYSTEM_TZDATA_FILE); + String emptyFilePath = makeEmptyFile().getPath(); + ZoneInfoDB.TzData dataWithEmptyOverride = - new ZoneInfoDB.TzData(makeEmptyFile(), TZDATA_IN_ROOT); + ZoneInfoDB.TzData.loadTzDataWithFallback(emptyFilePath, SYSTEM_TZDATA_FILE); assertEquals(data.getVersion(), dataWithEmptyOverride.getVersion()); assertEquals(data.getAvailableIDs().length, dataWithEmptyOverride.getAvailableIDs().length); } // A corrupt override file should fall back to the default file. - public void testCorruptOverrideFile() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + public void testLoadTzDataWithFallback_corruptOverrideFile() throws Exception { + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(SYSTEM_TZDATA_FILE); + String corruptFilePath = makeCorruptFile().getPath(); + ZoneInfoDB.TzData dataWithCorruptOverride = - new ZoneInfoDB.TzData(makeCorruptFile(), TZDATA_IN_ROOT); + ZoneInfoDB.TzData.loadTzDataWithFallback(corruptFilePath, SYSTEM_TZDATA_FILE); assertEquals(data.getVersion(), dataWithCorruptOverride.getVersion()); assertEquals(data.getAvailableIDs().length, dataWithCorruptOverride.getAvailableIDs().length); } // Given no tzdata files we can use, we should fall back to built-in "GMT". - public void testNoGoodFile() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(makeEmptyFile()); + public void testLoadTzDataWithFallback_noGoodFile() throws Exception { + String emptyFilePath = makeEmptyFile().getPath(); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzDataWithFallback(emptyFilePath); assertEquals("missing", data.getVersion()); assertEquals(1, data.getAvailableIDs().length); assertEquals("GMT", data.getAvailableIDs()[0]); } // Given a valid override file, we should find ourselves using that. - public void testGoodOverrideFile() throws Exception { - RandomAccessFile in = new RandomAccessFile(TZDATA_IN_ROOT, "r"); + public void testLoadTzDataWithFallback_goodOverrideFile() throws Exception { + RandomAccessFile in = new RandomAccessFile(SYSTEM_TZDATA_FILE, "r"); byte[] content = new byte[(int) in.length()]; in.readFully(content); + in.close(); + // Bump the version number to one long past where humans will be extinct. content[6] = '9'; content[7] = '9'; content[8] = '9'; content[9] = '9'; content[10] = 'z'; - in.close(); - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); - String goodFile = makeTemporaryFile(content); + File goodFile = makeTemporaryFile(content); try { - ZoneInfoDB.TzData dataWithOverride = new ZoneInfoDB.TzData(goodFile, TZDATA_IN_ROOT); + ZoneInfoDB.TzData dataWithOverride = + ZoneInfoDB.TzData.loadTzDataWithFallback(goodFile.getPath(), SYSTEM_TZDATA_FILE); assertEquals("9999z", dataWithOverride.getVersion()); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(SYSTEM_TZDATA_FILE); assertEquals(data.getAvailableIDs().length, dataWithOverride.getAvailableIDs().length); } finally { - new File(goodFile).delete(); + goodFile.delete(); + } + } + + public void testLoadTzData_badHeader() throws Exception { + RandomAccessFile in = new RandomAccessFile(SYSTEM_TZDATA_FILE, "r"); + byte[] content = new byte[(int) in.length()]; + in.readFully(content); + in.close(); + + // Break the header. + content[0] = 'a'; + checkInvalidDataDetected(content); + } + + public void testLoadTzData_validTestData() throws Exception { + byte[] data = new ZoneInfoTestHelper.TzDataBuilder().initializeToValid().build(); + File testFile = makeTemporaryFile(data); + try { + assertNotNull(ZoneInfoDB.TzData.loadTzData(testFile.getPath())); + } finally { + testFile.delete(); + } + } + + public void testLoadTzData_invalidOffsets() throws Exception { + ZoneInfoTestHelper.TzDataBuilder builder = + new ZoneInfoTestHelper.TzDataBuilder().initializeToValid(); + + // Sections must be in the correct order: section sizing is calculated using them. + builder.setIndexOffsetOverride(10); + builder.setDataOffsetOverride(30); + + byte[] data = builder.build(); + // The offsets must all be under the total size of the file for this test to be valid. + assertTrue(30 < data.length); + checkInvalidDataDetected(data); + } + + public void testLoadTzData_zoneTabOutsideFile() throws Exception { + ZoneInfoTestHelper.TzDataBuilder builder = + new ZoneInfoTestHelper.TzDataBuilder() + .initializeToValid(); + + // Sections must be in the correct order: section sizing is calculated using them. + builder.setIndexOffsetOverride(10); + builder.setDataOffsetOverride(10 + SIZEOF_INDEX_ENTRY); + builder.setZoneTabOffsetOverride(3000); // This is invalid if it is outside of the file. + + byte[] data = builder.build(); + // The zoneTab offset must be outside of the file for this test to be valid. + assertTrue(3000 > data.length); + checkInvalidDataDetected(data); + } + + public void testLoadTzData_nonDivisibleIndex() throws Exception { + ZoneInfoTestHelper.TzDataBuilder builder = + new ZoneInfoTestHelper.TzDataBuilder().initializeToValid(); + + // Sections must be in the correct order: section sizing is calculated using them. + int indexOffset = 10; + builder.setIndexOffsetOverride(indexOffset); + int dataOffset = indexOffset + ZoneInfoDB.TzData.SIZEOF_INDEX_ENTRY - 1; + builder.setDataOffsetOverride(dataOffset); + builder.setZoneTabOffsetOverride(dataOffset + 40); + + byte[] data = builder.build(); + // The zoneTab offset must be outside of the file for this test to be valid. + assertTrue(3000 > data.length); + checkInvalidDataDetected(data); + } + + public void testLoadTzData_badId() throws Exception { + ZoneInfoTestHelper.TzDataBuilder builder = + new ZoneInfoTestHelper.TzDataBuilder().initializeToValid(); + builder.clearZicData(); + byte[] validZicData = + new ZoneInfoTestHelper.ZicDataBuilder().initializeToValid().build(); + builder.addZicData("", validZicData); // "" is an invalid ID + + checkInvalidDataDetected(builder.build()); + } + + public void testLoadTzData_badIdOrder() throws Exception { + ZoneInfoTestHelper.TzDataBuilder builder = + new ZoneInfoTestHelper.TzDataBuilder().initializeToValid(); + builder.clearZicData(); + byte[] validZicData = + new ZoneInfoTestHelper.ZicDataBuilder().initializeToValid().build(); + builder.addZicData("Europe/Zurich", validZicData); + builder.addZicData("Europe/London", validZicData); + + checkInvalidDataDetected(builder.build()); + } + + public void testLoadTzData_duplicateId() throws Exception { + ZoneInfoTestHelper.TzDataBuilder builder = + new ZoneInfoTestHelper.TzDataBuilder().initializeToValid(); + builder.clearZicData(); + byte[] validZicData = + new ZoneInfoTestHelper.ZicDataBuilder().initializeToValid().build(); + builder.addZicData("Europe/London", validZicData); + builder.addZicData("Europe/London", validZicData); + + checkInvalidDataDetected(builder.build()); + } + + public void testLoadTzData_badZicLength() throws Exception { + ZoneInfoTestHelper.TzDataBuilder builder = + new ZoneInfoTestHelper.TzDataBuilder().initializeToValid(); + builder.clearZicData(); + byte[] invalidZicData = "This is too short".getBytes(); + builder.addZicData("Europe/London", invalidZicData); + + checkInvalidDataDetected(builder.build()); + } + + private static void checkInvalidDataDetected(byte[] data) throws Exception { + File testFile = makeTemporaryFile(data); + try { + assertNull(ZoneInfoDB.TzData.loadTzData(testFile.getPath())); + } finally { + testFile.delete(); } } // Confirms any caching that exists correctly handles TimeZone mutability. public void testMakeTimeZone_timeZoneMutability() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(SYSTEM_TZDATA_FILE); String tzId = "Europe/London"; ZoneInfo first = data.makeTimeZone(tzId); ZoneInfo second = data.makeTimeZone(tzId); @@ -96,30 +229,71 @@ public void testMakeTimeZone_timeZoneMutability() throws Exception { } public void testMakeTimeZone_notFound() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(SYSTEM_TZDATA_FILE); assertNull(data.makeTimeZone("THIS_TZ_DOES_NOT_EXIST")); assertFalse(data.hasTimeZone("THIS_TZ_DOES_NOT_EXIST")); } public void testMakeTimeZone_found() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(SYSTEM_TZDATA_FILE); assertNotNull(data.makeTimeZone("Europe/London")); assertTrue(data.hasTimeZone("Europe/London")); } - private static String makeCorruptFile() throws Exception { + public void testGetRulesVersion() throws Exception { + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(SYSTEM_TZDATA_FILE); + + String rulesVersion = ZoneInfoDB.TzData.getRulesVersion(new File(SYSTEM_TZDATA_FILE)); + assertEquals(data.getVersion(), rulesVersion); + } + + public void testGetRulesVersion_corruptFile() throws Exception { + File corruptFilePath = makeCorruptFile(); + try { + ZoneInfoDB.TzData.getRulesVersion(corruptFilePath); + fail(); + } catch (IOException expected) { + } + } + + public void testGetRulesVersion_emptyFile() throws Exception { + File emptyFilePath = makeEmptyFile(); + try { + ZoneInfoDB.TzData.getRulesVersion(emptyFilePath); + fail(); + } catch (IOException expected) { + } + } + + public void testGetRulesVersion_missingFile() throws Exception { + File missingFile = makeMissingFile(); + try { + ZoneInfoDB.TzData.getRulesVersion(missingFile); + fail(); + } catch (IOException expected) { + } + } + + private static File makeMissingFile() throws Exception { + File file = File.createTempFile("temp-", ".txt"); + assertTrue(file.delete()); + assertFalse(file.exists()); + return file; + } + + private static File makeCorruptFile() throws Exception { return makeTemporaryFile("invalid content".getBytes()); } - private static String makeEmptyFile() throws Exception { + private static File makeEmptyFile() throws Exception { return makeTemporaryFile(new byte[0]); } - private static String makeTemporaryFile(byte[] content) throws Exception { + private static File makeTemporaryFile(byte[] content) throws Exception { File f = File.createTempFile("temp-", ".txt"); FileOutputStream fos = new FileOutputStream(f); fos.write(content); fos.close(); - return f.getPath(); + return f; } } diff --git a/luni/src/test/java/libcore/util/ZoneInfoTest.java b/luni/src/test/java/libcore/util/ZoneInfoTest.java index 67e229d90..82418caed 100644 --- a/luni/src/test/java/libcore/util/ZoneInfoTest.java +++ b/luni/src/test/java/libcore/util/ZoneInfoTest.java @@ -16,14 +16,15 @@ package libcore.util; import junit.framework.TestCase; -import java.io.ByteArrayOutputStream; + +import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; -import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Date; import libcore.io.BufferIterator; +import libcore.tzdata.testing.ZoneInfoTestHelper; /** * Tests for {@link ZoneInfo} @@ -31,28 +32,27 @@ public class ZoneInfoTest extends TestCase { /** - * Checks that a {@link ZoneInfo} cannot be created without any offsets. + * Checks that a {@link ZoneInfo} cannot be created without any types. */ - public void testMakeTimeZone_NoOffsets() throws Exception { - int[][] times = {}; - int[][] offsets = {}; + public void testMakeTimeZone_NoTypes() throws Exception { + int[][] transitions = {}; + int[][] types = {}; try { - createZoneInfo(times, offsets); - fail("Did not detect no transitions"); - } catch (IllegalStateException expected) { - // Expected this to happen + createZoneInfo(transitions, types); + fail(); + } catch (IOException expected) { } } /** - * Checks that a {@link ZoneInfo} can be created with one offset and no transitions. + * Checks that a {@link ZoneInfo} can be created with one type and no transitions. */ - public void testMakeTimeZone_OneOffset_NoTransitions() throws Exception { - int[][] times = {}; - int[][] offsets = { + public void testMakeTimeZone_OneType_NoTransitions() throws Exception { + int[][] transitions = {}; + int[][] types = { { 4800, 0 } }; - ZoneInfo zoneInfo = createZoneInfo(times, offsets); + ZoneInfo zoneInfo = createZoneInfo(transitions, types); // If there are no transitions then the offset should be constant irrespective of the time. assertEquals(secondsInMillis(4800), zoneInfo.getOffset(Long.MIN_VALUE)); @@ -63,21 +63,21 @@ public void testMakeTimeZone_OneOffset_NoTransitions() throws Exception { assertFalse("Doesn't use DST", zoneInfo.useDaylightTime()); assertEquals(0, zoneInfo.getDSTSavings()); - // The raw offset should be the first offset. + // The raw offset should be the offset of the first type. assertEquals(secondsInMillis(4800), zoneInfo.getRawOffset()); } /** * Checks that a {@link ZoneInfo} can be created with one non-DST transition. */ - public void testMakeTimeZone_OneNonDstTransition() throws Exception { - int[][] times = { + public void testReadTimeZone_OneNonDstTransition() throws Exception { + int[][] transitions = { { 0, 0 } }; - int[][] offsets = { + int[][] types = { { 3600, 0 } }; - ZoneInfo zoneInfo = createZoneInfo(times, offsets); + ZoneInfo zoneInfo = createZoneInfo(transitions, types); // Any time before the first transition is assumed to use the first standard transition. assertEquals(secondsInMillis(3600), zoneInfo.getOffset(secondsInMillis(-2))); @@ -88,25 +88,24 @@ public void testMakeTimeZone_OneNonDstTransition() throws Exception { assertFalse("Doesn't use DST", zoneInfo.useDaylightTime()); assertEquals(0, zoneInfo.getDSTSavings()); - // The raw offset should be the first offset. + // The raw offset should be the offset of the first type. assertEquals(secondsInMillis(3600), zoneInfo.getRawOffset()); } /** * Checks that a {@link ZoneInfo} cannot be created with one DST but no non-DSTs transitions. */ - public void testMakeTimeZone_OneDstTransition() throws Exception { - int[][] times = { + public void testReadTimeZone_OneDstTransition() throws Exception { + int[][] transitions = { { 0, 0 } }; - int[][] offsets = { + int[][] types = { { 3600, 1 } }; try { - createZoneInfo(times, offsets); + createZoneInfo(transitions, types); fail("Did not detect no non-DST transitions"); } catch (IllegalStateException expected) { - // Expected this to happen } } @@ -114,18 +113,18 @@ public void testMakeTimeZone_OneDstTransition() throws Exception { * Checks to make sure that rounding the time from milliseconds to seconds does not cause issues * around the boundary of negative transitions. */ - public void testMakeTimeZone_NegativeTransition() throws Exception { - int[][] times = { + public void testReadTimeZone_NegativeTransition() throws Exception { + int[][] transitions = { { -2000, 0 }, { -5, 1 }, { 0, 2 }, }; - int[][] offsets = { + int[][] types = { { 1800, 0 }, { 3600, 1 }, { 5400, 0 } }; - ZoneInfo zoneInfo = createZoneInfo(times, offsets); + ZoneInfo zoneInfo = createZoneInfo(transitions, types); // Even a millisecond before a transition means that the transition is not active. assertEquals(1800000, zoneInfo.getOffset(secondsInMillis(-5) - 1)); @@ -148,18 +147,18 @@ public void testMakeTimeZone_NegativeTransition() throws Exception { * Checks to make sure that rounding the time from milliseconds to seconds does not cause issues * around the boundary of positive transitions. */ - public void testMakeTimeZone_PositiveTransition() throws Exception { - int[][] times = { + public void testReadTimeZone_PositiveTransition() throws Exception { + int[][] transitions = { { 0, 0 }, { 5, 1 }, { 2000, 2 }, }; - int[][] offsets = { + int[][] types = { { 1800, 0 }, { 3600, 1 }, { 5400, 0 } }; - ZoneInfo zoneInfo = createZoneInfo(times, offsets); + ZoneInfo zoneInfo = createZoneInfo(transitions, types); // Even a millisecond before a transition means that the transition is not active. assertEquals(secondsInMillis(1800), zoneInfo.getOffset(secondsInMillis(5) - 1)); @@ -182,13 +181,13 @@ public void testMakeTimeZone_PositiveTransition() throws Exception { * Checks that creating a {@link ZoneInfo} with future DST transitions but no past DST * transitions where the transition times are negative is not affected by rounding issues. */ - public void testMakeTimeZone_HasFutureDST_NoPastDST_NegativeTransitions() throws Exception { - int[][] times = { + public void testReadTimeZone_HasFutureDST_NoPastDST_NegativeTransitions() throws Exception { + int[][] transitions = { { -2000, 0 }, { -500, 1 }, { -100, 2 }, }; - int[][] offsets = { + int[][] types = { { 1800, 0 }, { 3600, 0 }, { 5400, 1 } @@ -198,14 +197,14 @@ public void testMakeTimeZone_HasFutureDST_NoPastDST_NegativeTransitions() throws // Or in other words (5400 - 3600) * 1000 int expectedDSTSavings = secondsInMillis(5400 - 3600); - ZoneInfo zoneInfo = createZoneInfo(times, offsets, secondsInMillis(-700)); + ZoneInfo zoneInfo = createZoneInfo(transitions, types, secondsInMillis(-700)); assertTrue("Should use DST but doesn't", zoneInfo.useDaylightTime()); assertEquals(expectedDSTSavings, zoneInfo.getDSTSavings()); // Now create one a few milliseconds before the DST transition to make sure that rounding // errors don't cause a problem. - zoneInfo = createZoneInfo(times, offsets, secondsInMillis(-100) - 5); + zoneInfo = createZoneInfo(transitions, types, secondsInMillis(-100) - 5); assertTrue("Should use DST but doesn't", zoneInfo.useDaylightTime()); assertEquals(expectedDSTSavings, zoneInfo.getDSTSavings()); @@ -215,13 +214,13 @@ public void testMakeTimeZone_HasFutureDST_NoPastDST_NegativeTransitions() throws * Checks that creating a {@link ZoneInfo} with future DST transitions but no past DST * transitions where the transition times are positive is not affected by rounding issues. */ - public void testMakeTimeZone_HasFutureDST_NoPastDST_PositiveTransitions() throws Exception { - int[][] times = { + public void testReadTimeZone_HasFutureDST_NoPastDST_PositiveTransitions() throws Exception { + int[][] transitions = { { 4000, 0 }, { 5500, 1 }, { 6000, 2 }, }; - int[][] offsets = { + int[][] types = { { 1800, 0 }, { 3600, 0 }, { 7200, 1 } @@ -231,14 +230,15 @@ public void testMakeTimeZone_HasFutureDST_NoPastDST_PositiveTransitions() throws // Or in other words (7200 - 3600) * 1000 int expectedDSTSavings = secondsInMillis(7200 - 3600); - ZoneInfo zoneInfo = createZoneInfo(times, offsets, secondsInMillis(4500)); + ZoneInfo zoneInfo = createZoneInfo( + transitions, types, secondsInMillis(4500) /* currentTimeMillis */); assertTrue("Should use DST but doesn't", zoneInfo.useDaylightTime()); assertEquals(expectedDSTSavings, zoneInfo.getDSTSavings()); // Now create one a few milliseconds before the DST transition to make sure that rounding // errors don't cause a problem. - zoneInfo = createZoneInfo(times, offsets, secondsInMillis(6000) - 5); + zoneInfo = createZoneInfo(transitions, types, secondsInMillis(6000) - 5); assertTrue("Should use DST but doesn't", zoneInfo.useDaylightTime()); assertEquals(expectedDSTSavings, zoneInfo.getDSTSavings()); @@ -248,26 +248,27 @@ public void testMakeTimeZone_HasFutureDST_NoPastDST_PositiveTransitions() throws * Checks that creating a {@link ZoneInfo} with past DST transitions but no future DST * transitions where the transition times are negative is not affected by rounding issues. */ - public void testMakeTimeZone_HasPastDST_NoFutureDST_NegativeTransitions() throws Exception { - int[][] times = { + public void testReadTimeZone_HasPastDST_NoFutureDST_NegativeTransitions() throws Exception { + int[][] transitions = { { -5000, 0 }, { -2000, 1 }, { -500, 0 }, { 0, 2 }, }; - int[][] offsets = { + int[][] types = { { 3600, 0 }, { 1800, 1 }, { 5400, 0 } }; - ZoneInfo zoneInfo = createZoneInfo(times, offsets, secondsInMillis(-1)); + ZoneInfo zoneInfo = createZoneInfo(transitions, types, + secondsInMillis(-1) /* currentTimeMillis */); assertFalse("Shouldn't use DST but does", zoneInfo.useDaylightTime()); assertEquals(0, zoneInfo.getDSTSavings()); // Now create one a few milliseconds after the DST transition to make sure that rounding // errors don't cause a problem. - zoneInfo = createZoneInfo(times, offsets, secondsInMillis(-2000) + 5); + zoneInfo = createZoneInfo(transitions, types, secondsInMillis(-2000) + 5); assertFalse("Shouldn't use DST but does", zoneInfo.useDaylightTime()); assertEquals(0, zoneInfo.getDSTSavings()); @@ -277,72 +278,53 @@ public void testMakeTimeZone_HasPastDST_NoFutureDST_NegativeTransitions() throws * Checks that creating a {@link ZoneInfo} with past DST transitions but no future DST * transitions where the transition times are positive is not affected by rounding issues. */ - public void testMakeTimeZone_HasPastDST_NoFutureDST_PositiveTransitions() throws Exception { - int[][] times = { + public void testReadTimeZone_HasPastDST_NoFutureDST_PositiveTransitions() throws Exception { + int[][] transitions = { { 1000, 0 }, { 4000, 1 }, { 5500, 0 }, { 6000, 2 }, }; - int[][] offsets = { + int[][] types = { { 3600, 0 }, { 1800, 1 }, { 5400, 0 } }; - ZoneInfo zoneInfo = createZoneInfo(times, offsets, secondsInMillis(4700)); + ZoneInfo zoneInfo = createZoneInfo(transitions, types, secondsInMillis(4700)); assertFalse("Shouldn't use DST but does", zoneInfo.useDaylightTime()); assertEquals(0, zoneInfo.getDSTSavings()); // Now create one a few milliseconds after the DST transition to make sure that rounding // errors don't cause a problem. - zoneInfo = createZoneInfo(times, offsets, secondsInMillis(4000) + 5); + zoneInfo = createZoneInfo(transitions, types, secondsInMillis(4000) + 5); assertFalse("Shouldn't use DST but does", zoneInfo.useDaylightTime()); assertEquals(0, zoneInfo.getDSTSavings()); } /** - * Checks to make sure that it can handle up to 256 offsets. + * Checks to make sure that it can handle up to 256 types. */ - public void testMakeTimeZone_LotsOfOffsets() throws Exception { - int[][] times = { + public void testReadTimeZone_LotsOfTypes() throws Exception { + int[][] transitions = { { -2000, 255 }, }; - int[][] offsets = new int[256][]; - Arrays.fill(offsets, new int[2]); - offsets[255] = new int[] { 3600, 0 }; + int[][] types = new int[256][]; + Arrays.fill(types, new int[2]); + types[255] = new int[] { 3600, 0 }; - ZoneInfo zoneInfo = createZoneInfo(times, offsets, Integer.MIN_VALUE); + ZoneInfo zoneInfo = createZoneInfo(getName(), transitions, types, (long) Integer.MIN_VALUE); assertFalse("Shouldn't use DST but does", zoneInfo.useDaylightTime()); assertEquals(0, zoneInfo.getDSTSavings()); - // Make sure that WallTime works properly with a ZoneInfo with 256 offsets. + // Make sure that WallTime works properly with a ZoneInfo with 256 types. ZoneInfo.WallTime wallTime = new ZoneInfo.WallTime(); wallTime.localtime(0, zoneInfo); wallTime.mktime(zoneInfo); } - /** - * Checks to make sure that it rejects more than 256 offsets. - */ - public void testMakeTimeZone_TooManyOffsets() throws Exception { - int[][] times = { - { -2000, 255 }, - }; - int[][] offsets = new int[257][]; - Arrays.fill(offsets, new int[2]); - offsets[255] = new int[] { 3600, 0 }; - - try { - createZoneInfo(times, offsets); - fail("Did not detect too many offsets"); - } catch (IllegalStateException expected) { - // Expected this to happen - } - } - /** * Create an instance for every available time zone for which we have data to ensure that they * can all be handled correctly. @@ -352,7 +334,7 @@ public void testMakeTimeZone_TooManyOffsets() throws Exception { * to ensure that any additional checks added to the code that reads the data source and * creates the {@link ZoneInfo} instances does not prevent any of the time zones being loaded. */ - public void testMakeTimeZone_All() throws Exception { + public void testReadTimeZone_All() throws Exception { ZoneInfoDB.TzData instance = ZoneInfoDB.getInstance(); String[] availableIDs = instance.getAvailableIDs(); Arrays.sort(availableIDs); @@ -362,12 +344,166 @@ public void testMakeTimeZone_All() throws Exception { // Create a ZoneInfo at the earliest possible time to allow us to use the // useDaylightTime() method to check whether it ever has or ever will support daylight // savings time. - ZoneInfo zoneInfo = ZoneInfo.makeTimeZone(id, bufferIterator, Long.MIN_VALUE); + ZoneInfo zoneInfo = ZoneInfo.readTimeZone(id, bufferIterator, Long.MIN_VALUE); assertNotNull("TimeZone " + id + " was not created", zoneInfo); assertEquals(id, zoneInfo.getID()); } } + public void testReadTimeZone_valid() throws Exception { + ZoneInfoTestHelper.ZicDataBuilder builder = + new ZoneInfoTestHelper.ZicDataBuilder() + .initializeToValid(); + assertNotNull(createZoneInfo(getName(), System.currentTimeMillis(), builder.build())); + } + + public void testReadTimeZone_badMagic() throws Exception { + ZoneInfoTestHelper.ZicDataBuilder builder = + new ZoneInfoTestHelper.ZicDataBuilder() + .initializeToValid() + .setMagic(0xdeadbeef); // Bad magic. + try { + createZoneInfo(getName(), System.currentTimeMillis(), builder.build()); + fail(); + } catch (IOException expected) {} + } + + /** + * Checks to make sure that ZoneInfo rejects more than 256 types. + */ + public void testReadTimeZone_TooManyTypes() throws Exception { + ZoneInfoTestHelper.ZicDataBuilder builder = + new ZoneInfoTestHelper.ZicDataBuilder() + .initializeToValid() + .setTypeCountOverride(257); + byte[] bytes = builder.build(); + try { + createZoneInfo(getName(), System.currentTimeMillis(), bytes); + fail("Did not detect too many types"); + } catch (IOException expected) { + } + } + + /** + * Checks to make sure that ZoneInfo rejects more than 2000 transitions. + */ + public void testReadTimeZone_TooManyTransitions() throws Exception { + ZoneInfoTestHelper.ZicDataBuilder builder = + new ZoneInfoTestHelper.ZicDataBuilder() + .initializeToValid() + .setTransitionCountOverride(2001); + byte[] bytes = builder.build(); + try { + createZoneInfo(getName(), System.currentTimeMillis(), bytes); + fail("Did not detect too many transitions"); + } catch (IOException expected) { + } + } + + /** + * Checks to make sure that ZoneInfo rejects a negative type count. + */ + public void testReadTimeZone_NegativeTypes() throws Exception { + ZoneInfoTestHelper.ZicDataBuilder builder = + new ZoneInfoTestHelper.ZicDataBuilder() + .initializeToValid() + .setTypeCountOverride(-1); + byte[] bytes = builder.build(); + try { + createZoneInfo(getName(), System.currentTimeMillis(), bytes); + fail(); + } catch (IOException expected) { + } + } + + /** + * Checks to make sure that ZoneInfo rejects a negative transition count. + */ + public void testReadTimeZone_NegativeTransitions() throws Exception { + ZoneInfoTestHelper.ZicDataBuilder builder = + new ZoneInfoTestHelper.ZicDataBuilder() + .initializeToValid() + .setTransitionCountOverride(-1); + byte[] bytes = builder.build(); + try { + createZoneInfo(getName(), System.currentTimeMillis(), bytes); + fail(); + } catch (IOException expected) { + } + } + + public void testReadTimeZone_TransitionsNotSorted() throws Exception { + int[][] transitions = { + { 1000, 0 }, + { 3000, 1 }, // Out of transition order. + { 2000, 0 }, + }; + int[][] types = { + { 3600, 0 }, + { 1800, 1 }, + }; + + ZoneInfoTestHelper.ZicDataBuilder builder = + new ZoneInfoTestHelper.ZicDataBuilder() + .initializeToValid() + .setTransitionsAndTypes(transitions, types); + + byte[] bytes = builder.build(); + try { + createZoneInfo(getName(), System.currentTimeMillis(), bytes); + fail(); + } catch (IOException expected) { + } + } + + public void testReadTimeZone_InvalidTypeIndex() throws Exception { + int[][] transitions = { + { 1000, 0 }, + { 2000, 2 }, // Invalid type index - only 0 and 1 defined below. + { 3000, 0 }, + }; + int[][] types = { + { 3600, 0 }, + { 1800, 1 }, + }; + + ZoneInfoTestHelper.ZicDataBuilder builder = + new ZoneInfoTestHelper.ZicDataBuilder() + .initializeToValid() + .setTransitionsAndTypes(transitions, types); + + byte[] bytes = builder.build(); + try { + createZoneInfo(getName(), System.currentTimeMillis(), bytes); + fail(); + } catch (IOException expected) { + } + } + + public void testReadTimeZone_InvalidIsDst() throws Exception { + int[][] transitions = { + { 1000, 0 }, + { 2000, 1 }, + { 3000, 0 }, + }; + int[][] types = { + { 3600, 0 }, + { 1800, 2 }, // Invalid isDst - must be 0 or 1 + }; + + ZoneInfoTestHelper.ZicDataBuilder builder = + new ZoneInfoTestHelper.ZicDataBuilder() + .initializeToValid() + .setTransitionsAndTypes(transitions, types); + + byte[] bytes = builder.build(); + try { + createZoneInfo(getName(), System.currentTimeMillis(), bytes); + fail(); + } catch (IOException expected) { + } + } + /** * Checks that we can read the serialized form of a {@link ZoneInfo} created in pre-OpenJDK * AOSP. @@ -387,18 +523,18 @@ public void testReadSerialized() throws Exception { zoneInfoRead = (ZoneInfo) object; } - int[][] times = { + int[][] transitions = { { -5000, 0 }, { -2000, 1 }, { -500, 0 }, { 0, 2 }, }; - int[][] offsets = { + int[][] types = { { 3600, 0 }, { 1800, 1 }, { 5400, 0 } }; - ZoneInfo zoneInfoCreated = createZoneInfo("test", times, offsets, secondsInMillis(-1)); + ZoneInfo zoneInfoCreated = createZoneInfo("test", transitions, types, secondsInMillis(-1)); assertEquals("Read ZoneInfo does not match created one", zoneInfoCreated, zoneInfoRead); assertEquals("useDaylightTime() mismatch", @@ -411,66 +547,29 @@ private static int secondsInMillis(int seconds) { return seconds * 1000; } - private ZoneInfo createZoneInfo(int[][] transitionTimes, int[][] transitionTypes) + private ZoneInfo createZoneInfo(int[][] transitions, int[][] types) throws Exception { - return createZoneInfo(getName(), transitionTimes, transitionTypes, System.currentTimeMillis()); + return createZoneInfo(getName(), transitions, types, System.currentTimeMillis()); } - private ZoneInfo createZoneInfo(int[][] transitionTimes, int[][] transitionTypes, + private ZoneInfo createZoneInfo(int[][] transitions, int[][] types, long currentTimeMillis) throws Exception { - return createZoneInfo(getName(), transitionTimes, transitionTypes, currentTimeMillis); + return createZoneInfo(getName(), transitions, types, currentTimeMillis); } - private ZoneInfo createZoneInfo(String name, int[][] transitionTimes, int[][] transitionTypes, + private ZoneInfo createZoneInfo(String name, int[][] transitions, int[][] types, long currentTimeMillis) throws Exception { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - // Magic number. - writeInt(baos, 0x545a6966); - - // Some useless stuff in the header. - for (int i = 0; i < 28; ++i) { - baos.write(i); - } - - // Transition time count - writeInt(baos, transitionTimes.length); - // Transition type count. - writeInt(baos, transitionTypes.length); - // Useless stuff. - writeInt(baos, 0xdeadbeef); - - // Transition time array, as ints. - for (int[] transitionTime : transitionTimes) { - int transition = transitionTime[0]; - writeInt(baos, transition); - } - - // Transition type array. - for (int[] transitionTime : transitionTimes) { - byte type = (byte) transitionTime[1]; - baos.write(type); - } - - for (int i = 0; i < transitionTypes.length; i++) { - int[] transitionType = transitionTypes[i]; - int offset = transitionType[0]; - byte dst = (byte) transitionType[1]; - writeInt(baos, offset); - baos.write(dst); - - // Useless stuff. - baos.write(i); - } - - return ZoneInfo.makeTimeZone("TimeZone for '" + name + "'", - new ByteBufferIterator(ByteBuffer.wrap(baos.toByteArray())), currentTimeMillis); + ZoneInfoTestHelper.ZicDataBuilder builder = + new ZoneInfoTestHelper.ZicDataBuilder() + .setTransitionsAndTypes(transitions, types); + return createZoneInfo(name, currentTimeMillis, builder.build()); } - private static void writeInt(OutputStream os, int value) throws Exception { - byte[] bytes = ByteBuffer.allocate(4).putInt(value).array(); - os.write(bytes); + private ZoneInfo createZoneInfo(String name, long currentTimeMillis, byte[] bytes) + throws IOException { + ByteBufferIterator bufferIterator = new ByteBufferIterator(ByteBuffer.wrap(bytes)); + return ZoneInfo.readTimeZone("TimeZone for '" + name + "'", bufferIterator, currentTimeMillis); } /** @@ -494,6 +593,11 @@ public void skip(int byteCount) { buffer.position(buffer.position() + byteCount); } + @Override + public int pos() { + return buffer.position(); + } + @Override public void readByteArray(byte[] dst, int dstOffset, int byteCount) { buffer.get(dst, dstOffset, byteCount); diff --git a/luni/src/test/java/libcore/xml/ExpatSaxParserTest.java b/luni/src/test/java/libcore/xml/ExpatSaxParserTest.java index a065cb824..2a6a383a2 100644 --- a/luni/src/test/java/libcore/xml/ExpatSaxParserTest.java +++ b/luni/src/test/java/libcore/xml/ExpatSaxParserTest.java @@ -630,7 +630,7 @@ class Handler extends DefaultHandler { * A little endian UTF-16 file with an odd number of bytes. */ public void testBug28698301_1() throws Exception { - checkBug28698301("bug28698301-1.xml"); + checkBug28698301("bug28698301-1.xml", "At line 19, column 18: no element found"); } /** @@ -638,14 +638,15 @@ public void testBug28698301_1() throws Exception { * reported in the bug. */ public void testBug28698301_2() throws Exception { - checkBug28698301("bug28698301-2.xml"); + checkBug28698301("bug28698301-2.xml", "At line 3, column 18: no element found"); } /** * A big endian UTF-16 file with an odd number of bytes. */ public void testBug28698301_3() throws Exception { - checkBug28698301("bug28698301-3.xml"); + checkBug28698301("bug28698301-3.xml", + "At line 97, column 21: not well-formed (invalid token)"); } /** @@ -662,14 +663,15 @@ public void testBug28698301_3() throws Exception { * range checks used == and != rather than >= and <. The patch fixes the initial jump and then * uses inequalities in the range check to fail fast in the event of another overflow bug. */ - private void checkBug28698301(String name) throws IOException, SAXException { + private void checkBug28698301(String name, String expectedMessage) + throws IOException, SAXException { InputStream is = getClass().getResourceAsStream(name); try { parse(is, Encoding.UTF_16, new TestHandler()); } catch (SAXParseException exception) { String message = exception.getMessage(); - if (!message.contains("no element found")) { - fail("Expected 'no element found' exception, found: " + message); + if (!message.equals(expectedMessage)) { + fail("Expected '" + expectedMessage + "' exception, found: '" + message + "'"); } } } diff --git a/luni/src/test/java/libcore/xml/KxmlSerializerTest.java b/luni/src/test/java/libcore/xml/KxmlSerializerTest.java index 5f68a9943..fffb3f13a 100644 --- a/luni/src/test/java/libcore/xml/KxmlSerializerTest.java +++ b/luni/src/test/java/libcore/xml/KxmlSerializerTest.java @@ -129,14 +129,17 @@ public void testBadSurrogates() throws Exception { serializer.startTag(NAMESPACE, "tag"); try { serializer.attribute(NAMESPACE, "attr", "a\ud83d\u0040b"); + fail(); } catch (IllegalArgumentException expected) { } try { serializer.text("c\ud83d\u0040d"); + fail(); } catch (IllegalArgumentException expected) { } try { serializer.cdsect("e\ud83d\u0040f"); + fail(); } catch (IllegalArgumentException expected) { } } diff --git a/luni/src/test/java/libcore/xml/XsltXPathConformanceTestSuite.java b/luni/src/test/java/libcore/xml/XsltXPathConformanceTestSuite.java index f59f457cf..ca3c35798 100644 --- a/luni/src/test/java/libcore/xml/XsltXPathConformanceTestSuite.java +++ b/luni/src/test/java/libcore/xml/XsltXPathConformanceTestSuite.java @@ -133,7 +133,7 @@ public static void main(String[] args) throws Exception { } File catalogXml = new File(args[0]); - // TestRunner.run(suite(catalogXml)); android-changed + // TestRunner.run(suite(catalogXml)); Android-changed } public static Test suite() throws Exception { diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherOutputStream1Test.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherOutputStream1Test.java index 359ac66f5..5c88e71c0 100644 --- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherOutputStream1Test.java +++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherOutputStream1Test.java @@ -23,20 +23,27 @@ package org.apache.harmony.crypto.tests.javax.crypto; import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.OutputStream; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.security.Security; +import java.security.spec.AlgorithmParameterSpec; import java.util.Arrays; +import javax.crypto.BadPaddingException; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.NullCipher; import javax.crypto.CipherOutputStream; import javax.crypto.Cipher; +import javax.crypto.ShortBufferException; import junit.framework.TestCase; @@ -205,5 +212,121 @@ public void test_ConstructorLjava_io_OutputStreamLjavax_crypto_Cipher() throws assertNotNull(cos); } + + private static class CipherSpiThatThrowsOnSecondDoFinal extends CipherSpi { + + private boolean wasDoFinalCalled = false; + + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + + } + + @Override + protected void engineSetPadding(String padding) throws NoSuchPaddingException { + + } + + @Override + protected int engineGetBlockSize() { + return 0; + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return 0; + } + + @Override + protected byte[] engineGetIV() { + return new byte[0]; + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + } + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + return new byte[0]; + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) throws ShortBufferException { + return 0; + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + // Just call the other overriding for engineDoFinal. + try { + engineDoFinal(input, inputOffset, inputLen, new byte[10], 0); + } catch (ShortBufferException e) { + throw new RuntimeException(e); + } + return new byte[0]; + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + if (wasDoFinalCalled) { + throw new UnsupportedOperationException( + "doFinal not supposed to be called two times"); + } + wasDoFinalCalled = true; + return 0; + } + }; + + + public void test_close_doubleCloseDoesntCallDoFinal() throws Exception { + CipherSpi cipherSpiThatThrowsOnSecondDoFinal = new CipherSpiThatThrowsOnSecondDoFinal(); + Cipher cipherThatThrowsOnSecondDoFinal = new Cipher( + cipherSpiThatThrowsOnSecondDoFinal, + Security.getProviders()[0], + "SomeTransformation") { + }; + + TestOutputStream testOutputStream = new TestOutputStream(); + CipherOutputStream cipherOutputStream = new CipherOutputStream( + testOutputStream, cipherThatThrowsOnSecondDoFinal); + + cipherThatThrowsOnSecondDoFinal.init(Cipher.ENCRYPT_MODE, (Key) null); + + cipherOutputStream.close(); + // Should just check that it's already closed and return, without calling doFinal, thus + // throwing any exception + cipherOutputStream.close(); + + // Check that the spi didn't change, as it might be changed dynamically by the Cipher + // methods. + assertEquals(cipherSpiThatThrowsOnSecondDoFinal, + cipherThatThrowsOnSecondDoFinal.getCurrentSpi()); + } } diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherSpiTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherSpiTest.java index e240f6f1b..1a2ac2303 100644 --- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherSpiTest.java +++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherSpiTest.java @@ -22,6 +22,7 @@ package org.apache.harmony.crypto.tests.javax.crypto; +import java.nio.DirectByteBuffer; import java.security.spec.AlgorithmParameterSpec; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -35,6 +36,7 @@ import javax.crypto.ShortBufferException; import javax.crypto.CipherSpi; import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; @@ -298,6 +300,137 @@ public void testCipherSpi06() throws BadPaddingException, bb2.position(0); assertTrue("Incorrect result", cSpi.engineDoFinal(bb1, bb2) > 0); } + + public void testCrypt_doNotCallPositionInNonArrayBackedInputBuffer() throws Exception { + ByteBuffer nonArrayBackedInputBuffer = new MockNonArrayBackedByteBuffer(10, false); + ByteBuffer nonArrayBackedOutputBuffer = new MockNonArrayBackedByteBuffer(10, false); + Mock_CipherSpi cipherSpi = new Mock_CipherSpi() { + public int engineGetOutputSize(int inputLength) { + return inputLength; + } + }; + cipherSpi.engineUpdate(nonArrayBackedInputBuffer, nonArrayBackedOutputBuffer); + assertEquals(0, nonArrayBackedInputBuffer.position()); + } + + public void testCrypt_doNotCallPutForZeroLengthOutput() throws Exception { + ByteBuffer nonArrayBackedInputBuffer = new MockNonArrayBackedByteBuffer(10, false); + ByteBuffer nonArrayBackedOutputBuffer = new MockNonArrayBackedByteBuffer(10, false) { + @Override + public ByteBuffer put(byte[] dst, int offset, int length) { + if (length == 0) { + throw new IllegalStateException("put shouldn't be called with length 0"); + } + return this; + } + }; + + Mock_CipherSpi cipherSpi = new Mock_CipherSpi() { + public int engineUpdate( + byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { + return 0; + } + }; + + // The put method is not called in the output buffer and so the test passes. + cipherSpi.engineUpdate(nonArrayBackedInputBuffer, nonArrayBackedOutputBuffer); + } + + // In case a call to engineGetOutputSize returns 0 for the whole input size, but a positive + // value for the chunk size to be written, check that the positive output size is used in the + // second attempt to read from the the buffer. + public void testCrypt_outputSizeUpdatedAfterShortBufferException() + throws Exception { + + // 4096 is the value hardcoded for a maximum array allocation in CipherSpi#getTempArraySize + final int maxInternalArrayAllocation = 4096; + // The length of the input is greater than the max chunk allowed, so the size of the chunk + // and the size of the input will differ. + final int testInputLength = maxInternalArrayAllocation + 1; + // Length to be returned the second time engineGetOutputSize is called (that is, when it's + // called with maxInternalArrayAllocation). First length returned (that is, when it's + // called with testInputLength) is 0. + final int testSecondOutputLength = 1000; + + final AtomicInteger firstGetLength = new AtomicInteger(0); + final AtomicInteger secondGetLength = new AtomicInteger(0); + + ByteBuffer inputBuffer = new MockNonArrayBackedByteBuffer(testInputLength, false) { + private boolean getWasCalled = false; + + @Override + public ByteBuffer get(byte[] dst, int offset, int length) { + if (!getWasCalled) { + getWasCalled = true; + firstGetLength.set(length); + } else { + if (secondGetLength.get() == 0) { + secondGetLength.set(length); + } + } + return this; + } + }; + + ByteBuffer outputBuffer = new MockNonArrayBackedByteBuffer(10, false); + + Mock_CipherSpi cipherSpi = new Mock_CipherSpi() { + @Override + public int engineGetOutputSize(int inputLength) { + if (inputLength == testInputLength) { + return 0; + } else if (inputLength == maxInternalArrayAllocation) { + return testSecondOutputLength; + } else { + throw new IllegalStateException("Unexpected value " + inputLength); + } + } + + @Override + public int engineUpdate( + byte[] inArray, int inOfs, int inLen, byte[] outArray, int outputOffset) + throws ShortBufferException { + if (inLen == maxInternalArrayAllocation) { + throw new ShortBufferException("to be caught in order to retry with a new" + + "output size"); + } + return 0; + } + }; + + cipherSpi.engineUpdate(inputBuffer, outputBuffer); + + assertEquals( + "first call to get must use the input length, as the output length " + + "from engineGetOutputSize is 0", + maxInternalArrayAllocation, + firstGetLength.get()); + + assertEquals( + "second call to get must use the new output length", + testSecondOutputLength, + secondGetLength.get()); + } + + // The tests using ByteBuffer depend on final methods (like hasArray) that cannot be mocked in + // Mockito, so the mock is done manually. ByteBuffer has abstract methods that are + // package-private, so extending DirectByteBuffer. It happens to be not backed by an array, so + // we use it when we need a byte buffer not array-backed. + private class MockNonArrayBackedByteBuffer extends DirectByteBuffer { + public MockNonArrayBackedByteBuffer(int capacity, boolean isReadOnly) { + super(capacity, 0 /* addr */, null /* fd */, null /* unmapper */, isReadOnly); + } + + @Override + public ByteBuffer get(byte[] dst, int offset, int length) { + return this; + } + + @Override + public ByteBuffer put(byte[] dst, int offset, int length) { + return this; + } + } } /** * diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherTest.java index b4da1b8db..e893670f8 100644 --- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherTest.java +++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherTest.java @@ -561,6 +561,7 @@ public void testGetMaxAllowedKeyLength() throws Exception { } try { Cipher.getMaxAllowedKeyLength(""); + fail(); } catch (NoSuchAlgorithmException expected) { } try { diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/interfaces/DHPrivateKeyTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/interfaces/DHPrivateKeyTest.java index eedb14c5a..2159e785c 100644 --- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/interfaces/DHPrivateKeyTest.java +++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/interfaces/DHPrivateKeyTest.java @@ -25,7 +25,9 @@ import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import javax.crypto.KeyGenerator; import javax.crypto.interfaces.DHKey; import javax.crypto.interfaces.DHPrivateKey; import javax.crypto.spec.DHParameterSpec; @@ -48,9 +50,34 @@ public void testField() { 2211791113380396553L); } - public void test_getParams() throws Exception { + public void test_getParams_initToHardCoded() throws Exception { + // (p, g) values from RFC 7919, Appendix A (2048-bit group) + String pStr = "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" + + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" + + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" + + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" + + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" + + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + + "886B423861285C97FFFFFFFFFFFFFFFF"; + BigInteger p = new BigInteger(new BigInteger(pStr, 16).toByteArray()); + BigInteger g = BigInteger.valueOf(2); KeyPairGenerator kg = KeyPairGenerator.getInstance("DH"); + kg.initialize(new DHParameterSpec(p, g), new SecureRandom()); + check_getParams(kg); + } + + public void test_getParams_initToRandom192bit() throws Exception { + KeyPairGenerator kg = KeyPairGenerator.getInstance("DH"); + // DH group generation is slow, so we test with a small (insecure) value kg.initialize(192); + check_getParams(kg); + } + + private static void check_getParams(KeyPairGenerator kg) throws Exception { KeyPair kp1 = kg.genKeyPair(); KeyPair kp2 = kg.genKeyPair(); DHPrivateKey pk1 = (DHPrivateKey) kp1.getPrivate(); diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/interfaces/DHPublicKeyTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/interfaces/DHPublicKeyTest.java index bc7b3386c..392046600 100644 --- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/interfaces/DHPublicKeyTest.java +++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/interfaces/DHPublicKeyTest.java @@ -27,6 +27,7 @@ import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.SecureRandom; import javax.crypto.interfaces.DHKey; import javax.crypto.interfaces.DHPublicKey; @@ -49,9 +50,34 @@ public void testField() { -6628103563352519193L); } - public void test_getParams() throws Exception { + public void test_getParams_initToHardCoded() throws Exception { + // (p, g) values from RFC 7919, Appendix A (2048-bit group) + String pStr = "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" + + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" + + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" + + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" + + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" + + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + + "886B423861285C97FFFFFFFFFFFFFFFF"; + BigInteger p = new BigInteger(new BigInteger(pStr, 16).toByteArray()); + BigInteger g = BigInteger.valueOf(2); KeyPairGenerator kg = KeyPairGenerator.getInstance("DH"); - kg.initialize(1024); + kg.initialize(new DHParameterSpec(p, g), new SecureRandom()); + check_getParams(kg); + } + + public void test_getParams_initToRandom192bit() throws Exception { + KeyPairGenerator kg = KeyPairGenerator.getInstance("DH"); + // DH group generation is slow, so we test with a small (insecure) value + kg.initialize(192); + check_getParams(kg); + } + + private void check_getParams(KeyPairGenerator kg) { KeyPair kp1 = kg.genKeyPair(); KeyPair kp2 = kg.genKeyPair(); DHPublicKey pk1 = (DHPublicKey) kp1.getPublic(); diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/spec/PBEParameterSpecTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/spec/PBEParameterSpecTest.java index 66390992f..ae1a25f32 100644 --- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/spec/PBEParameterSpecTest.java +++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/spec/PBEParameterSpecTest.java @@ -23,7 +23,8 @@ package org.apache.harmony.crypto.tests.javax.crypto.spec; import java.util.Arrays; - +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEParameterSpec; import junit.framework.Test; @@ -90,6 +91,29 @@ public void testGetIterationCount() { pbeps.getIterationCount() == iterationCount); } + /** + * getAlgorithmParameterSpec() method testing. Tests that returned value is equal + * to the value specified in the constructor. + */ + public void testGetAlgorithmParameterSpec() { + byte[] salt = new byte[] {1, 2, 3, 4, 5}; + int iterationCount = 10; + + // Check that the constructor works with a null AlgorithmParameterSpec and it's correctly + // returned in the getter. + PBEParameterSpec pbeps = new PBEParameterSpec(salt, iterationCount, null); + assertNull("The returned AlgorithmParameterSpec is not null, as the specified " + + "in the constructor.", + pbeps.getParameterSpec()); + + // Check that a non-null AlgorithmParameterSpec is returned correctly. + AlgorithmParameterSpec aps = new IvParameterSpec(new byte[16]); + pbeps = new PBEParameterSpec(salt, iterationCount, aps); + assertSame("The returned AlgorithmParameterSpec is not the same as the specified " + + "in the constructor.", + aps, pbeps.getParameterSpec()); + } + public static Test suite() { return new TestSuite(PBEParameterSpecTest.class); } diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java index a76886336..37b561c6e 100644 --- a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java +++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java @@ -16,7 +16,7 @@ package org.apache.harmony.luni.tests.java.net; -import junit.framework.TestCase; +import libcore.junit.util.ResourceLeakageDetector.DisableResourceLeakageDetection; import tests.support.Support_Configuration; import tests.support.Support_TestWebData; import tests.support.Support_TestWebServer; @@ -27,29 +27,21 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FilePermission; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.net.CacheRequest; -import java.net.CacheResponse; import java.net.FileNameMap; import java.net.HttpURLConnection; import java.net.JarURLConnection; import java.net.MalformedURLException; -import java.net.ResponseCache; -import java.net.SocketPermission; import java.net.SocketTimeoutException; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.UnknownServiceException; -import java.security.Permission; import java.text.ParseException; import java.util.Arrays; import java.util.Calendar; @@ -58,8 +50,14 @@ import java.util.List; import java.util.Map; import java.util.TimeZone; +import libcore.junit.junit3.TestCaseWithRules; +import libcore.junit.util.ResourceLeakageDetector; +import org.junit.Rule; +import org.junit.rules.TestRule; -public class URLConnectionTest extends TestCase { +public class URLConnectionTest extends TestCaseWithRules { + //@Rule + //public TestRule guardRule = ResourceLeakageDetector.getRule(); private static final String testString = "Hello World"; @@ -69,8 +67,6 @@ public class URLConnectionTest extends TestCase { private JarURLConnection jarURLCon; - private URLConnection gifURLCon; - /** * {@link java.net.URLConnection#addRequestProperty(String, String)} */ @@ -235,7 +231,6 @@ public void setUp() throws Exception { fileURLCon = fileURL.openConnection(); jarURLCon = openJarURLConnection(); - gifURLCon = openGifURLConnection(); } @Override @@ -296,11 +291,11 @@ public void testHttpPostHeaders() throws IOException { // post a request connection.setDoOutput(true); - OutputStreamWriter writer - = new OutputStreamWriter(connection.getOutputStream()); + OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); writer.write("hello"); writer.flush(); assertEquals(200, connection.getResponseCode()); + connection.disconnect(); // validate the request by asking the server what was received Map headers = server.pathToRequest().get(path).getHeaders(); @@ -372,10 +367,11 @@ public void test_getContent() throws IOException { URL url = new URL("http://a/b/c/?y"); URLConnection fakeCon = url.openConnection(); try { - fakeCon.getContent(); + fakeCon.getContent(); } catch (IOException e) { //ok } + ((HttpURLConnection) fakeCon).disconnect(); ((HttpURLConnection) uc).disconnect(); try { @@ -445,12 +441,26 @@ public void test_getContentLength() throws Exception { assertEquals(Support_TestWebData.test1.length, uc.getContentLength()); assertEquals(Support_TestWebData.test2.length, uc2.getContentLength()); - assertTrue(jarURLCon.getContentLength() > 0); + URLConnection gifURLCon = openGifURLConnection(); assertTrue(gifURLCon.getContentLength() > 0); - + gifURLCon.getInputStream().close(); fileURLCon.getInputStream().close(); } + /** + * {@link java.net.URLConnection#getContentLength()} + */ + @DisableResourceLeakageDetection( + why = "URLConnection has no mechanism for releasing resources owned by the connection." + + " HttpURLConnection provides a disconnect() method but " + + " JarURLConnection does not and does not provide access to the underlying" + + " URLConnection that it uses to access the JAR.", + bug = "bad API design" + ) + public void test_getContentLength_leaky() throws Exception { + assertTrue(jarURLCon.getContentLength() > 0); + } + public void test_getContentType() throws Exception { assertTrue("getContentType failed: " + fileURLCon.getContentType(), fileURLCon.getContentType().contains("text/plain")); @@ -530,7 +540,8 @@ public void test_getDoInput() throws IOException { uc2.connect(); try { uc2.getInputStream(); - } catch (Throwable expected) { + fail(); + } catch (IOException expected) { } } @@ -1144,20 +1155,38 @@ public void test_setReadTimeoutI() throws Exception { // correct } assertEquals(100, uc.getReadTimeout()); + } + + public void test_setReadTimeoutI_SocketTimeoutException() throws Exception { + // Create another Support_TestWebServer but with a time delay, so that we can ensure that + // subsequent read() of shorter timeout period will definitely fail. + Support_TestWebServer localServer = new Support_TestWebServer(); + localServer.setDelay(10 /* responseDelayMillis */); + final int localPort = localServer.initServer(); + + // "/test2" will cause the server to return an 8k binary file but only after 10ms delay. + URLConnection uc = new URL("http", "localhost", localPort, "test2").openConnection(); byte[] ba = new byte[600]; - uc2.setReadTimeout(5); - uc2.setDoInput(true); - uc2.connect(); + uc.setReadTimeout(1); + uc.setDoInput(true); + uc.connect(); try { - ((InputStream) uc2.getInputStream()).read(ba, 0, 600); + // Either of the getInputStream() or the read(...) call can time out. + try (InputStream inputStream = uc.getInputStream()) { + inputStream.read(ba, 0, 600); + } + fail("SocketTimeoutException expected"); } catch (SocketTimeoutException e) { //ok - } catch ( UnknownServiceException e) { - fail(""+e.getMessage()); + } catch (UnknownServiceException e) { + fail("" + e.getMessage()); } + + localServer.close(); + ((HttpURLConnection) uc).disconnect(); } /** diff --git a/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java b/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java index 41b96433a..96de6c816 100644 --- a/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java +++ b/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java @@ -37,11 +37,11 @@ public class PatternTest extends TestCase { "(a|b)*(a|b)*A(a|b)*lice.*", "(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z)(a|b|c|d|e|f|g|h|" + "i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z)*(1|2|3|4|5|6|7|8|9|0)*|while|for|struct|if|do", -// BEGIN android-changed +// BEGIN Android-changed // We don't have canonical equivalence. // "x(?c)y", "x(?cc)y" // "x(?:c)y" -// END android-changed +// END Android-changed }; @@ -336,7 +336,7 @@ public void testFlags() { assertFalse(mat.matches()); } -// BEGIN android-removed +// BEGIN Android-removed // The flags() method should only return those flags that were explicitly // passed during the compilation. The JDK also accepts the ones implicitly // contained in the pattern, but ICU doesn't do this. @@ -379,7 +379,7 @@ public void testFlags() { // pat = Pattern.compile("(?is)abc"); // assertEquals(pat.flags(), Pattern.CASE_INSENSITIVE | Pattern.DOTALL); // } -//END android-removed +//END Android-removed /* * Check default flags when they are not specified in pattern. Based on RI @@ -482,13 +482,13 @@ public void testQuantCompileNeg() { } } // Regression for HARMONY-1365 -// BEGIN android-changed +// BEGIN Android-changed // Original regex contained some illegal stuff. Changed it slightly, // while maintaining the wicked character of this "mother of all // regexes". // String pattern = "(?![^\\\\G*?)(?![^|\\]\\070\\ne\\{\\t\\[\\053\\?\\\\\\x51\\a\\075\\0023-\\[&&[|\\022-\\xEA\\00-\\u41C2&&[^|a-\\xCC&&[^\\037\\uECB3\\u3D9A\\x31\\|\\[^\\016\\r\\{\\,\\uA29D\\034\\02[\\02-\\[|\\t\\056\\uF599\\x62\\e\\<\\032\\uF0AC\\0026\\0205Q\\|\\\\\\06\\0164[|\\057-\\u7A98&&[\\061-g|\\|\\0276\\n\\042\\011\\e\\xE8\\x64B\\04\\u6D0EDW^\\p{Lower}]]]]?)(?<=[^\\n\\\\\\t\\u8E13\\,\\0114\\u656E\\xA5\\]&&[\\03-\\026|\\uF39D\\01\\{i\\u3BC2\\u14FE]])(?<=[^|\\uAE62\\054H\\|\\}&&^\\p{Space}])(?sxx)(?<=[\\f\\006\\a\\r\\xB4]*+)|(?x-xd:^{5}+)()"; String pattern = "(?![^\\\\.*?)(?![^|\\]\\070\\ne\\{\\t\\[\\053\\?\\\\\\x51\\a\\075\\0023-\\[&&[|\\022-\\xEA\\00-\\u41C2&&[^|a-\\xCC&&[^\\037\\uECB3\\u3D9A\\x31\\|\\[^\\016\\r\\{\\,\\uA29D\\034\\02[\\02-\\[|\\t\\056\\uF599\\x62\\e\\<\\032\\uF0AC\\0026\\0205Q\\|\\\\\\06\\0164[|\\057-\\u7A98&&[\\061-g|\\|\\0276\\n\\042\\011\\e\\xE8\\x64B\\04\\u6D0EDW^\\p{Lower}]]]]?)(?<=[^\\n\\\\\\t\\u8E13\\,\\0114\\u656E\\xA5\\]&&[\\03-\\026|\\uF39D\\01\\{i\\u3BC2\\u14FE]])(?<=[^|\\uAE62\\054H\\|\\}&&^\\p{Space}])(?sxx)(?<=[\\f\\006\\a\\r\\xB4]{1,5})|(?x-xd:^{5}+)()"; -// END android-changed +// END Android-changed assertNotNull(Pattern.compile(pattern)); } @@ -560,7 +560,7 @@ public void testTimeZoneIssue() { assertEquals("45", m.group(4)); } -// BEGIN android-changed +// BEGIN Android-changed // Removed one pattern that is buggy on the JDK. We don't want to duplicate that. public void testCompileRanges() { String[] correctTestPatterns = { "[^]*abb]*", /* "[^a-d[^m-p]]*abb", */ @@ -616,7 +616,7 @@ public void testRangesSpecialCases() { pat, inp)); } } - // END android-changed + // END Android-changed public void testZeroSymbols() { assertTrue(Pattern.matches("[\0]*abb", "\0\0\0\0\0\0abb")); @@ -891,14 +891,14 @@ public void testNonCaptConstr() { pat = Pattern.compile("(?>aa|a)aabb"); assertFalse(pat.matcher("aaabb").matches()); -// BEGIN android-removed +// BEGIN Android-removed // Questionable constructs that ICU doesn't support. // // quantifiers over look ahead // pat = Pattern.compile(".*(?<=abc)*\\.log$"); // assertTrue(pat.matcher("cde.log").matches()); // pat = Pattern.compile(".*(?<=abc)+\\.log$"); // assertFalse(pat.matcher("cde.log").matches()); -// END android-removed +// END Android-removed } @@ -1208,12 +1208,12 @@ public void testSequencesWithSurrogatesSupplementary() { String testString = "abcd\uD8D3\uDFFC"; Pattern pat = Pattern.compile(patString); Matcher mat = pat.matcher(testString); -// BEGIN android-changed +// BEGIN Android-changed // This one really doesn't make sense, as the above is a corrupt surrogate. // Even if it's matched by the JDK, it's more of a bug than of a behavior one // might want to duplicate. // assertFalse(mat.find()); -// END android-changed +// END Android-changed testString = "abcd\uD8D3abc"; mat = pat.matcher(testString); diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/DigestOutputStreamTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/DigestOutputStreamTest.java index d90c8ecb3..2fc09c677 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/DigestOutputStreamTest.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/DigestOutputStreamTest.java @@ -594,6 +594,67 @@ public void test_writeI() throws Exception { Arrays.equals(digestResult, expected)); } + private class MessageDigestWithUnsupportedUpdate extends MessageDigest { + private MessageDigestWithUnsupportedUpdate() { + super("SomeAlgorithm"); + } + + @Override + protected void engineUpdate(byte input) { + throw new UnsupportedOperationException(); + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + throw new UnsupportedOperationException(); + } + + @Override + protected byte[] engineDigest() { + return new byte[0]; + } + + @Override + protected void engineReset() { + + } + } + + public void test_write_writeToUnderlyingStreamBeforeUpdatingDigest() { + MessageDigest messageDigestWithUnsupportedUpdate = new MessageDigestWithUnsupportedUpdate(); + OutputStream outputStreamThatThrowsIOException = new OutputStream() { + @Override + public void write(int b) throws IOException { + throw new IOException(); + } + }; + + DigestOutputStream digestOutputStream = new DigestOutputStream( + outputStreamThatThrowsIOException, messageDigestWithUnsupportedUpdate); + + // Writing throws an IOException (and not an UnsupportedOperationException) meaning than + // it tried to write to the underlying stream before updating the digest. + digestOutputStream.on(true); + try { + digestOutputStream.write(3); + fail(); + } catch (IOException expected) { + } + + digestOutputStream.on(true); + try { + digestOutputStream.write(new byte[10], 0, 10); + fail(); + } catch (IOException expected) { + } + + digestOutputStream.on(true); + try { + digestOutputStream.write(new byte[10]); + fail(); + } catch (IOException expected) { + } + } private class MyOutputStream extends OutputStream { @Override diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSPasswordProtectionTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSPasswordProtectionTest.java index af811b050..c7ed46bd2 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSPasswordProtectionTest.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSPasswordProtectionTest.java @@ -23,7 +23,9 @@ package org.apache.harmony.security.tests.java.security; import java.security.KeyStore; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.spec.IvParameterSpec; import javax.security.auth.DestroyFailedException; import junit.framework.TestCase; @@ -68,4 +70,49 @@ public void testGetPassword() throws DestroyFailedException { fail("Unexpected exception for NULL parameter"); } } + + /** + * Test for KeyStore.PasswordProtection(char[] password, String protectionAlgorithm, + * AlgorithmParameterSpec protectionParameters) constructor + * and the method getProtectionAlgorithm() + + * Assertions: constructor throws NullPointerException if protectionAlgorithm is null. + * getProtectionAlgorithm() returns the protection algorithm passed in the constructor. + */ + public void testGetProtectionAlgorithm() throws DestroyFailedException { + char [] pass = {'a', 'b', 'c'}; + String protectionAlgorithm = "ThisBeautifulAlgorithm"; + AlgorithmParameterSpec protectionParameters = new IvParameterSpec(new byte[]{}); + KeyStore.PasswordProtection ksPWP; + try { + ksPWP = new KeyStore.PasswordProtection( + pass, null /* protectionAlgorithm */, protectionParameters); + fail("Expected null pointer exception"); + } catch (NullPointerException expected) { + } + ksPWP = new KeyStore.PasswordProtection( + pass, protectionAlgorithm, null /* protectionParameters */); + assertSame(protectionAlgorithm, ksPWP.getProtectionAlgorithm()); + } + + /** + * Test for KeyStore.PasswordProtection(char[] password, String protectionAlgorithm, + * AlgorithmParameterSpec protectionParameters) constructor + * and the method getProtectionParameters() + + * Assertions: constructor creates new PasswordProtection object, even if protectionParameters + * is null. getProtectionParameterrs() returns the protection algorithm passed in the + * constructor. + */ + public void testGetProtectionParameters() throws DestroyFailedException { + char [] pass = {'a', 'b', 'c'}; + AlgorithmParameterSpec protectionParameters = new IvParameterSpec(new byte[]{}); + KeyStore.PasswordProtection ksPWP = + new KeyStore.PasswordProtection( + pass, "protectionAlgorithm", null /* protectionParameters */); + assertNull(ksPWP.getProtectionParameters()); + ksPWP = new KeyStore.PasswordProtection( + pass, "protectionAlgorithm", protectionParameters); + assertSame(protectionParameters, ksPWP.getProtectionParameters()); + } } diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSPrivateKeyEntryTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSPrivateKeyEntryTest.java index 53956ef17..90eb2795a 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSPrivateKeyEntryTest.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSPrivateKeyEntryTest.java @@ -25,6 +25,9 @@ import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.Certificate; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import org.apache.harmony.security.tests.support.cert.MyCertificate; @@ -120,6 +123,22 @@ public void testPrivateKeyEntry04() { } } + /** + * Test for + * PrivateKeyEntry( + * PrivateKey privateKey, Certificate[] chain, Set attributes) + * constructor + * Assertion: throws NullPointerException when attributes is null + */ + public void testPrivateKeyEntry05() { + createParams(false, true); + try { + new KeyStore.PrivateKeyEntry(testPrivateKey, testChain, null /* attributes */); + fail("NullPointerException must be thrown when attributes is null"); + } catch (NullPointerException expected) { + } + } + /** * Test for getPrivateKey() method * Assertion: returns PrivateKey object @@ -160,6 +179,41 @@ public void testGetCertificate() { assertEquals("Incorrect end certificate (number 0)", testChain[0], res); } + /** + * Test for getAttributes() method + * Assertion: returns attributes specified in the constructor, as an unmodifiable set. + */ + public void testGetAttributes() { + createParams(false, false); + final String attributeName = "theAttributeName"; + KeyStore.Entry.Attribute myAttribute = new KeyStore.Entry.Attribute() { + @Override + public String getName() { + return attributeName; + } + + @Override + public String getValue() { + return null; + } + }; + Set attributeSet = new HashSet(); + attributeSet.add(myAttribute); + + KeyStore.PrivateKeyEntry ksPKE = new KeyStore.PrivateKeyEntry( + testPrivateKey, testChain, attributeSet); + Set returnedAttributeSet = ksPKE.getAttributes(); + assertEquals(attributeSet, returnedAttributeSet); + // Adding an element to the original set is OK. + attributeSet.add(myAttribute); + // The returned set is unmodifiabled. + try { + returnedAttributeSet.add(myAttribute); + fail("The returned set of attributed should be unmodifiable"); + } catch (UnsupportedOperationException expected) { + } + } + /** * Test for toString() method * Assertion: returns non null String diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSSecretKeyEntryTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSSecretKeyEntryTest.java index f85593e06..3a9e80838 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSSecretKeyEntryTest.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSSecretKeyEntryTest.java @@ -23,6 +23,8 @@ package org.apache.harmony.security.tests.java.security; import java.security.KeyStore; +import java.util.HashSet; +import java.util.Set; import javax.crypto.SecretKey; @@ -57,6 +59,21 @@ public void testSecretKeyEntry() { } } + /** + * Test for + * SecretKeyEntry(SecretKey secretKey, Set attribute) + * constructor + * Assertion: throws NullPointerException when attributes is null + */ + public void testSecretKeyEntry_nullAttributes() { + SecretKey sk = new tmpSecretKey(); + try { + new KeyStore.SecretKeyEntry(sk, null /* attributes */); + fail("NullPointerException must be thrown when attributes is null"); + } catch(NullPointerException expected) { + } + } + /** * Test for getSecretKey() method * Assertion: returns SecretKey from the given entry @@ -67,6 +84,40 @@ public void testGetSecretKey() { assertEquals("Incorrect SecretKey", sk, ske.getSecretKey()); } + /** + * Test for getAttributes() method + * Assertion: returns the attributes specified in the constructor, as an unmodifiable set + */ + public void testGetAttributes() { + SecretKey sk = new tmpSecretKey(); + final String attributeName = "theAttributeName"; + KeyStore.Entry.Attribute myAttribute = new KeyStore.Entry.Attribute() { + @Override + public String getName() { + return attributeName; + } + + @Override + public String getValue() { + return null; + } + }; + Set attributeSet = new HashSet(); + attributeSet.add(myAttribute); + + KeyStore.SecretKeyEntry ksSKE = new KeyStore.SecretKeyEntry(sk, attributeSet); + Set returnedAttributeSet = ksSKE.getAttributes(); + assertEquals(attributeSet, returnedAttributeSet); + // Adding an element to the original set is OK. + attributeSet.add(myAttribute); + // The returned set is unmodifiabled. + try { + returnedAttributeSet.add(myAttribute); + fail("The returned set of attributed should be unmodifiable"); + } catch (UnsupportedOperationException expected) { + } + } + /** * Test for toString() method * Assertion: returns non null string diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSTrustedCertificateEntryTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSTrustedCertificateEntryTest.java index 535054246..bb427b667 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSTrustedCertificateEntryTest.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KSTrustedCertificateEntryTest.java @@ -24,6 +24,8 @@ import java.security.KeyStore; import java.security.cert.Certificate; +import java.util.HashSet; +import java.util.Set; import org.apache.harmony.security.tests.support.cert.MyCertificate; @@ -60,6 +62,20 @@ public void testTrustedCertificateEntry() { } } + /** + * Test for SecretKeyEntry(SecretKey secretKey, Set attributes) + * constructor + * Assertion: throws NullPointerException when attributes is null + */ + public void testSecretKeyEntry_nullAttributes() { + Certificate cert = new MyCertificate("TEST", new byte[10]); + try { + new KeyStore.TrustedCertificateEntry(cert, null /* attributes */); + fail("NullPointerException must be thrown when attributes is null"); + } catch(NullPointerException expected) { + } + } + /** * Test for getTrustedCertificate() method * Assertion: returns trusted Certificate from goven entry @@ -71,6 +87,41 @@ public void testGetTrustedCertificate() { assertEquals("Incorrect certificate", cert, ksTCE.getTrustedCertificate()); } + /** + * Test for getAttributes() method + * Assertion: returns the attributes specified in the constructor, as an unmodifiable set + */ + public void testGetAttributes() { + Certificate cert = new MyCertificate("TEST", new byte[10]); + final String attributeName = "theAttributeName"; + KeyStore.Entry.Attribute myAttribute = new KeyStore.Entry.Attribute() { + @Override + public String getName() { + return attributeName; + } + + @Override + public String getValue() { + return null; + } + }; + Set attributeSet = new HashSet(); + attributeSet.add(myAttribute); + + KeyStore.TrustedCertificateEntry ksTCE = + new KeyStore.TrustedCertificateEntry(cert, attributeSet); + Set returnedAttributeSet = ksTCE.getAttributes(); + assertEquals(attributeSet, returnedAttributeSet); + // Adding an element to the original set is OK. + attributeSet.add(myAttribute); + // The returned set is unmodifiabled. + try { + returnedAttributeSet.add(myAttribute); + fail("The returned set of attributed should be unmodifiable"); + } catch (UnsupportedOperationException expected) { + } + } + /** * Test for toString() method * Assertion: returns non null string diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore2Test.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore2Test.java index d30390399..dd325b1c6 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore2Test.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore2Test.java @@ -892,6 +892,7 @@ public void test_entryInstanceOf() throws Exception { try { keyStore.entryInstanceOf(null, KeyStore.SecretKeyEntry.class); + fail(); } catch (NullPointerException expected) { } @@ -924,6 +925,7 @@ public void test_store_java_io_OutputStream_char() throws Exception { try { keyStore.store(new ByteArrayOutputStream(), "pwd".toCharArray()); + fail(); } catch (KeyStoreException expected) { } diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore3Test.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore3Test.java index 77bf62fab..d0e299546 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore3Test.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore3Test.java @@ -48,32 +48,6 @@ public class KeyStore3Test extends TestCase { private Certificate certificate; - public KeyStore3Test() throws Exception { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA"); - keyPair = keyPairGenerator.generateKeyPair(); - - String certificateData = "-----BEGIN CERTIFICATE-----\n" - + "MIICZTCCAdICBQL3AAC2MA0GCSqGSIb3DQEBAgUAMF8xCzAJBgNVBAYTAlVTMSAw\n" - + "HgYDVQQKExdSU0EgRGF0YSBTZWN1cml0eSwgSW5jLjEuMCwGA1UECxMlU2VjdXJl\n" - + "IFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05NzAyMjAwMDAwMDBa\n" - + "Fw05ODAyMjAyMzU5NTlaMIGWMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv\n" - + "cm5pYTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQKExZTdW4gTWljcm9zeXN0\n" - + "ZW1zLCBJbmMuMSEwHwYDVQQLExhUZXN0IGFuZCBFdmFsdWF0aW9uIE9ubHkxGjAY\n" - + "BgNVBAMTEWFyZ29uLmVuZy5zdW4uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB\n" - + "iQKBgQCofmdY+PiUWN01FOzEewf+GaG+lFf132UpzATmYJkA4AEA/juW7jSi+LJk\n" - + "wJKi5GO4RyZoyimAL/5yIWDV6l1KlvxyKslr0REhMBaD/3Z3EsLTTEf5gVrQS6sT\n" - + "WMoSZAyzB39kFfsB6oUXNtV8+UKKxSxKbxvhQn267PeCz5VX2QIDAQABMA0GCSqG\n" - + "SIb3DQEBAgUAA34AXl3at6luiV/7I9MN5CXYoPJYI8Bcdc1hBagJvTMcmlqL2uOZ\n" - + "H9T5hNMEL9Tk6aI7yZPXcw/xI2K6pOR/FrMp0UwJmdxX7ljV6ZtUZf7pY492UqwC\n" - + "1777XQ9UEZyrKJvF5ntleeO0ayBqLGVKCWzWZX9YsXCpv47FNLZbupE=\n" - + "-----END CERTIFICATE-----\n"; - - ByteArrayInputStream certArray = new ByteArrayInputStream( - certificateData.getBytes()); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - certificate = cf.generateCertificate(certArray); - } - public void test_load() throws Exception { // No exception should be thrown out. mockKeyStore.load(null); @@ -168,6 +142,29 @@ public void test_KeyStore() { protected void setUp() throws Exception { super.setUp(); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA"); + keyPair = keyPairGenerator.generateKeyPair(); + + String certificateData = "-----BEGIN CERTIFICATE-----\n" + + "MIICZTCCAdICBQL3AAC2MA0GCSqGSIb3DQEBAgUAMF8xCzAJBgNVBAYTAlVTMSAw\n" + + "HgYDVQQKExdSU0EgRGF0YSBTZWN1cml0eSwgSW5jLjEuMCwGA1UECxMlU2VjdXJl\n" + + "IFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05NzAyMjAwMDAwMDBa\n" + + "Fw05ODAyMjAyMzU5NTlaMIGWMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv\n" + + "cm5pYTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQKExZTdW4gTWljcm9zeXN0\n" + + "ZW1zLCBJbmMuMSEwHwYDVQQLExhUZXN0IGFuZCBFdmFsdWF0aW9uIE9ubHkxGjAY\n" + + "BgNVBAMTEWFyZ29uLmVuZy5zdW4uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB\n" + + "iQKBgQCofmdY+PiUWN01FOzEewf+GaG+lFf132UpzATmYJkA4AEA/juW7jSi+LJk\n" + + "wJKi5GO4RyZoyimAL/5yIWDV6l1KlvxyKslr0REhMBaD/3Z3EsLTTEf5gVrQS6sT\n" + + "WMoSZAyzB39kFfsB6oUXNtV8+UKKxSxKbxvhQn267PeCz5VX2QIDAQABMA0GCSqG\n" + + "SIb3DQEBAgUAA34AXl3at6luiV/7I9MN5CXYoPJYI8Bcdc1hBagJvTMcmlqL2uOZ\n" + + "H9T5hNMEL9Tk6aI7yZPXcw/xI2K6pOR/FrMp0UwJmdxX7ljV6ZtUZf7pY492UqwC\n" + + "1777XQ9UEZyrKJvF5ntleeO0ayBqLGVKCWzWZX9YsXCpv47FNLZbupE=\n" + + "-----END CERTIFICATE-----\n"; + ByteArrayInputStream certArray = new ByteArrayInputStream( + certificateData.getBytes()); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + certificate = cf.generateCertificate(certArray); mockKeyStore = new MyKeyStore(new MyKeyStoreSpi(), null, "MyKeyStore"); } diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStoreSpiTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStoreSpiTest.java index a85459bb6..be3e6d186 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStoreSpiTest.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStoreSpiTest.java @@ -77,18 +77,12 @@ public void test_engineEntryInstanceOf() throws Exception { KeyStore.TrustedCertificateEntry.class)); try { - assertFalse(ksSpi.engineEntryInstanceOf(null, - KeyStore.TrustedCertificateEntry.class)); + ksSpi.engineEntryInstanceOf(null, KeyStore.TrustedCertificateEntry.class); + fail(); } catch (NullPointerException expected) { } - try { - assertFalse(ksSpi.engineEntryInstanceOf( - "test_engineEntryInstanceOf_Alias1", null)); - } catch (NullPointerException expected) { - } - - + assertFalse(ksSpi.engineEntryInstanceOf("test_engineEntryInstanceOf_Alias1", null)); } public void testKeyStoreSpi01() throws IOException, @@ -111,6 +105,7 @@ public void testKeyStoreSpi01() throws IOException, try { ksSpi.engineStore(null); + fail(); } catch (UnsupportedOperationException expected) { } assertNull("Not null entry", ksSpi.engineGetEntry("aaa", null)); diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/ProviderTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/ProviderTest.java index 4462f8138..1b83db300 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/ProviderTest.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/ProviderTest.java @@ -372,26 +372,26 @@ class MyProvider extends Provider { super(name, version, info); } - // BEGIN android-added + // BEGIN Android-added public void putService(Provider.Service s) { super.putService(s); } - // END android-added + // END Android-added - // BEGIN android-added + // BEGIN Android-added public void removeService(Provider.Service s) { super.removeService(s); } - // END android-added + // END Android-added - // BEGIN android-added + // BEGIN Android-added public int getNumServices() { return getServices().size(); } - // END android-added + // END Android-added } - // BEGIN android-added + // BEGIN Android-added public final void testService2() { Provider[] pp = Security.getProviders("MessageDigest.ASH-1"); if (pp == null) { @@ -410,9 +410,9 @@ public final void testService2() { } catch (NoSuchAlgorithmException e) { } } - // END android-added + // END Android-added - // BEGIN android-added + // BEGIN Android-added public final void testGetServices() { MyProvider myProvider = new MyProvider(null, 1, null); Set services = myProvider.getServices(); @@ -450,9 +450,9 @@ public final void testGetServices() { assertTrue(!actual.contains(s[1])); assertTrue(actual.contains(s[2])); } - // END android-added + // END Android-added - // BEGIN android-added + // BEGIN Android-added public final void testPutService() { MyProvider myProvider = new MyProvider(null, 1, null); Provider.Service s[] = new Provider.Service[3]; @@ -501,9 +501,9 @@ public final void testPutService() { // expected } } - // END android-added + // END Android-added - // BEGIN android-added + // BEGIN Android-added public final void testRemoveService() { MyProvider myProvider = new MyProvider(null, 1, null); try { @@ -569,9 +569,9 @@ public final void testRemoveService() { // expected } } - // END android-added + // END Android-added - // BEGIN android-added + // BEGIN Android-added public final void testLoad() throws IOException { InputStream is = new ByteArrayInputStream(writeProperties()); MyProvider myProvider = new MyProvider("name", 1, "info"); @@ -594,9 +594,9 @@ public final void testLoad() throws IOException { // expected } } - // END android-added + // END Android-added - // BEGIN android-added + // BEGIN Android-added public final void testLoad2() { class TestInputStream extends InputStream { @Override @@ -613,9 +613,9 @@ public int read() throws IOException { // expected } } - // END android-added + // END Android-added - // BEGIN android-added + // BEGIN Android-added protected byte[] writeProperties() { ByteArrayOutputStream bout = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(bout); @@ -625,9 +625,9 @@ protected byte[] writeProperties() { ps.close(); return bout.toByteArray(); } - // END android-added + // END Android-added - // BEGIN android-added + // BEGIN Android-added static class TestSecurityManager extends SecurityManager { boolean called = false; private final String permissionName; @@ -645,5 +645,5 @@ public void checkPermission(Permission permission) { } } } - // END android-added + // END Android-added } diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/SignatureTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/SignatureTest.java index d00e18ab8..b023bb018 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/SignatureTest.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/SignatureTest.java @@ -36,6 +36,7 @@ import java.security.PublicKey; import java.security.SecureRandom; import java.security.SignatureException; +import java.security.SignatureSpi; import java.security.cert.Certificate; import java.security.spec.AlgorithmParameterSpec; @@ -520,6 +521,19 @@ public void testGetParameter() { } + // https://android-review.googlesource.com/#/c/309105/ + // http://b/33383388 + // getCurrentSpi throws a NPE on a Signature that was obtained via a provider that has that + // algorithm registered for a SignatureSpi. + public void testSignature_getCurrentSpi_Success() throws Exception { + Provider provider = new MyProvider( + "TestProvider", 1.0, "Test Provider", "Signature.ABC", + MySignatureSpi.class.getName()); + Signature signature = Signature.getInstance("ABC", provider); + assertNotNull(signature.getCurrentSpi()); + assertEquals(MySignatureSpi.class, signature.getCurrentSpi().getClass()); + } + private class MyKey implements Key { public String getFormat() { return "123"; @@ -558,7 +572,14 @@ public String toString() { } @SuppressWarnings("unused") - protected static class MySignature extends Signature implements Cloneable { + // Needs to be public as this is checked by the provider class when providing an instance of + // a class + // There is a lot of code duplication, or better said, signature duplication with respect to + // MySignatureSpi. However, as for the test to check the desired functionality this class + // must extend from Signature and MySignatureSpi must extend from SignatureSpi. Then there is + // no way to avoid duplication other than delegation, but delegation would require to repeat + // all method signatures once more. + public static class MySignature extends Signature implements Cloneable { public MySignature() { super("TestSignature"); @@ -619,6 +640,66 @@ protected void engineSetParameter(AlgorithmParameterSpec params) } } + @SuppressWarnings("unused") + // Needs to be public as this is checked by the provider class when providing an instance of + // a class + public static class MySignatureSpi extends SignatureSpi implements Cloneable { + + @Override + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new InvalidParameterException(); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + throw new InvalidKeyException(); + } + + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + throw new InvalidKeyException(); + } + + @Override + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new InvalidParameterException(); + } + + @Override + protected byte[] engineSign() throws SignatureException { + return null; + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + throw new SignatureException(); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + + } + + @Override + protected boolean engineVerify(byte[] sigBytes) + throws SignatureException { + return false; + } + + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (params == null) { + throw new InvalidAlgorithmParameterException(); + } + } + } + private class MyProvider extends Provider { protected MyProvider(String name, double version, String info, String signame, String className) { diff --git a/luni/src/test/java/tests/java/sql/DatabaseMetaDataTest.java b/luni/src/test/java/tests/java/sql/DatabaseMetaDataTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/tests/java/sql/DeleteFunctionalityTest.java b/luni/src/test/java/tests/java/sql/DeleteFunctionalityTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/tests/java/sql/InsertFunctionalityTest.java b/luni/src/test/java/tests/java/sql/InsertFunctionalityTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/tests/java/sql/MultiThreadAccessTest.java b/luni/src/test/java/tests/java/sql/MultiThreadAccessTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/tests/java/sql/SelectFunctionalityTest.java b/luni/src/test/java/tests/java/sql/SelectFunctionalityTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/tests/java/sql/StressTest.java b/luni/src/test/java/tests/java/sql/StressTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/tests/java/sql/UpdateFunctionalityTest.java b/luni/src/test/java/tests/java/sql/UpdateFunctionalityTest.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/tests/java/sql/UpdateFunctionalityTest2.java b/luni/src/test/java/tests/java/sql/UpdateFunctionalityTest2.java old mode 100755 new mode 100644 diff --git a/luni/src/test/java/tests/org/w3c/dom/CreateAttributeNS.java b/luni/src/test/java/tests/org/w3c/dom/CreateAttributeNS.java index 0e53813a4..c6856b3ac 100644 --- a/luni/src/test/java/tests/org/w3c/dom/CreateAttributeNS.java +++ b/luni/src/test/java/tests/org/w3c/dom/CreateAttributeNS.java @@ -176,13 +176,13 @@ public void testCreateAttributeNS6() throws Throwable { doc = (Document) load("hc_staff", builder); - // BEGIN android-changed + // BEGIN Android-changed // Our exception priorities differ from the spec try { doc.createAttributeNS(namespaceURI, ""); fail(); } catch (DOMException ex) { } - // END android-changed + // END Android-changed } } diff --git a/luni/src/test/java/tests/org/w3c/dom/CreateDocument.java b/luni/src/test/java/tests/org/w3c/dom/CreateDocument.java index e973842d1..64a92a07a 100644 --- a/luni/src/test/java/tests/org/w3c/dom/CreateDocument.java +++ b/luni/src/test/java/tests/org/w3c/dom/CreateDocument.java @@ -263,13 +263,13 @@ public void testCreateDocument8() throws Throwable { domImpl = builder.getDOMImplementation(); - // BEGIN android-changed + // BEGIN Android-changed // Our exception priorities differ from the spec try { domImpl.createDocument(namespaceURI, "", docType); fail(); } catch (DOMException ex) { } - // END android-changed + // END Android-changed } } diff --git a/luni/src/test/java/tests/org/w3c/dom/CreateElementNS.java b/luni/src/test/java/tests/org/w3c/dom/CreateElementNS.java index 50bfbf30e..ec69717f4 100644 --- a/luni/src/test/java/tests/org/w3c/dom/CreateElementNS.java +++ b/luni/src/test/java/tests/org/w3c/dom/CreateElementNS.java @@ -197,14 +197,14 @@ public void testCreateElementNS6() throws Throwable { doc = (Document) load("hc_staff", builder); { - // BEGIN android-changed + // BEGIN Android-changed // Our exception priorities differ from the spec try { doc.createElementNS(namespaceURI, ""); fail(); } catch (DOMException ex) { } - // END android-changed + // END Android-changed } } } diff --git a/luni/src/test/java/tests/org/w3c/dom/DocumentCreateAttributeNS.java b/luni/src/test/java/tests/org/w3c/dom/DocumentCreateAttributeNS.java index 00d52870d..c6e41f16e 100644 --- a/luni/src/test/java/tests/org/w3c/dom/DocumentCreateAttributeNS.java +++ b/luni/src/test/java/tests/org/w3c/dom/DocumentCreateAttributeNS.java @@ -187,14 +187,14 @@ public void testCreateAttributeNS4() throws Throwable { { - // BEGIN android-changed + // BEGIN Android-changed // Our exception priorities differ from the spec try { doc.createAttributeNS(namespaceURI, qualifiedName); fail(); } catch (DOMException ex) { } - // END android-changed + // END Android-changed } } } diff --git a/luni/src/test/java/tests/org/w3c/dom/GetElementsByTagNameNS.java b/luni/src/test/java/tests/org/w3c/dom/GetElementsByTagNameNS.java index 19dc827cc..1b9cd43f2 100644 --- a/luni/src/test/java/tests/org/w3c/dom/GetElementsByTagNameNS.java +++ b/luni/src/test/java/tests/org/w3c/dom/GetElementsByTagNameNS.java @@ -73,9 +73,9 @@ public void testGetElementsByTagNameNS1() throws Throwable { NodeList newList; doc = (Document) load("staffNS", builder); newList = doc.getElementsByTagNameNS(namespaceURI, localName); - // BEGIN android-changed: Was 37, but that assumed validation. + // BEGIN Android-changed: Was 37, but that assumed validation. assertEquals("throw_Size", 36, newList.getLength()); - // END android-changed + // END Android-changed } public void testGetElementsByTagNameNS2() throws Throwable { Document doc; diff --git a/luni/src/test/java/tests/org/w3c/dom/SetAttributeNS.java b/luni/src/test/java/tests/org/w3c/dom/SetAttributeNS.java index 671efbe94..886fcbcd6 100644 --- a/luni/src/test/java/tests/org/w3c/dom/SetAttributeNS.java +++ b/luni/src/test/java/tests/org/w3c/dom/SetAttributeNS.java @@ -108,7 +108,7 @@ public void testSetAttributeNS2() throws Throwable { testAddr = elementList.item(0); { - // BEGIN android-changed + // BEGIN Android-changed // Our exception priorities differ from the spec try { ((Element) /* Node */testAddr).setAttributeNS(namespaceURI, @@ -116,7 +116,7 @@ public void testSetAttributeNS2() throws Throwable { fail(); } catch (DOMException ex) { } - // END android-changed + // END Android-changed } } @@ -287,7 +287,7 @@ public void testSetAttributeNS10() throws Throwable { testAddr = elementList.item(0); { - // BEGIN android-changed + // BEGIN Android-changed // Our exception priorities differ from the spec try { ((Element) /* Node */testAddr).setAttributeNS(namespaceURI, "", @@ -295,7 +295,7 @@ public void testSetAttributeNS10() throws Throwable { fail(); } catch (DOMException ex) { } - // END android-changed + // END Android-changed } } } diff --git a/luni/src/test/java/tests/security/cert/CertificateTest.java b/luni/src/test/java/tests/security/cert/CertificateTest.java index 194bfdb7a..156ccd25b 100644 --- a/luni/src/test/java/tests/security/cert/CertificateTest.java +++ b/luni/src/test/java/tests/security/cert/CertificateTest.java @@ -97,6 +97,15 @@ public final void testHashCodeEqualsObject() { assertFalse(cert.equals(c1)); } + /** + * Test for hashCode() method
+ * Assertion: returns the value computed with the algorithm in jdk8u60. + */ + public final void testHashCodeValue() { + Certificate c1 = new MyCertificate("TEST_TYPE", testEncoding); + // Result used to be 40 prior to jdk8u60. + assertEquals(29615266, c1.hashCode()); + } /** * Test for getType() method
@@ -175,7 +184,7 @@ public final void testGetEncoded() throws CertificateException { } /** - * This test just calls verify(PublicKey) method
+ * verify(PublicKey) with null args * * @throws InvalidKeyException * @throws CertificateException @@ -194,7 +203,7 @@ public final void testVerifyPublicKey() } /** - * This test just calls verify(PublicKey,String) method
+ * verify(PublicKey,String) with null args * * @throws InvalidKeyException * @throws CertificateException @@ -209,7 +218,26 @@ public final void testVerifyPublicKeyString() NoSuchProviderException, SignatureException { Certificate c1 = new MyCertificate("TEST_TYPE", testEncoding); - c1.verify(null, null); + c1.verify((PublicKey) null, (String) null); + } + + /** + * verify(PublicKey,Provider) with null args + * + * @throws InvalidKeyException + * @throws CertificateException + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws SignatureException + */ + public final void testVerifyPublicKeyProvider() + throws Exception { + Certificate c1 = new MyCertificate("TEST_TYPE", testEncoding); + try { + // Android-changed: throw UOE instead of infinite recursion. + c1.verify((PublicKey) null, (Provider) null); + fail(); + } catch(UnsupportedOperationException expected) {} } /** @@ -370,6 +398,15 @@ public final void testVerifyPublicKeyString2() throws InvalidKeyException, */ } + public final void testVerifyPublicKeyProvider2() throws Exception { + final Signature sig = Signature.getInstance("SHA1WithRSA"); + sig.initVerify(cert.getPublicKey()); + final Provider provider = sig.getProvider(); + cert.verify(cert.getPublicKey(), provider); + // equivalent to calling cert.verify(cert.getPublicKey()) + cert.verify(cert.getPublicKey(), (Provider)null); + } + /** * This test just calls verify(PublicKey) method
* diff --git a/luni/src/test/java/tests/security/cert/X509CertSelectorTest.java b/luni/src/test/java/tests/security/cert/X509CertSelectorTest.java index a6eaf05a5..1ef25fd8b 100644 --- a/luni/src/test/java/tests/security/cert/X509CertSelectorTest.java +++ b/luni/src/test/java/tests/security/cert/X509CertSelectorTest.java @@ -397,7 +397,8 @@ public void test_getIssuerAsBytes() throws Exception { selector.setIssuer(iss1); assertTrue("The returned issuer should be equal to specified", Arrays.equals(name1, selector.getIssuerAsBytes())); - assertFalse("The returned issuer should differ", name2.equals(selector.getIssuerAsBytes())); + assertFalse("The returned issuer should differ", + Arrays.equals(name2, selector.getIssuerAsBytes())); selector.setIssuer(iss2); assertTrue("The returned issuer should be equal to specified", Arrays.equals(name2, selector.getIssuerAsBytes())); @@ -682,7 +683,7 @@ public void test_getSubjectAsBytes() throws Exception { assertTrue("The returned issuer should be equal to specified", Arrays.equals(name1, selector.getSubjectAsBytes())); assertFalse("The returned issuer should differ", - name2.equals(selector.getSubjectAsBytes())); + Arrays.equals(name2, selector.getSubjectAsBytes())); selector.setSubject(sub2); assertTrue("The returned issuer should be equal to specified", Arrays.equals(name2, selector.getSubjectAsBytes())); @@ -858,6 +859,7 @@ public void test_setBasicConstraintsLint() { for (int i = 0; i < invalidValues.length; i++) { try { selector.setBasicConstraints(-3); + fail(); } catch (IllegalArgumentException expected) { } } diff --git a/luni/src/test/java/tests/security/cert/X509Certificate2Test.java b/luni/src/test/java/tests/security/cert/X509Certificate2Test.java index 03b02435b..bf736d58e 100644 --- a/luni/src/test/java/tests/security/cert/X509Certificate2Test.java +++ b/luni/src/test/java/tests/security/cert/X509Certificate2Test.java @@ -306,16 +306,19 @@ public void testGetExtendedKeyUsage() throws Exception { try { l.clear(); + fail(); } catch (UnsupportedOperationException expected) { } try { l.add("Test"); + fail(); } catch (UnsupportedOperationException expected) { } try { l.remove(0); + fail(); } catch (UnsupportedOperationException expected) { } } diff --git a/luni/src/test/java/tests/support/DatabaseCreator.java b/luni/src/test/java/tests/support/DatabaseCreator.java old mode 100755 new mode 100644 diff --git a/luni/src/test/native/libcore_java_io_FileTest.cpp b/luni/src/test/native/libcore_java_io_FileTest.cpp index 1793a8a77..4dcf487b3 100644 --- a/luni/src/test/native/libcore_java_io_FileTest.cpp +++ b/luni/src/test/native/libcore_java_io_FileTest.cpp @@ -13,9 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#include +#include #include +#include #include +#include +#include #include #include "JNIHelp.h" @@ -47,3 +51,26 @@ extern "C" void Java_libcore_java_io_FileTest_nativeTestFilesWithSurrogatePairs( jniThrowException(env, "java/lang/IllegalStateException", "expected file"); } } + +extern "C" int Java_libcore_java_io_FileTest_installSeccompFilter(JNIEnv* , jclass /* clazz */) { + struct sock_filter filter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, nr)), + +// for arm, mips, x86. +#ifdef __NR_fstatat64 + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_fstatat64, 0, 1), +#else +// for arm64, x86_64. + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_newfstatat, 0, 1), +#endif + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | EPERM), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog = { + .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), + .filter = filter, + }; + long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + + return ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); +} diff --git a/luni/src/test/resources/libcore/java/lang/reflect/parameter/metadata_variations.dex b/luni/src/test/resources/libcore/java/lang/reflect/parameter/metadata_variations.dex new file mode 100644 index 000000000..2cade4513 Binary files /dev/null and b/luni/src/test/resources/libcore/java/lang/reflect/parameter/metadata_variations.dex differ diff --git a/luni/src/test/resources/libcore/java/lang/reflect/parameter/parameter_metadata_test_classes.dex b/luni/src/test/resources/libcore/java/lang/reflect/parameter/parameter_metadata_test_classes.dex new file mode 100644 index 000000000..ad7a53db0 Binary files /dev/null and b/luni/src/test/resources/libcore/java/lang/reflect/parameter/parameter_metadata_test_classes.dex differ diff --git a/luni/src/test/resources/serialization/org/apache/harmony/regex/tests/java/util/regex/PatternSyntaxExceptionTest.golden.ser b/luni/src/test/resources/serialization/org/apache/harmony/regex/tests/java/util/regex/PatternSyntaxExceptionTest.golden.ser old mode 100755 new mode 100644 diff --git a/luni/src/test/resources/serialization/org/apache/harmony/regex/tests/java/util/regex/PatternTest.golden.ser b/luni/src/test/resources/serialization/org/apache/harmony/regex/tests/java/util/regex/PatternTest.golden.ser old mode 100755 new mode 100644 diff --git a/luni/src/test/resources/tests/api/java/io/sameFieldNames.dex b/luni/src/test/resources/tests/api/java/io/sameFieldNames.dex new file mode 100644 index 000000000..ada4935de Binary files /dev/null and b/luni/src/test/resources/tests/api/java/io/sameFieldNames.dex differ diff --git a/luni/src/test/resources/tests/api/java/io/sameFieldNames.smali b/luni/src/test/resources/tests/api/java/io/sameFieldNames.smali new file mode 100644 index 000000000..657b96578 --- /dev/null +++ b/luni/src/test/resources/tests/api/java/io/sameFieldNames.smali @@ -0,0 +1,17 @@ +# Source for sameFieldNames.dex +.class public LsameFieldNames; +.super Ljava/lang/Object; +.implements Ljava/io/Serializable; + +# Test multiple fields with the same name and different types. +# (Invalid in Java language but valid in bytecode.) +.field public a:J +.field public a:I +.field public a:Ljava/lang/Integer; +.field public a:Ljava/lang/Long; + +.method public constructor ()V + .registers 2 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method diff --git a/non_openjdk_java_files.mk b/non_openjdk_java_files.mk index ff97f3a6c..31ee13936 100644 --- a/non_openjdk_java_files.mk +++ b/non_openjdk_java_files.mk @@ -6,9 +6,13 @@ non_openjdk_javadoc_files := \ luni/src/main/java/android/system/OsConstants.java \ luni/src/main/java/android/system/PacketSocketAddress.java \ luni/src/main/java/android/system/StructAddrinfo.java \ + luni/src/main/java/android/system/StructCapUserData.java \ + luni/src/main/java/android/system/StructCapUserHeader.java \ luni/src/main/java/android/system/StructFlock.java \ luni/src/main/java/android/system/StructGroupReq.java \ luni/src/main/java/android/system/StructGroupSourceReq.java \ + luni/src/main/java/android/system/StructIcmpHdr.java \ + luni/src/main/java/android/system/StructIfaddrs.java \ luni/src/main/java/android/system/StructLinger.java \ luni/src/main/java/android/system/StructPasswd.java \ luni/src/main/java/android/system/StructPollfd.java \ @@ -31,30 +35,36 @@ non_openjdk_javadoc_files := \ dalvik/src/main/java/dalvik/annotation/InnerClass.java \ dalvik/src/main/java/dalvik/annotation/KnownFailure.java \ dalvik/src/main/java/dalvik/annotation/MemberClasses.java \ + dalvik/src/main/java/dalvik/annotation/MethodParameters.java \ dalvik/src/main/java/dalvik/annotation/Signature.java \ dalvik/src/main/java/dalvik/annotation/TestTarget.java \ dalvik/src/main/java/dalvik/annotation/TestTargetClass.java \ dalvik/src/main/java/dalvik/annotation/Throws.java \ + dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java \ + dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java \ dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java \ dalvik/src/main/java/dalvik/bytecode/Opcodes.java \ dalvik/src/main/java/dalvik/system/AllocationLimitError.java \ dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java \ dalvik/src/main/java/dalvik/system/BlockGuard.java \ + libart/src/main/java/dalvik/system/ClassExt.java \ dalvik/src/main/java/dalvik/system/CloseGuard.java \ dalvik/src/main/java/dalvik/system/DalvikLogHandler.java \ dalvik/src/main/java/dalvik/system/DalvikLogging.java \ dalvik/src/main/java/dalvik/system/DexClassLoader.java \ dalvik/src/main/java/dalvik/system/DexFile.java \ dalvik/src/main/java/dalvik/system/DexPathList.java \ + dalvik/src/main/java/dalvik/system/EmulatedStackFrame.java \ + dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java \ dalvik/src/main/java/dalvik/system/NativeStart.java \ dalvik/src/main/java/dalvik/system/PathClassLoader.java \ dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java \ dalvik/src/main/java/dalvik/system/SocketTagger.java \ dalvik/src/main/java/dalvik/system/TemporaryDirectory.java \ + libart/src/main/java/dalvik/system/TransactionAbortError.java \ dalvik/src/main/java/dalvik/system/VMDebug.java \ libart/src/main/java/dalvik/system/VMRuntime.java \ libart/src/main/java/dalvik/system/VMStack.java \ - libart/src/main/java/dalvik/system/TransactionAbortError.java \ dalvik/src/main/java/dalvik/system/ZygoteHooks.java \ libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java \ libart/src/main/java/java/lang/Daemons.java \ @@ -62,7 +72,6 @@ non_openjdk_javadoc_files := \ luni/src/main/java/java/lang/FindBugsSuppressWarnings.java \ libart/src/main/java/java/lang/VMClassLoader.java \ luni/src/main/java/java/lang/ref/FinalizerReference.java \ - libart/src/main/java/java/lang/reflect/AbstractMethod.java \ luni/src/main/java/java/math/BigDecimal.java \ luni/src/main/java/java/math/BigInt.java \ luni/src/main/java/java/math/BigInteger.java \ @@ -82,95 +91,6 @@ non_openjdk_javadoc_files := \ luni/src/main/java/java/nio/charset/CharsetEncoderICU.java \ luni/src/main/java/java/nio/charset/CharsetICU.java \ luni/src/main/java/java/nio/charset/ModifiedUtf8.java \ - luni/src/main/java/java/util/concurrent/AbstractExecutorService.java \ - luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java \ - luni/src/main/java/java/util/concurrent/BlockingDeque.java \ - luni/src/main/java/java/util/concurrent/BlockingQueue.java \ - luni/src/main/java/java/util/concurrent/BrokenBarrierException.java \ - luni/src/main/java/java/util/concurrent/Callable.java \ - luni/src/main/java/java/util/concurrent/CancellationException.java \ - luni/src/main/java/java/util/concurrent/CompletableFuture.java \ - luni/src/main/java/java/util/concurrent/CompletionException.java \ - luni/src/main/java/java/util/concurrent/CompletionService.java \ - luni/src/main/java/java/util/concurrent/CompletionStage.java \ - luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java \ - luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java \ - luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java \ - luni/src/main/java/java/util/concurrent/ConcurrentMap.java \ - luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java \ - luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java \ - luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java \ - luni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java \ - luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java \ - luni/src/main/java/java/util/concurrent/CountDownLatch.java \ - luni/src/main/java/java/util/concurrent/CountedCompleter.java \ - luni/src/main/java/java/util/concurrent/CyclicBarrier.java \ - luni/src/main/java/java/util/concurrent/DelayQueue.java \ - luni/src/main/java/java/util/concurrent/Delayed.java \ - luni/src/main/java/java/util/concurrent/Exchanger.java \ - luni/src/main/java/java/util/concurrent/ExecutionException.java \ - luni/src/main/java/java/util/concurrent/Executor.java \ - luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java \ - luni/src/main/java/java/util/concurrent/ExecutorService.java \ - luni/src/main/java/java/util/concurrent/Executors.java \ - luni/src/main/java/java/util/concurrent/ForkJoinPool.java \ - luni/src/main/java/java/util/concurrent/ForkJoinTask.java \ - luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java \ - luni/src/main/java/java/util/concurrent/Future.java \ - luni/src/main/java/java/util/concurrent/FutureTask.java \ - luni/src/main/java/java/util/concurrent/Helpers.java \ - luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java \ - luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java \ - luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java \ - luni/src/main/java/java/util/concurrent/Phaser.java \ - luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java \ - luni/src/main/java/java/util/concurrent/RecursiveAction.java \ - luni/src/main/java/java/util/concurrent/RecursiveTask.java \ - luni/src/main/java/java/util/concurrent/RejectedExecutionException.java \ - luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java \ - luni/src/main/java/java/util/concurrent/RunnableFuture.java \ - luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java \ - luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java \ - luni/src/main/java/java/util/concurrent/ScheduledFuture.java \ - luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java \ - luni/src/main/java/java/util/concurrent/Semaphore.java \ - luni/src/main/java/java/util/concurrent/SynchronousQueue.java \ - luni/src/main/java/java/util/concurrent/ThreadFactory.java \ - luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java \ - luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java \ - luni/src/main/java/java/util/concurrent/TimeUnit.java \ - luni/src/main/java/java/util/concurrent/TimeoutException.java \ - luni/src/main/java/java/util/concurrent/TransferQueue.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java \ - luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java \ - luni/src/main/java/java/util/concurrent/atomic/DoubleAccumulator.java \ - luni/src/main/java/java/util/concurrent/atomic/DoubleAdder.java \ - luni/src/main/java/java/util/concurrent/atomic/LongAccumulator.java \ - luni/src/main/java/java/util/concurrent/atomic/LongAdder.java \ - luni/src/main/java/java/util/concurrent/atomic/Striped64.java \ - luni/src/main/java/java/util/concurrent/atomic/package-info.java \ - luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java \ - luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java \ - luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java \ - luni/src/main/java/java/util/concurrent/locks/Condition.java \ - luni/src/main/java/java/util/concurrent/locks/Lock.java \ - luni/src/main/java/java/util/concurrent/locks/LockSupport.java \ - luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java \ - luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java \ - luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java \ - luni/src/main/java/java/util/concurrent/locks/StampedLock.java \ - luni/src/main/java/java/util/concurrent/locks/package-info.java \ - luni/src/main/java/java/util/concurrent/package-info.java \ luni/src/main/java/javax/xml/XMLConstants.java \ luni/src/main/java/javax/xml/datatype/DatatypeConfigurationException.java \ luni/src/main/java/javax/xml/datatype/DatatypeConstants.java \ @@ -317,31 +237,6 @@ non_openjdk_javadoc_files := \ xml/src/main/java/org/xmlpull/v1/sax2/Driver.java \ non_openjdk_java_files := \ - dex/src/main/java/com/android/dex/Annotation.java \ - dex/src/main/java/com/android/dex/ClassData.java \ - dex/src/main/java/com/android/dex/ClassDef.java \ - dex/src/main/java/com/android/dex/Code.java \ - dex/src/main/java/com/android/dex/Dex.java \ - dex/src/main/java/com/android/dex/DexException.java \ - dex/src/main/java/com/android/dex/DexFormat.java \ - dex/src/main/java/com/android/dex/DexIndexOverflowException.java \ - dex/src/main/java/com/android/dex/EncodedValue.java \ - dex/src/main/java/com/android/dex/EncodedValueCodec.java \ - dex/src/main/java/com/android/dex/EncodedValueReader.java \ - dex/src/main/java/com/android/dex/FieldId.java \ - dex/src/main/java/com/android/dex/Leb128.java \ - dex/src/main/java/com/android/dex/MethodId.java \ - dex/src/main/java/com/android/dex/Mutf8.java \ - dex/src/main/java/com/android/dex/ProtoId.java \ - dex/src/main/java/com/android/dex/SizeOf.java \ - dex/src/main/java/com/android/dex/TableOfContents.java \ - dex/src/main/java/com/android/dex/TypeList.java \ - dex/src/main/java/com/android/dex/util/ByteArrayByteInput.java \ - dex/src/main/java/com/android/dex/util/ByteInput.java \ - dex/src/main/java/com/android/dex/util/ByteOutput.java \ - dex/src/main/java/com/android/dex/util/ExceptionWithContext.java \ - dex/src/main/java/com/android/dex/util/FileUtils.java \ - dex/src/main/java/com/android/dex/util/Unsigned.java \ dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java \ dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java \ dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java \ @@ -367,20 +262,20 @@ non_openjdk_java_files := \ luni/src/main/java/libcore/internal/StringPool.java \ luni/src/main/java/libcore/io/AsynchronousCloseMonitor.java \ luni/src/main/java/libcore/io/ClassPathURLStreamHandler.java \ - luni/src/main/java/libcore/io/Base64.java \ luni/src/main/java/libcore/io/BlockGuardOs.java \ luni/src/main/java/libcore/io/BufferIterator.java \ luni/src/main/java/libcore/io/DropBox.java \ luni/src/main/java/libcore/io/EventLogger.java \ luni/src/main/java/libcore/io/ForwardingOs.java \ luni/src/main/java/libcore/io/IoBridge.java \ + luni/src/main/java/libcore/io/IoTracker.java \ luni/src/main/java/libcore/io/IoUtils.java \ luni/src/main/java/libcore/io/Libcore.java \ + luni/src/main/java/libcore/io/Linux.java \ luni/src/main/java/libcore/io/Memory.java \ luni/src/main/java/libcore/io/MemoryMappedFile.java \ luni/src/main/java/libcore/io/NioBufferIterator.java \ luni/src/main/java/libcore/io/Os.java \ - luni/src/main/java/libcore/io/Posix.java \ luni/src/main/java/libcore/io/SizeOf.java \ luni/src/main/java/libcore/io/Streams.java \ luni/src/main/java/libcore/math/MathUtils.java \ @@ -412,6 +307,7 @@ non_openjdk_java_files := \ luni/src/main/java/libcore/util/Objects.java \ luni/src/main/java/libcore/util/RecoverySystem.java \ luni/src/main/java/libcore/util/SneakyThrow.java \ + luni/src/main/java/libcore/util/TimeZoneDataFiles.java \ luni/src/main/java/libcore/util/ZoneInfo.java \ luni/src/main/java/libcore/util/ZoneInfoDB.java \ luni/src/main/java/libcore/util/HexEncoding.java \ diff --git a/ojluni/src/lambda/java/java/lang/invoke/MethodHandleInfo.java b/ojluni/src/lambda/java/java/lang/invoke/MethodHandleInfo.java deleted file mode 100644 index 68a736c72..000000000 --- a/ojluni/src/lambda/java/java/lang/invoke/MethodHandleInfo.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.lang.invoke; - -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.reflect.Member; - -public -interface MethodHandleInfo { - public static final int REF_getField = 0; - public static final int REF_getStatic = 0; - public static final int REF_putField = 0; - public static final int REF_putStatic = 0; - public static final int REF_invokeVirtual = 0; - public static final int REF_invokeStatic = 0; - public static final int REF_invokeSpecial = 0; - public static final int REF_newInvokeSpecial = 0; - public static final int REF_invokeInterface = 0; - - public int getReferenceKind(); - - public Class getDeclaringClass(); - - public String getName(); - - public MethodType getMethodType(); - - public T reflectAs(Class expected, Lookup lookup); - - public int getModifiers(); - - public default boolean isVarArgs() { return false; } - - public static String referenceKindToString(int referenceKind) { return null; } - - public static String toString(int kind, Class defc, String name, MethodType type) { - return null; - } -} diff --git a/ojluni/src/main/java/com/sun/net/ssl/internal/ssl/Provider.java b/ojluni/src/main/java/com/sun/net/ssl/internal/ssl/Provider.java deleted file mode 100755 index b1a08ce6f..000000000 --- a/ojluni/src/main/java/com/sun/net/ssl/internal/ssl/Provider.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.net.ssl.internal.ssl; - -import sun.security.ssl.SunJSSE; - -/** - * Main class for the SunJSSE provider. The actual code was moved to the - * class sun.security.ssl.SunJSSE, but for backward compatibility we - * continue to use this class as the main Provider class. - */ -public final class Provider extends SunJSSE { - - private static final long serialVersionUID = 3231825739635378733L; - - // standard constructor - public Provider() { - super(); - } - - // prefered constructor to enable FIPS mode at runtime - public Provider(java.security.Provider cryptoProvider) { - super(cryptoProvider); - } - - // constructor to enable FIPS mode from java.security file - public Provider(String cryptoProvider) { - super(cryptoProvider); - } - - // public for now, but we may want to change it or not document it. - public static synchronized boolean isFIPS() { - return SunJSSE.isFIPS(); - } - - /** - * Installs the JSSE provider. - */ - public static synchronized void install() { - /* nop. Remove this method in the future. */ - } - -} diff --git a/ojluni/src/main/java/com/sun/net/ssl/internal/ssl/X509ExtendedTrustManager.java b/ojluni/src/main/java/com/sun/net/ssl/internal/ssl/X509ExtendedTrustManager.java old mode 100755 new mode 100644 diff --git a/ojluni/src/main/java/com/sun/nio/file/ExtendedCopyOption.java b/ojluni/src/main/java/com/sun/nio/file/ExtendedCopyOption.java new file mode 100644 index 000000000..5c49d1615 --- /dev/null +++ b/ojluni/src/main/java/com/sun/nio/file/ExtendedCopyOption.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.nio.file; + +import java.nio.file.CopyOption; + +/** + * Defines extended copy options supported on some platforms + * by Sun's provider implementation. + * + * @since 1.7 + */ + +public enum ExtendedCopyOption implements CopyOption { + /** + * The copy may be interrupted by the {@link Thread#interrupt interrupt} + * method. + */ + INTERRUPTIBLE, +} diff --git a/ojluni/src/main/java/com/sun/nio/file/ExtendedOpenOption.java b/ojluni/src/main/java/com/sun/nio/file/ExtendedOpenOption.java new file mode 100644 index 000000000..28cef93d8 --- /dev/null +++ b/ojluni/src/main/java/com/sun/nio/file/ExtendedOpenOption.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.nio.file; + +import java.nio.file.OpenOption; + +/** + * Defines extended open options supported on some platforms + * by Sun's provider implementation. + * + * @since 1.7 + */ + +public enum ExtendedOpenOption implements OpenOption { + /** + * Prevent operations on the file that request read access. + */ + NOSHARE_READ, + /** + * Prevent operations on the file that request write access. + */ + NOSHARE_WRITE, + /** + * Prevent operations on the file that request delete access. + */ + NOSHARE_DELETE; +} diff --git a/ojluni/src/main/java/com/sun/nio/file/ExtendedWatchEventModifier.java b/ojluni/src/main/java/com/sun/nio/file/ExtendedWatchEventModifier.java new file mode 100644 index 000000000..805386dd1 --- /dev/null +++ b/ojluni/src/main/java/com/sun/nio/file/ExtendedWatchEventModifier.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.nio.file; + +import java.nio.file.WatchEvent.Modifier; + +/** + * Defines extended watch event modifiers supported on some platforms + * by Sun's provider implementation. + * + * @since 1.7 + */ + +public enum ExtendedWatchEventModifier implements Modifier { + + /** + * Register a file tree instead of a single directory. + */ + FILE_TREE, +} diff --git a/ojluni/src/main/java/com/sun/nio/file/SensitivityWatchEventModifier.java b/ojluni/src/main/java/com/sun/nio/file/SensitivityWatchEventModifier.java new file mode 100644 index 000000000..cc5066697 --- /dev/null +++ b/ojluni/src/main/java/com/sun/nio/file/SensitivityWatchEventModifier.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.nio.file; + +import java.nio.file.WatchEvent.Modifier; + +/** + * Defines the sensitivity levels when registering objects with a + * watch service implementation that polls the file system. + * + * @since 1.7 + */ + +public enum SensitivityWatchEventModifier implements Modifier { + /** + * High sensitivity. + */ + HIGH(2), + /** + * Medium sensitivity. + */ + MEDIUM(10), + /** + * Low sensitivity. + */ + LOW(30); + + /** + * Returns the sensitivity in seconds. + */ + public int sensitivityValueInSeconds() { + return sensitivity; + } + + private final int sensitivity; + private SensitivityWatchEventModifier(int sensitivity) { + this.sensitivity = sensitivity; + } +} diff --git a/ojluni/src/main/java/com/sun/security/cert/internal/x509/X509V1CertImpl.java b/ojluni/src/main/java/com/sun/security/cert/internal/x509/X509V1CertImpl.java old mode 100755 new mode 100644 diff --git a/ojluni/src/main/java/java/awt/font/NumericShaper.java b/ojluni/src/main/java/java/awt/font/NumericShaper.java old mode 100755 new mode 100644 index c8100bfb0..7e1980e9f --- a/ojluni/src/main/java/java/awt/font/NumericShaper.java +++ b/ojluni/src/main/java/java/awt/font/NumericShaper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -633,7 +633,6 @@ private Range rangeForCodePoint(final int codepoint) { 0x06d6, 0x06e5, 0x06e7, 0x06ee, 0x06f0, 0x06fa, - 0x070f, 0x0710, 0x0711, 0x0712, 0x0730, 0x074d, 0x07a6, 0x07b1, @@ -644,7 +643,7 @@ private Range rangeForCodePoint(final int codepoint) { 0x0825, 0x0828, 0x0829, 0x0830, 0x0859, 0x085e, - 0x0900, 0x0903, + 0x08e4, 0x0903, 0x093a, 0x093b, 0x093c, 0x093d, 0x0941, 0x0949, @@ -723,6 +722,7 @@ private Range rangeForCodePoint(final int codepoint) { 0x1732, 0x1735, 0x1752, 0x1760, 0x1772, 0x1780, + 0x17b4, 0x17b6, 0x17b7, 0x17be, 0x17c6, 0x17c7, 0x17c9, 0x17d4, @@ -750,6 +750,7 @@ private Range rangeForCodePoint(final int codepoint) { 0x1b80, 0x1b82, 0x1ba2, 0x1ba6, 0x1ba8, 0x1baa, + 0x1bab, 0x1bac, 0x1be6, 0x1be7, 0x1be8, 0x1bea, 0x1bed, 0x1bee, @@ -760,6 +761,7 @@ private Range rangeForCodePoint(final int codepoint) { 0x1cd4, 0x1ce1, 0x1ce2, 0x1ce9, 0x1ced, 0x1cee, + 0x1cf4, 0x1cf5, 0x1dc0, 0x1e00, 0x1fbd, 0x1fbe, 0x1fbf, 0x1fc2, @@ -791,7 +793,8 @@ private Range rangeForCodePoint(final int codepoint) { 0x26ad, 0x2800, 0x2900, 0x2c00, 0x2ce5, 0x2ceb, - 0x2cef, 0x2d00, + 0x2cef, 0x2cf2, + 0x2cf9, 0x2d00, 0x2d7f, 0x2d80, 0x2de0, 0x3005, 0x3008, 0x3021, @@ -814,6 +817,7 @@ private Range rangeForCodePoint(final int codepoint) { 0xa490, 0xa4d0, 0xa60d, 0xa610, 0xa66f, 0xa680, + 0xa69f, 0xa6a0, 0xa6f0, 0xa6f2, 0xa700, 0xa722, 0xa788, 0xa789, @@ -842,6 +846,8 @@ private Range rangeForCodePoint(final int codepoint) { 0xaab7, 0xaab9, 0xaabe, 0xaac0, 0xaac1, 0xaac2, + 0xaaec, 0xaaee, + 0xaaf6, 0xab01, 0xabe5, 0xabe6, 0xabe8, 0xabe9, 0xabed, 0xabf0, @@ -867,6 +873,16 @@ private Range rangeForCodePoint(final int codepoint) { 0x11080, 0x11082, 0x110b3, 0x110b7, 0x110b9, 0x110bb, + 0x11100, 0x11103, + 0x11127, 0x1112c, + 0x1112d, 0x11136, + 0x11180, 0x11182, + 0x111b6, 0x111bf, + 0x116ab, 0x116ac, + 0x116ad, 0x116ae, + 0x116b0, 0x116b6, + 0x116b7, 0x116c0, + 0x16f8f, 0x16f93, 0x1d167, 0x1d16a, 0x1d173, 0x1d183, 0x1d185, 0x1d18c, @@ -877,7 +893,9 @@ private Range rangeForCodePoint(final int codepoint) { 0x1d74f, 0x1d750, 0x1d789, 0x1d78a, 0x1d7c3, 0x1d7c4, - 0x1d7ce, 0x1f110, + 0x1d7ce, 0x1ee00, + 0x1eef0, 0x1f110, + 0x1f16a, 0x1f170, 0x1f300, 0x1f48c, 0x1f48d, 0x1f524, 0x1f525, 0x20000, @@ -1194,7 +1212,7 @@ public boolean isContextual() { * For example, to check if a shaper shapes to Arabic, you would use the * following: *

- * if ((shaper.getRanges() & shaper.ARABIC) != 0) { ... + * {@code if ((shaper.getRanges() & shaper.ARABIC) != 0) { ... } *
* *

Note that this method supports only the bit mask-based diff --git a/ojluni/src/main/java/java/awt/font/TextAttribute.java b/ojluni/src/main/java/java/awt/font/TextAttribute.java old mode 100755 new mode 100644 index bfa271d3e..04a921d30 --- a/ojluni/src/main/java/java/awt/font/TextAttribute.java +++ b/ojluni/src/main/java/java/awt/font/TextAttribute.java @@ -93,7 +93,7 @@ * @see java.text.AttributedCharacterIterator */ -// Android-removed : Removed Summary of Attributes. +// Android-removed: Removed Summary of Attributes. /*

Summary of attributes

*

* diff --git a/ojluni/src/main/java/java/beans/ChangeListenerMap.java b/ojluni/src/main/java/java/beans/ChangeListenerMap.java old mode 100755 new mode 100644 index fead0a479..fa8be4722 --- a/ojluni/src/main/java/java/beans/ChangeListenerMap.java +++ b/ojluni/src/main/java/java/beans/ChangeListenerMap.java @@ -76,7 +76,7 @@ abstract class ChangeListenerMap { */ public final synchronized void add(String name, L listener) { if (this.map == null) { - this.map = new HashMap(); + this.map = new HashMap<>(); } L[] array = this.map.get(name); int size = (array != null) @@ -146,7 +146,7 @@ public final synchronized L[] get(String name) { public final void set(String name, L[] listeners) { if (listeners != null) { if (this.map == null) { - this.map = new HashMap(); + this.map = new HashMap<>(); } this.map.put(name, listeners); } @@ -167,7 +167,7 @@ public final synchronized L[] getListeners() { if (this.map == null) { return newArray(0); } - List list = new ArrayList(); + List list = new ArrayList<>(); L[] listeners = this.map.get(null); if (listeners != null) { diff --git a/ojluni/src/main/java/java/beans/IndexedPropertyChangeEvent.java b/ojluni/src/main/java/java/beans/IndexedPropertyChangeEvent.java old mode 100755 new mode 100644 diff --git a/ojluni/src/main/java/java/beans/PropertyChangeEvent.java b/ojluni/src/main/java/java/beans/PropertyChangeEvent.java old mode 100755 new mode 100644 index 55397ef1a..eeaa65127 --- a/ojluni/src/main/java/java/beans/PropertyChangeEvent.java +++ b/ojluni/src/main/java/java/beans/PropertyChangeEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package java.beans; +import java.util.EventObject; + /** * A "PropertyChange" event gets delivered whenever a bean changes a "bound" * or "constrained" property. A PropertyChangeEvent object is sent as an @@ -42,21 +44,21 @@ * arbitrary set of if its properties have changed. In this case the * old and new values should also be null. */ - -public class PropertyChangeEvent extends java.util.EventObject { +public class PropertyChangeEvent extends EventObject { private static final long serialVersionUID = 7042693688939648123L; /** - * Constructs a new PropertyChangeEvent. + * Constructs a new {@code PropertyChangeEvent}. + * + * @param source the bean that fired the event + * @param propertyName the programmatic name of the property that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property * - * @param source The bean that fired the event. - * @param propertyName The programmatic name of the property - * that was changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @throws IllegalArgumentException if {@code source} is {@code null} */ public PropertyChangeEvent(Object source, String propertyName, - Object oldValue, Object newValue) { + Object oldValue, Object newValue) { super(source); this.propertyName = propertyName; this.newValue = newValue; diff --git a/ojluni/src/main/java/java/beans/PropertyChangeListener.java b/ojluni/src/main/java/java/beans/PropertyChangeListener.java old mode 100755 new mode 100644 diff --git a/ojluni/src/main/java/java/beans/PropertyChangeListenerProxy.java b/ojluni/src/main/java/java/beans/PropertyChangeListenerProxy.java old mode 100755 new mode 100644 diff --git a/ojluni/src/main/java/java/beans/PropertyChangeSupport.java b/ojluni/src/main/java/java/beans/PropertyChangeSupport.java old mode 100755 new mode 100644 index 270912c7c..d55ae76ef --- a/ojluni/src/main/java/java/beans/PropertyChangeSupport.java +++ b/ojluni/src/main/java/java/beans/PropertyChangeSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -156,7 +156,7 @@ public void removePropertyChangeListener(PropertyChangeListener listener) { * PropertyChangeListenerProxy, perform the cast, and examine * the parameter. * - *

+     * 
{@code
      * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
      * for (int i = 0; i < listeners.length; i++) {
      *   if (listeners[i] instanceof PropertyChangeListenerProxy) {
@@ -168,7 +168,7 @@ public void removePropertyChangeListener(PropertyChangeListener listener) {
      *     }
      *   }
      * }
-     *
+ * }
* * @see PropertyChangeListenerProxy * @return all of the PropertyChangeListeners added or an @@ -431,7 +431,7 @@ private void writeObject(ObjectOutputStream s) throws IOException { listeners = entry.getValue(); } else { if (children == null) { - children = new Hashtable(); + children = new Hashtable<>(); } PropertyChangeSupport pcs = new PropertyChangeSupport(this.source); pcs.map.set(null, entry.getValue()); @@ -460,6 +460,7 @@ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOEx ObjectInputStream.GetField fields = s.readFields(); + @SuppressWarnings("unchecked") Hashtable children = (Hashtable) fields.get("children", null); this.source = fields.get("source", null); fields.get("propertyChangeSupportSerializedDataVersion", 2); diff --git a/ojluni/src/main/java/java/beans/beancontext/package.html b/ojluni/src/main/java/java/beans/beancontext/package.html deleted file mode 100755 index 739356e24..000000000 --- a/ojluni/src/main/java/java/beans/beancontext/package.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - -Provides classes and interfaces relating to bean context. -A bean context is a container for beans and defines the execution -environment for the beans it contains. There can be several beans in -a single bean context, and a bean context can be nested within another -bean context. This package also contains events and listener -interface for beans being added and removed from a bean context. - - - -@since 1.2 - - diff --git a/ojluni/src/main/java/java/io/Bits.java b/ojluni/src/main/java/java/io/Bits.java old mode 100755 new mode 100644 diff --git a/ojluni/src/main/java/java/io/BufferedInputStream.java b/ojluni/src/main/java/java/io/BufferedInputStream.java old mode 100755 new mode 100644 index fcc7e702d..e39c20c44 --- a/ojluni/src/main/java/java/io/BufferedInputStream.java +++ b/ojluni/src/main/java/java/io/BufferedInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,17 @@ public class BufferedInputStream extends FilterInputStream { - private static int defaultBufferSize = 8192; + // Android-changed: made final + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * The maximum size of array to allocate. + * Some VMs reserve some header words in an array. + * Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + // Android-changed: made final + private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; /** * The internal buffer array where the data is stored. When necessary, @@ -172,7 +182,7 @@ private byte[] getBufIfOpen() throws IOException { * @param in the underlying input stream. */ public BufferedInputStream(InputStream in) { - this(in, defaultBufferSize); + this(in, DEFAULT_BUFFER_SIZE); } /** @@ -185,7 +195,7 @@ public BufferedInputStream(InputStream in) { * * @param in the underlying input stream. * @param size the buffer size. - * @exception IllegalArgumentException if size <= 0. + * @exception IllegalArgumentException if {@code size <= 0}. */ public BufferedInputStream(InputStream in, int size) { super(in); @@ -215,8 +225,11 @@ else if (pos >= buffer.length) /* no room left in buffer */ } else if (buffer.length >= marklimit) { markpos = -1; /* buffer got too big, invalidate mark */ pos = 0; /* drop buffer contents */ + } else if (buffer.length >= MAX_BUFFER_SIZE) { + throw new OutOfMemoryError("Required array size too large"); } else { /* grow buffer */ - int nsz = pos * 2; + int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? + pos * 2 : MAX_BUFFER_SIZE; if (nsz > marklimit) nsz = marklimit; byte nbuf[] = new byte[nsz]; diff --git a/ojluni/src/main/java/java/io/BufferedOutputStream.java b/ojluni/src/main/java/java/io/BufferedOutputStream.java old mode 100755 new mode 100644 diff --git a/ojluni/src/main/java/java/io/BufferedReader.java b/ojluni/src/main/java/java/io/BufferedReader.java old mode 100755 new mode 100644 index 5a45f4b97..fb814b1ea --- a/ojluni/src/main/java/java/io/BufferedReader.java +++ b/ojluni/src/main/java/java/io/BufferedReader.java @@ -96,7 +96,7 @@ public class BufferedReader extends Reader { * @param in A Reader * @param sz Input-buffer size * - * @exception IllegalArgumentException If sz is <= 0 + * @exception IllegalArgumentException If {@code sz <= 0} */ public BufferedReader(Reader in, int sz) { super(in); @@ -148,7 +148,7 @@ private void fill() throws IOException { } else { /* Reallocate buffer to accommodate read-ahead limit */ // - // Android changed : Use the same strategy as BufferedInputStream, + // Android-changed: Use the same strategy as BufferedInputStream, // i.e, double the size of the buffer on each fill. Do not directly // size the buffer to the readAheadLimit. // @@ -495,7 +495,7 @@ public boolean markSupported() { * whose size is no smaller than limit. * Therefore large values should be used with care. * - * @exception IllegalArgumentException If readAheadLimit is < 0 + * @exception IllegalArgumentException If {@code readAheadLimit < 0} * @exception IOException If an I/O error occurs */ public void mark(int readAheadLimit) throws IOException { @@ -532,9 +532,12 @@ public void close() throws IOException { synchronized (lock) { if (in == null) return; - in.close(); - in = null; - cb = null; + try { + in.close(); + } finally { + in = null; + cb = null; + } } } diff --git a/ojluni/src/main/java/java/io/BufferedWriter.java b/ojluni/src/main/java/java/io/BufferedWriter.java old mode 100755 new mode 100644 index 220a47edf..a5d810ad9 --- a/ojluni/src/main/java/java/io/BufferedWriter.java +++ b/ojluni/src/main/java/java/io/BufferedWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,7 +95,7 @@ public BufferedWriter(Writer out) { * @param out A Writer * @param sz Output-buffer size, a positive integer * - * @exception IllegalArgumentException If sz is <= 0 + * @exception IllegalArgumentException If {@code sz <= 0} */ public BufferedWriter(Writer out, int sz) { super(out); @@ -255,15 +255,15 @@ public void flush() throws IOException { } } + @SuppressWarnings("try") public void close() throws IOException { synchronized (lock) { if (out == null) { return; } - try { + try (Writer w = out) { flushBuffer(); } finally { - out.close(); out = null; cb = null; } diff --git a/ojluni/src/main/java/java/io/ByteArrayInputStream.java b/ojluni/src/main/java/java/io/ByteArrayInputStream.java old mode 100755 new mode 100644 index e58c6e64a..d07f0743c --- a/ojluni/src/main/java/java/io/ByteArrayInputStream.java +++ b/ojluni/src/main/java/java/io/ByteArrayInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -275,7 +275,6 @@ public synchronized void reset() { * Closing a ByteArrayInputStream has no effect. The methods in * this class can be called after the stream has been closed without * generating an IOException. - *

*/ public void close() throws IOException { } diff --git a/ojluni/src/main/java/java/io/ByteArrayOutputStream.java b/ojluni/src/main/java/java/io/ByteArrayOutputStream.java old mode 100755 new mode 100644 index c4df675c9..f1d429b48 --- a/ojluni/src/main/java/java/io/ByteArrayOutputStream.java +++ b/ojluni/src/main/java/java/io/ByteArrayOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -93,6 +93,14 @@ private void ensureCapacity(int minCapacity) { grow(minCapacity); } + /** + * The maximum size of array to allocate. + * Some VMs reserve some header words in an array. + * Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. @@ -105,14 +113,19 @@ private void grow(int minCapacity) { int newCapacity = oldCapacity << 1; if (newCapacity - minCapacity < 0) newCapacity = minCapacity; - if (newCapacity < 0) { - if (minCapacity < 0) // overflow - throw new OutOfMemoryError(); - newCapacity = Integer.MAX_VALUE; - } + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); buf = Arrays.copyOf(buf, newCapacity); } + private static int hugeCapacity(int minCapacity) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + return (minCapacity > MAX_ARRAY_SIZE) ? + Integer.MAX_VALUE : + MAX_ARRAY_SIZE; + } + /** * Writes the specified byte to this byte array output stream. * @@ -210,21 +223,21 @@ public synchronized String toString() { /** * Converts the buffer's contents into a string by decoding the bytes using - * the specified {@link java.nio.charset.Charset charsetName}. The length of - * the new String is a function of the charset, and hence may not be - * equal to the length of the byte array. + * the named {@link java.nio.charset.Charset charset}. The length of the new + * String is a function of the charset, and hence may not be equal + * to the length of the byte array. * *

This method always replaces malformed-input and unmappable-character * sequences with this charset's default replacement string. The {@link * java.nio.charset.CharsetDecoder} class should be used when more control * over the decoding process is required. * - * @param charsetName the name of a supported - * {@linkplain java.nio.charset.Charset charset} - * @return String decoded from the buffer's contents. + * @param charsetName the name of a supported + * {@link java.nio.charset.Charset charset} + * @return String decoded from the buffer's contents. * @exception UnsupportedEncodingException * If the named charset is not supported - * @since JDK1.1 + * @since JDK1.1 */ public synchronized String toString(String charsetName) throws UnsupportedEncodingException @@ -263,8 +276,6 @@ public synchronized String toString(int hibyte) { * Closing a ByteArrayOutputStream has no effect. The methods in * this class can be called after the stream has been closed without * generating an IOException. - *

- * */ public void close() throws IOException { } diff --git a/ojluni/src/main/java/java/io/CharArrayReader.java b/ojluni/src/main/java/java/io/CharArrayReader.java old mode 100755 new mode 100644 index 08d447a58..2190be39a --- a/ojluni/src/main/java/java/io/CharArrayReader.java +++ b/ojluni/src/main/java/java/io/CharArrayReader.java @@ -130,8 +130,10 @@ public int read(char b[], int off, int len) throws IOException { if (pos >= count) { return -1; } - if (pos + len > count) { - len = count - pos; + + int avail = count - pos; + if (len > avail) { + len = avail; } if (len <= 0) { return 0; @@ -157,8 +159,10 @@ public int read(char b[], int off, int len) throws IOException { public long skip(long n) throws IOException { synchronized (lock) { ensureOpen(); - if (pos + n > count) { - n = count - pos; + + long avail = count - pos; + if (n > avail) { + n = avail; } if (n < 0) { return 0; diff --git a/ojluni/src/main/java/java/io/CharArrayWriter.java b/ojluni/src/main/java/java/io/CharArrayWriter.java old mode 100755 new mode 100644 diff --git a/ojluni/src/main/java/java/io/CharConversionException.java b/ojluni/src/main/java/java/io/CharConversionException.java old mode 100755 new mode 100644 diff --git a/ojluni/src/main/java/java/io/Closeable.java b/ojluni/src/main/java/java/io/Closeable.java old mode 100755 new mode 100644 index 7f3cc8dcd..b4a1c81f0 --- a/ojluni/src/main/java/java/io/Closeable.java +++ b/ojluni/src/main/java/java/io/Closeable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,6 @@ * * @since 1.5 */ - public interface Closeable extends AutoCloseable { /** @@ -42,6 +41,12 @@ public interface Closeable extends AutoCloseable { * with it. If the stream is already closed then invoking this * method has no effect. * + *

As noted in {@link AutoCloseable#close()}, cases where the + * close may fail require careful attention. It is strongly advised + * to relinquish the underlying resources and to internally + * mark the {@code Closeable} as closed, prior to throwing + * the {@code IOException}. + * * @throws IOException if an I/O error occurs */ public void close() throws IOException; diff --git a/ojluni/src/main/java/java/io/Console.java b/ojluni/src/main/java/java/io/Console.java old mode 100755 new mode 100644 index 4eca41cc3..2b4e4e662 --- a/ojluni/src/main/java/java/io/Console.java +++ b/ojluni/src/main/java/java/io/Console.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2014 The Android Open Source Project - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,7 +76,7 @@ * manually zero the returned character array after processing to minimize the * lifetime of sensitive data in memory. * - *

+ * 
{@code
  * Console cons;
  * char[] passwd;
  * if ((cons = System.console()) != null &&
@@ -84,7 +84,7 @@
  *     ...
  *     java.util.Arrays.fill(passwd, ' ');
  * }
- * 
+ * }
* * @author Xueming Shen * @since 1.6 @@ -125,9 +125,11 @@ public PrintWriter writer() { * {@link java.io.Reader#read(java.nio.CharBuffer) read(java.nio.CharBuffer)} * on the returned object will not read in characters beyond the line * bound for each invocation, even if the destination buffer has space for - * more characters. A line bound is considered to be any one of a line feed - * ('\n'), a carriage return ('\r'), a carriage return - * followed immediately by a linefeed, or an end of stream. + * more characters. The {@code Reader}'s {@code read} methods may block if a + * line bound has not been entered or reached on the console's input device. + * A line bound is considered to be any one of a line feed ('\n'), + * a carriage return ('\r'), a carriage return followed immediately + * by a linefeed, or an end of stream. * * @return The reader associated with this console */ @@ -557,19 +559,4 @@ private Console(InputStream inStream, OutputStream outStream) { cs)); rcb = new char[1024]; } - - /** - * Android-changed: Added method for internal use only, and also in use - * by tests. - * - * @hide - */ - public static synchronized Console getConsole() { - if (istty()) { - if (cons == null) - cons = new Console(); - return cons; - } - return null; - } } diff --git a/ojluni/src/main/java/java/io/DataInput.java b/ojluni/src/main/java/java/io/DataInput.java old mode 100755 new mode 100644 index e4b7e83ff..3e0f0ddbd --- a/ojluni/src/main/java/java/io/DataInput.java +++ b/ojluni/src/main/java/java/io/DataInput.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,12 +26,12 @@ package java.io; /** - * The DataInput interface provides + * The {@code DataInput} interface provides * for reading bytes from a binary stream and * reconstructing from them data in any of * the Java primitive types. There is also * a - * facility for reconstructing a String + * facility for reconstructing a {@code String} * from data in * modified UTF-8 * format. @@ -39,146 +39,101 @@ * It is generally true of all the reading * routines in this interface that if end of * file is reached before the desired number - * of bytes has been read, an EOFException - * (which is a kind of IOException) + * of bytes has been read, an {@code EOFException} + * (which is a kind of {@code IOException}) * is thrown. If any byte cannot be read for - * any reason other than end of file, an IOException - * other than EOFException is - * thrown. In particular, an IOException + * any reason other than end of file, an {@code IOException} + * other than {@code EOFException} is + * thrown. In particular, an {@code IOException} * may be thrown if the input stream has been * closed. * - *

Modified UTF-8

+ *

Modified UTF-8

*

* Implementations of the DataInput and DataOutput interfaces represent * Unicode strings in a format that is a slight modification of UTF-8. * (For information regarding the standard UTF-8 format, see section * 3.9 Unicode Encoding Forms of The Unicode Standard, Version * 4.0). - * Note that in the following tables, the most significant bit appears in the + * Note that in the following table, the most significant bit appears in the * far left-hand column. - *

- * All characters in the range '\u0001' to - * '\u007F' are represented by a single byte: * *

- * * + * + * + * * - * + * * * - * - * + * + * + * + * * - *
+ * All characters in the range {@code '\u005Cu0001'} to + * {@code '\u005Cu007F'} are represented by a single byte:
Bit ValuesBit Values
Byte 1 - * - * - * - *
0
- *
bits 6-0
- *
- *
Byte 1
0
+ *
bits 6-0
+ *
+ * The null character {@code '\u005Cu0000'} and characters + * in the range {@code '\u005Cu0080'} to {@code '\u005Cu07FF'} are + * represented by a pair of bytes:
- *
- * - *

- * The null character '\u0000' and characters in the - * range '\u0080' to '\u07FF' are - * represented by a pair of bytes: - * - *

- * * * - * + * * * - * - * + * + * * - * - * + * + * + * + * * - *
Bit ValuesBit Values
Byte 1 - * - * - * - *
1
- *
1
- *
0
- *
bits 10-6
- *
- *
Byte 1
1
+ *
1
+ *
0
+ *
bits 10-6
*
Byte 2 - * - * - * - *
1
- *
0
- *
bits 5-0
- *
- *
Byte 2
1
+ *
0
+ *
bits 5-0
+ *
+ * {@code char} values in the range {@code '\u005Cu0800'} + * to {@code '\u005CuFFFF'} are represented by three bytes:
- *
- * - *
- * char values in the range '\u0800' to - * '\uFFFF' are represented by three bytes: - * - *
- * * * - * + * * * - * - * + * + * * - * - * + * + * * * - * + * *
Bit ValuesBit Values
Byte 1 - * - * - * - *
1
- *
1
- *
1
- *
0
- *
bits 15-12
- *
- *
Byte 1
1
+ *
1
+ *
1
+ *
0
+ *
bits 15-12
*
Byte 2 - * - * - * - *
1
- *
0
- *
bits 11-6
- *
- *
Byte 2
1
+ *
0
+ *
bits 11-6
*
Byte 3 - * - * - * - *
1
- *
0
- *
bits 5-0
- *
- *
1
+ *
0
+ *
bits 5-0
*
- *
- * + * *

* The differences between this format and the * standard UTF-8 format are the following: *