aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-01-04 20:01:19 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-01-04 20:01:19 +0000
commit3114d0b8cffe9adae8ad32922c0c337e73bf54ab (patch)
treeca12680b5b047268f7bf89297750dfb780c86bae
parentac5b3ac7a022703b338769d3ee69ae8ed65672f1 (diff)
parent0c0ed7f7abf526422d91b9d559a24b7acbc2471d (diff)
downloadmodules-utils-aml_ips_341611000.tar.gz
Snap for 11273583 from 0c0ed7f7abf526422d91b9d559a24b7acbc2471d to mainline-ipsec-releaseaml_ips_341611000aml_ips_341510000
Change-Id: I866fa274e27e3178de87393f2cc51709d29fca2c
-rw-r--r--java/Android.bp9
-rw-r--r--java/android/annotation/FlaggedApi.java35
-rw-r--r--java/android/annotation/PermissionManuallyEnforced.java47
-rw-r--r--java/android/annotation/UserHandleAware.java4
-rw-r--r--java/com/android/internal/util/Android.bp11
-rw-r--r--java/com/android/internal/util/FastXmlSerializer.java424
-rw-r--r--java/com/android/modules/expresslog/Android.bp6
-rw-r--r--java/com/android/modules/expresslog/Counter.java8
-rw-r--r--java/com/android/modules/expresslog/Histogram.java21
-rw-r--r--java/com/android/modules/expresslog/Utils.java21
-rw-r--r--java/com/android/modules/utils/FastDataInput.java4
-rw-r--r--java/com/android/modules/utils/build/UnboundedSdkLevel.java21
-rw-r--r--java/com/android/modules/utils/testing/AbstractExtendedMockitoRule.java253
-rw-r--r--java/com/android/modules/utils/testing/ExtendedMockitoRule.java32
-rw-r--r--javatests/com/android/modules/expresslog/Android.bp33
-rw-r--r--javatests/com/android/modules/expresslog/ScaledRangeOptionsTest.java4
-rw-r--r--javatests/com/android/modules/expresslog/UniformOptionsTest.java4
-rw-r--r--javatests/com/android/modules/expresslog/jni/.clang-format17
-rw-r--r--javatests/com/android/modules/expresslog/jni/onload.cpp40
-rw-r--r--javatests/com/android/modules/utils/build/Android.bp2
-rw-r--r--javatests/com/android/modules/utils/testing/Android.bp2
-rw-r--r--javatests/com/android/modules/utils/testing/ExtendedMockitoRuleTest.java336
-rw-r--r--jni/expresslog/.clang-format17
-rw-r--r--jni/expresslog/Android.bp53
-rw-r--r--jni/expresslog/com_android_modules_expresslog_Utils.cpp81
25 files changed, 1041 insertions, 444 deletions
diff --git a/java/Android.bp b/java/Android.bp
index 670e4ed..eee1e0e 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -66,13 +66,13 @@ java_library {
srcs: [
"com/android/aconfig/annotations/*.java",
],
- sdk_version: "current",
+ sdk_version: "core_current",
host_supported: true,
optimize: {
proguard_flags_files: ["aconfig_proguard.flags"],
},
visibility: [
- "//visibility:private",
+ "//visibility:public",
],
}
@@ -80,8 +80,12 @@ filegroup {
name: "framework-api-annotations",
srcs: [
"android/annotation/Discouraged.java",
+ "android/annotation/FlaggedApi.java",
+ "android/annotation/IntDef.java",
"android/annotation/SystemApi.java",
"android/annotation/TestApi.java",
+ // aconfig annotations
+ "com/android/aconfig/annotations/*.java",
],
visibility: [
@@ -114,6 +118,7 @@ filegroup {
visibility: [
"//frameworks/libs/modules-utils/java/com/android/modules/utils",
"//packages/modules/Bluetooth/system/binder",
+ "//packages/modules/Bluetooth/android/app/aidl",
],
}
diff --git a/java/android/annotation/FlaggedApi.java b/java/android/annotation/FlaggedApi.java
index 9e43650..90c8e93 100644
--- a/java/android/annotation/FlaggedApi.java
+++ b/java/android/annotation/FlaggedApi.java
@@ -15,7 +15,9 @@
*/
package android.annotation;
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
@@ -24,18 +26,37 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Indicates an API is part of a feature that is guarded by an aconfig flag.
- * </p>
- * This annotation should only appear on APIs that are marked <pre>@hide</pre>.
+ * Indicates an API is part of a feature that is guarded by an aconfig flag, and only available if
+ * the flag is enabled.
+ * <p>
+ * Unless the API has been finalized and has become part of the SDK, callers of the annotated API
+ * must check that the flag is enabled before making any assumptions about the existence of the API.
+ * <p>
+ * Example:
+ * <code><pre>
+ * import com.example.foobar.Flags;
*
+ * &#64;FlaggedApi(Flags.FLAG_FOOBAR)
+ * public void foobar() { ... }
+ * </pre></code>
+ * Usage example:
+ * <code><pre>
+ * public void codeThatUsesFoobarApi() {
+ * if (Flags.foobar()) {
+ * foobar();
+ * } else {
+ * // gracefully handle absence of the foobar API.
+ * }
+ * }
+ * </pre></code>
* @hide
*/
-@Target({TYPE, METHOD, CONSTRUCTOR})
+@Target({TYPE, METHOD, CONSTRUCTOR, FIELD, ANNOTATION_TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface FlaggedApi {
/**
- * Namespace and name of aconfig flag used to guard the feature this API is part of. Expected
- * syntax: namespace/name, e.g. "the_namespace/the_name_of_the_flag".
+ * The aconfig flag used to guard the feature this API is part of. Use the aconfig
+ * auto-generated constant to refer to the flag, e.g. @FlaggedApi(Flags.FLAG_FOOBAR).
*/
- String flag() default "";
+ String value();
}
diff --git a/java/android/annotation/PermissionManuallyEnforced.java b/java/android/annotation/PermissionManuallyEnforced.java
new file mode 100644
index 0000000..5641cfc
--- /dev/null
+++ b/java/android/annotation/PermissionManuallyEnforced.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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.annotation;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated method validates permissions manually.
+ * <p>
+ * This explicit annotation helps distinguish which of states an
+ * element may exist in:
+ * <ul>
+ * <li>Annotated with {@link EnforcePermission}, indicating that an element
+ * strictly requires one or more permissions. The verification occurs within
+ * the annotated element.
+ * <li>Annotated with {@link RequiresNoPermission}, indicating that an element
+ * requires no permissions.
+ * <li>Annotated with {@link PermissionManuallyEnforced}, indicating that the
+ * element requires some kind of permission which cannot be described using the
+ * other annotations.
+ * </ul>
+ *
+ * @see EnforcePermission
+ * @see RequiresNoPermission
+ * @hide
+ */
+@Retention(CLASS)
+@Target({METHOD})
+public @interface PermissionManuallyEnforced {
+}
diff --git a/java/android/annotation/UserHandleAware.java b/java/android/annotation/UserHandleAware.java
index 60dcbd8..2c3badc 100644
--- a/java/android/annotation/UserHandleAware.java
+++ b/java/android/annotation/UserHandleAware.java
@@ -38,9 +38,9 @@ import java.lang.annotation.Target;
* public abstract PackageInfo getPackageInfo({@literal @}NonNull String packageName,
* {@literal @}PackageInfoFlags int flags) throws NameNotFoundException;
* }</pre>
+ * This method uses {@linkplain android.content.Context#getUser()} or
+ * {@linkplain android.content.Context#getUserId()} to execute across users.
*
- * @memberDoc This method uses {@linkplain android.content.Context#getUser}
- * or {@linkplain android.content.Context#getUserId} to execute across users.
* @hide
*/
@Retention(SOURCE)
diff --git a/java/com/android/internal/util/Android.bp b/java/com/android/internal/util/Android.bp
index ea21a6b..c2de1fb 100644
--- a/java/com/android/internal/util/Android.bp
+++ b/java/com/android/internal/util/Android.bp
@@ -38,3 +38,14 @@ java_library {
"unsupportedappusage",
],
}
+
+java_library {
+ name: "modules-utils-fastxmlserializer",
+ srcs: [
+ "FastXmlSerializer.java",
+ ],
+ defaults: ["modules-utils-defaults"],
+ libs: [
+ "unsupportedappusage",
+ ],
+}
diff --git a/java/com/android/internal/util/FastXmlSerializer.java b/java/com/android/internal/util/FastXmlSerializer.java
new file mode 100644
index 0000000..929c9e8
--- /dev/null
+++ b/java/com/android/internal/util/FastXmlSerializer.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2006 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.internal.util;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+
+/**
+ * This is a quick and dirty implementation of XmlSerializer that isn't horribly
+ * painfully slow like the normal one. It only does what is needed for the
+ * specific XML files being written with it.
+ */
+public class FastXmlSerializer implements XmlSerializer {
+ private static final String ESCAPE_TABLE[] = new String[] {
+ "&#0;", "&#1;", "&#2;", "&#3;", "&#4;", "&#5;", "&#6;", "&#7;", // 0-7
+ "&#8;", "&#9;", "&#10;", "&#11;", "&#12;", "&#13;", "&#14;", "&#15;", // 8-15
+ "&#16;", "&#17;", "&#18;", "&#19;", "&#20;", "&#21;", "&#22;", "&#23;", // 16-23
+ "&#24;", "&#25;", "&#26;", "&#27;", "&#28;", "&#29;", "&#30;", "&#31;", // 24-31
+ null, null, "&quot;", null, null, null, "&amp;", null, // 32-39
+ null, null, null, null, null, null, null, null, // 40-47
+ null, null, null, null, null, null, null, null, // 48-55
+ null, null, null, null, "&lt;", null, "&gt;", null, // 56-63
+ };
+
+ private static final int DEFAULT_BUFFER_LEN = 32*1024;
+
+ private static String sSpace = " ";
+
+ private final int mBufferLen;
+ private final char[] mText;
+ private int mPos;
+
+ private Writer mWriter;
+
+ private OutputStream mOutputStream;
+ private CharsetEncoder mCharset;
+ private ByteBuffer mBytes;
+
+ private boolean mIndent = false;
+ private boolean mInTag;
+
+ private int mNesting = 0;
+ private boolean mLineStart = true;
+
+ @UnsupportedAppUsage
+ public FastXmlSerializer() {
+ this(DEFAULT_BUFFER_LEN);
+ }
+
+ /**
+ * Allocate a FastXmlSerializer with the given internal output buffer size. If the
+ * size is zero or negative, then the default buffer size will be used.
+ *
+ * @param bufferSize Size in bytes of the in-memory output buffer that the writer will use.
+ */
+ public FastXmlSerializer(int bufferSize) {
+ mBufferLen = (bufferSize > 0) ? bufferSize : DEFAULT_BUFFER_LEN;
+ mText = new char[mBufferLen];
+ mBytes = ByteBuffer.allocate(mBufferLen);
+ }
+
+ private void append(char c) throws IOException {
+ int pos = mPos;
+ if (pos >= (mBufferLen-1)) {
+ flush();
+ pos = mPos;
+ }
+ mText[pos] = c;
+ mPos = pos+1;
+ }
+
+ private void append(String str, int i, final int length) throws IOException {
+ if (length > mBufferLen) {
+ final int end = i + length;
+ while (i < end) {
+ int next = i + mBufferLen;
+ append(str, i, next<end ? mBufferLen : (end-i));
+ i = next;
+ }
+ return;
+ }
+ int pos = mPos;
+ if ((pos+length) > mBufferLen) {
+ flush();
+ pos = mPos;
+ }
+ str.getChars(i, i+length, mText, pos);
+ mPos = pos + length;
+ }
+
+ private void append(char[] buf, int i, final int length) throws IOException {
+ if (length > mBufferLen) {
+ final int end = i + length;
+ while (i < end) {
+ int next = i + mBufferLen;
+ append(buf, i, next<end ? mBufferLen : (end-i));
+ i = next;
+ }
+ return;
+ }
+ int pos = mPos;
+ if ((pos+length) > mBufferLen) {
+ flush();
+ pos = mPos;
+ }
+ System.arraycopy(buf, i, mText, pos, length);
+ mPos = pos + length;
+ }
+
+ private void append(String str) throws IOException {
+ append(str, 0, str.length());
+ }
+
+ private void appendIndent(int indent) throws IOException {
+ indent *= 4;
+ if (indent > sSpace.length()) {
+ indent = sSpace.length();
+ }
+ append(sSpace, 0, indent);
+ }
+
+ private void escapeAndAppendString(final String string) throws IOException {
+ final int N = string.length();
+ final char NE = (char)ESCAPE_TABLE.length;
+ final String[] escapes = ESCAPE_TABLE;
+ int lastPos = 0;
+ int pos;
+ for (pos=0; pos<N; pos++) {
+ char c = string.charAt(pos);
+ if (c >= NE) continue;
+ String escape = escapes[c];
+ if (escape == null) continue;
+ if (lastPos < pos) append(string, lastPos, pos-lastPos);
+ lastPos = pos + 1;
+ append(escape);
+ }
+ if (lastPos < pos) append(string, lastPos, pos-lastPos);
+ }
+
+ private void escapeAndAppendString(char[] buf, int start, int len) throws IOException {
+ final char NE = (char)ESCAPE_TABLE.length;
+ final String[] escapes = ESCAPE_TABLE;
+ int end = start+len;
+ int lastPos = start;
+ int pos;
+ for (pos=start; pos<end; pos++) {
+ char c = buf[pos];
+ if (c >= NE) continue;
+ String escape = escapes[c];
+ if (escape == null) continue;
+ if (lastPos < pos) append(buf, lastPos, pos-lastPos);
+ lastPos = pos + 1;
+ append(escape);
+ }
+ if (lastPos < pos) append(buf, lastPos, pos-lastPos);
+ }
+
+ public XmlSerializer attribute(String namespace, String name, String value) throws IOException,
+ IllegalArgumentException, IllegalStateException {
+ append(' ');
+ if (namespace != null) {
+ append(namespace);
+ append(':');
+ }
+ append(name);
+ append("=\"");
+
+ escapeAndAppendString(value);
+ append('"');
+ mLineStart = false;
+ return this;
+ }
+
+ public void cdsect(String text) throws IOException, IllegalArgumentException,
+ IllegalStateException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void comment(String text) throws IOException, IllegalArgumentException,
+ IllegalStateException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void docdecl(String text) throws IOException, IllegalArgumentException,
+ IllegalStateException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException {
+ flush();
+ }
+
+ public XmlSerializer endTag(String namespace, String name) throws IOException,
+ IllegalArgumentException, IllegalStateException {
+ mNesting--;
+ if (mInTag) {
+ append(" />\n");
+ } else {
+ if (mIndent && mLineStart) {
+ appendIndent(mNesting);
+ }
+ append("</");
+ if (namespace != null) {
+ append(namespace);
+ append(':');
+ }
+ append(name);
+ append(">\n");
+ }
+ mLineStart = true;
+ mInTag = false;
+ return this;
+ }
+
+ public void entityRef(String text) throws IOException, IllegalArgumentException,
+ IllegalStateException {
+ throw new UnsupportedOperationException();
+ }
+
+ private void flushBytes() throws IOException {
+ int position;
+ if ((position = mBytes.position()) > 0) {
+ mBytes.flip();
+ mOutputStream.write(mBytes.array(), 0, position);
+ mBytes.clear();
+ }
+ }
+
+ public void flush() throws IOException {
+ //Log.i("PackageManager", "flush mPos=" + mPos);
+ if (mPos > 0) {
+ if (mOutputStream != null) {
+ CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
+ CoderResult result = mCharset.encode(charBuffer, mBytes, true);
+ while (true) {
+ if (result.isError()) {
+ throw new IOException(result.toString());
+ } else if (result.isOverflow()) {
+ flushBytes();
+ result = mCharset.encode(charBuffer, mBytes, true);
+ continue;
+ }
+ break;
+ }
+ flushBytes();
+ mOutputStream.flush();
+ } else {
+ mWriter.write(mText, 0, mPos);
+ mWriter.flush();
+ }
+ mPos = 0;
+ }
+ }
+
+ public int getDepth() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean getFeature(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getName() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getNamespace() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getPrefix(String namespace, boolean generatePrefix)
+ throws IllegalArgumentException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object getProperty(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException,
+ IllegalStateException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void processingInstruction(String text) throws IOException, IllegalArgumentException,
+ IllegalStateException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setFeature(String name, boolean state) throws IllegalArgumentException,
+ IllegalStateException {
+ if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) {
+ mIndent = true;
+ return;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ public void setOutput(OutputStream os, String encoding) throws IOException,
+ IllegalArgumentException, IllegalStateException {
+ if (os == null)
+ throw new IllegalArgumentException();
+ if (true) {
+ try {
+ mCharset = Charset.forName(encoding).newEncoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE);
+ } catch (IllegalCharsetNameException e) {
+ throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
+ encoding).initCause(e));
+ } catch (UnsupportedCharsetException e) {
+ throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
+ encoding).initCause(e));
+ }
+ mOutputStream = os;
+ } else {
+ setOutput(
+ encoding == null
+ ? new OutputStreamWriter(os)
+ : new OutputStreamWriter(os, encoding));
+ }
+ }
+
+ public void setOutput(Writer writer) throws IOException, IllegalArgumentException,
+ IllegalStateException {
+ mWriter = writer;
+ }
+
+ public void setPrefix(String prefix, String namespace) throws IOException,
+ IllegalArgumentException, IllegalStateException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setProperty(String name, Object value) throws IllegalArgumentException,
+ IllegalStateException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void startDocument(String encoding, Boolean standalone) throws IOException,
+ IllegalArgumentException, IllegalStateException {
+ append("<?xml version='1.0' encoding='utf-8'");
+ if (standalone != null) {
+ append(" standalone='" + (standalone ? "yes" : "no") + "'");
+ }
+ append(" ?>\n");
+ mLineStart = true;
+ }
+
+ public XmlSerializer startTag(String namespace, String name) throws IOException,
+ IllegalArgumentException, IllegalStateException {
+ if (mInTag) {
+ append(">\n");
+ }
+ if (mIndent) {
+ appendIndent(mNesting);
+ }
+ mNesting++;
+ append('<');
+ if (namespace != null) {
+ append(namespace);
+ append(':');
+ }
+ append(name);
+ mInTag = true;
+ mLineStart = false;
+ return this;
+ }
+
+ public XmlSerializer text(char[] buf, int start, int len) throws IOException,
+ IllegalArgumentException, IllegalStateException {
+ if (mInTag) {
+ append(">");
+ mInTag = false;
+ }
+ escapeAndAppendString(buf, start, len);
+ if (mIndent) {
+ mLineStart = buf[start+len-1] == '\n';
+ }
+ return this;
+ }
+
+ public XmlSerializer text(String text) throws IOException, IllegalArgumentException,
+ IllegalStateException {
+ if (mInTag) {
+ append(">");
+ mInTag = false;
+ }
+ escapeAndAppendString(text);
+ if (mIndent) {
+ mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n');
+ }
+ return this;
+ }
+
+}
diff --git a/java/com/android/modules/expresslog/Android.bp b/java/com/android/modules/expresslog/Android.bp
index cacc7f8..59504bd 100644
--- a/java/com/android/modules/expresslog/Android.bp
+++ b/java/com/android/modules/expresslog/Android.bp
@@ -28,12 +28,16 @@ java_library {
libs: [
"framework-statsd",
],
+ static_libs: [
+ "expresslog-catalog",
+ ],
}
genrule {
name: "statslog-expresslog-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module expresslog" +
- " --javaPackage com.android.modules.expresslog --javaClass StatsExpressLog",
+ " --javaPackage com.android.modules.expresslog" +
+ " --javaClass StatsExpressLog",
out: ["com/android/modules/expresslog/StatsExpressLog.java"],
}
diff --git a/java/com/android/modules/expresslog/Counter.java b/java/com/android/modules/expresslog/Counter.java
index b788c3f..bcacb8b 100644
--- a/java/com/android/modules/expresslog/Counter.java
+++ b/java/com/android/modules/expresslog/Counter.java
@@ -18,8 +18,6 @@ package com.android.modules.expresslog;
import android.annotation.NonNull;
-import com.android.modules.expresslog.StatsExpressLog;
-
/** Counter encapsulates StatsD write API calls */
public final class Counter {
@@ -49,7 +47,8 @@ public final class Counter {
* @param amount to increment counter
*/
public static void logIncrement(@NonNull String metricId, long amount) {
- final long metricIdHash = Utils.hashString(metricId);
+ final long metricIdHash =
+ MetricIds.getMetricIdHash(metricId, MetricIds.METRIC_TYPE_COUNTER);
StatsExpressLog.write(StatsExpressLog.EXPRESS_EVENT_REPORTED, metricIdHash, amount);
}
@@ -60,7 +59,8 @@ public final class Counter {
* @param amount to increment counter
*/
public static void logIncrementWithUid(@NonNull String metricId, int uid, long amount) {
- final long metricIdHash = Utils.hashString(metricId);
+ final long metricIdHash =
+ MetricIds.getMetricIdHash(metricId, MetricIds.METRIC_TYPE_COUNTER_WITH_UID);
StatsExpressLog.write(
StatsExpressLog.EXPRESS_UID_EVENT_REPORTED, metricIdHash, amount, uid);
}
diff --git a/java/com/android/modules/expresslog/Histogram.java b/java/com/android/modules/expresslog/Histogram.java
index be300bf..4f61c85 100644
--- a/java/com/android/modules/expresslog/Histogram.java
+++ b/java/com/android/modules/expresslog/Histogram.java
@@ -20,14 +20,12 @@ import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
-import com.android.modules.expresslog.StatsExpressLog;
-
import java.util.Arrays;
/** Histogram encapsulates StatsD write API calls */
public final class Histogram {
- private final long mMetricIdHash;
+ private final String mMetricId;
private final BinOptions mBinOptions;
/**
@@ -37,7 +35,7 @@ public final class Histogram {
* @param binOptions to calculate bin index for samples
*/
public Histogram(@NonNull String metricId, @NonNull BinOptions binOptions) {
- mMetricIdHash = Utils.hashString(metricId);
+ mMetricId = metricId;
mBinOptions = binOptions;
}
@@ -47,9 +45,10 @@ public final class Histogram {
* @param sample value
*/
public void logSample(float sample) {
+ final long hash = MetricIds.getMetricIdHash(mMetricId, MetricIds.METRIC_TYPE_HISTOGRAM);
final int binIndex = mBinOptions.getBinForSample(sample);
- StatsExpressLog.write(StatsExpressLog.EXPRESS_HISTOGRAM_SAMPLE_REPORTED, mMetricIdHash,
- /*count*/ 1, binIndex);
+ StatsExpressLog.write(
+ StatsExpressLog.EXPRESS_HISTOGRAM_SAMPLE_REPORTED, hash, /*count*/ 1, binIndex);
}
/**
@@ -59,9 +58,15 @@ public final class Histogram {
* @param sample value
*/
public void logSampleWithUid(int uid, float sample) {
+ final long hash =
+ MetricIds.getMetricIdHash(mMetricId, MetricIds.METRIC_TYPE_HISTOGRAM_WITH_UID);
final int binIndex = mBinOptions.getBinForSample(sample);
- StatsExpressLog.write(StatsExpressLog.EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED,
- mMetricIdHash, /*count*/ 1, binIndex, uid);
+ StatsExpressLog.write(
+ StatsExpressLog.EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED,
+ hash, /*count*/
+ 1,
+ binIndex,
+ uid);
}
/** Used by Histogram to map data sample to corresponding bin */
diff --git a/java/com/android/modules/expresslog/Utils.java b/java/com/android/modules/expresslog/Utils.java
deleted file mode 100644
index fde90fc..0000000
--- a/java/com/android/modules/expresslog/Utils.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2023 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.modules.expresslog;
-
-final class Utils {
- static native long hashString(String stringToHash);
-}
diff --git a/java/com/android/modules/utils/FastDataInput.java b/java/com/android/modules/utils/FastDataInput.java
index daa86d5..1437f80 100644
--- a/java/com/android/modules/utils/FastDataInput.java
+++ b/java/com/android/modules/utils/FastDataInput.java
@@ -207,6 +207,10 @@ public class FastDataInput implements DataInput, Closeable {
return s;
} else {
+ if (ref >= mStringRefs.length) {
+ throw new IOException("Invalid interned string reference " + ref + " for "
+ + mStringRefs.length + " interned strings");
+ }
return mStringRefs[ref];
}
}
diff --git a/java/com/android/modules/utils/build/UnboundedSdkLevel.java b/java/com/android/modules/utils/build/UnboundedSdkLevel.java
index 48185d5..cc84172 100644
--- a/java/com/android/modules/utils/build/UnboundedSdkLevel.java
+++ b/java/com/android/modules/utils/build/UnboundedSdkLevel.java
@@ -17,6 +17,7 @@
package com.android.modules.utils.build;
import android.os.Build;
+import android.util.ArraySet;
import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -50,10 +51,22 @@ public final class UnboundedSdkLevel {
private static final SparseArray<Set<String>> PREVIOUS_CODENAMES = new SparseArray<>(4);
static {
- PREVIOUS_CODENAMES.put(29, Set.of("Q"));
- PREVIOUS_CODENAMES.put(30, Set.of("Q", "R"));
- PREVIOUS_CODENAMES.put(31, Set.of("Q", "R", "S"));
- PREVIOUS_CODENAMES.put(32, Set.of("Q", "R", "S", "Sv2"));
+ PREVIOUS_CODENAMES.put(29, setOf("Q"));
+ PREVIOUS_CODENAMES.put(30, setOf("Q", "R"));
+ PREVIOUS_CODENAMES.put(31, setOf("Q", "R", "S"));
+ PREVIOUS_CODENAMES.put(32, setOf("Q", "R", "S", "Sv2"));
+ }
+
+ private static Set<String> setOf(String ... contents) {
+ if (SdkLevel.isAtLeastR()) {
+ return Set.of(contents);
+ }
+ // legacy code for Q
+ Set<String> set = new ArraySet(contents.length);
+ for (String codename : contents) {
+ set.add(codename);
+ }
+ return set;
}
private static final UnboundedSdkLevel sInstance =
diff --git a/java/com/android/modules/utils/testing/AbstractExtendedMockitoRule.java b/java/com/android/modules/utils/testing/AbstractExtendedMockitoRule.java
index 837c43b..2242ca0 100644
--- a/java/com/android/modules/utils/testing/AbstractExtendedMockitoRule.java
+++ b/java/com/android/modules/utils/testing/AbstractExtendedMockitoRule.java
@@ -22,12 +22,13 @@ import android.util.Log;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import com.android.modules.utils.testing.AbstractExtendedMockitoRule.AbstractBuilder;
+import com.android.modules.utils.testing.ExtendedMockitoRule.MockStatic;
+import com.android.modules.utils.testing.ExtendedMockitoRule.MockStaticClasses;
+import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic;
+import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStaticClasses;
-import org.junit.AssumptionViolatedException;
import org.junit.rules.TestRule;
-import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.Mockito;
@@ -35,11 +36,21 @@ import org.mockito.MockitoFramework;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -52,14 +63,20 @@ public abstract class AbstractExtendedMockitoRule<R extends AbstractExtendedMock
private static final String TAG = AbstractExtendedMockitoRule.class.getSimpleName();
+ private static final AnnotationFetcher<SpyStatic, SpyStaticClasses>
+ sSpyStaticAnnotationFetcher = new AnnotationFetcher<>(SpyStatic.class,
+ SpyStaticClasses.class, r -> r.value());
+ private static final AnnotationFetcher<MockStatic, MockStaticClasses>
+ sMockStaticAnnotationFetcher = new AnnotationFetcher<>(MockStatic.class,
+ MockStaticClasses.class, r -> r.value());
+
private final Object mTestClassInstance;
private final Strictness mStrictness;
private @Nullable final MockitoFramework mMockitoFramework;
private @Nullable final Runnable mAfterSessionFinishedCallback;
- private final List<Class<?>> mMockedStaticClasses;
- private final List<Class<?>> mSpiedStaticClasses;
+ private final Set<Class<?>> mMockedStaticClasses;
+ private final Set<Class<?>> mSpiedStaticClasses;
private final List<StaticMockFixture> mStaticMockFixtures;
- private final @Nullable SessionBuilderVisitor mSessionBuilderConfigurator;
private final boolean mClearInlineMocks;
private MockitoSession mMockitoSession;
@@ -70,7 +87,6 @@ public abstract class AbstractExtendedMockitoRule<R extends AbstractExtendedMock
mMockitoFramework = builder.mMockitoFramework;
mMockitoSession = builder.mMockitoSession;
mAfterSessionFinishedCallback = builder.mAfterSessionFinishedCallback;
- mSessionBuilderConfigurator = builder.mSessionBuilderConfigurator;
mMockedStaticClasses = builder.mMockedStaticClasses;
mSpiedStaticClasses = builder.mSpiedStaticClasses;
mStaticMockFixtures = builder.mStaticMockFixtures == null ? Collections.emptyList()
@@ -80,40 +96,92 @@ public abstract class AbstractExtendedMockitoRule<R extends AbstractExtendedMock
+ ", mockedStaticClasses=" + mMockedStaticClasses
+ ", spiedStaticClasses=" + mSpiedStaticClasses
+ ", staticMockFixtures=" + mStaticMockFixtures
- + ", sessionBuilderConfigurator=" + mSessionBuilderConfigurator
+ ", afterSessionFinishedCallback=" + mAfterSessionFinishedCallback
+ ", mockitoFramework=" + mMockitoFramework
+ ", mockitoSession=" + mMockitoSession
+ ", clearInlineMocks=" + mClearInlineMocks);
}
- @Override
- public Statement apply(Statement base, Description description) {
- createMockitoSession(description);
+ /**
+ * Gets the mocked static classes present in the given test.
+ *
+ * <p>By default, it returns the classes defined by {@link AbstractBuilder#mockStatic(Class)}
+ * plus the classes present in the {@link MockStatic} and {@link MockStaticClasses}
+ * annotations (presents in the test method, its class, or its superclasses).
+ */
+ protected Set<Class<?>> getMockedStaticClasses(Description description) {
+ Set<Class<?>> staticClasses = new HashSet<>(mMockedStaticClasses);
+ sMockStaticAnnotationFetcher.getAnnotations(description)
+ .forEach(a -> staticClasses.add(a.value()));
+ return Collections.unmodifiableSet(staticClasses);
+ }
- return new TestWatcher() {
- @Override
- protected void succeeded(Description description) {
- tearDown(description, /* e=*/ null);
- }
+ /**
+ * Gets the spied static classes present in the given test.
+ *
+ * <p>By default, it returns the classes defined by {@link AbstractBuilder#spyStatic(Class)}
+ * plus the classes present in the {@link SpyStatic} and {@link SpyStaticClasses}
+ * annotations (presents in the test method, its class, or its superclasses).
+ */
+ protected Set<Class<?>> getSpiedStaticClasses(Description description) {
+ Set<Class<?>> staticClasses = new HashSet<>(mSpiedStaticClasses);
+ sSpyStaticAnnotationFetcher.getAnnotations(description)
+ .forEach(a -> staticClasses.add(a.value()));
+ return Collections.unmodifiableSet(staticClasses);
+ }
- @Override
- protected void skipped(AssumptionViolatedException e, Description description) {
- tearDown(description, e);
- }
+ /**
+ * Gets whether the rule should clear the inline mocks after the given test.
+ *
+ * <p>By default, it returns {@code} (unless the rule was built with
+ * {@link AbstractBuilder#dontClearInlineMocks()}, but subclasses can override to change the
+ * behavior (for example, to decide based on custom annotations).
+ */
+ protected boolean getClearInlineMethodsAtTheEnd(Description description) {
+ return mClearInlineMocks;
+ }
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
@Override
- protected void failed(Throwable e, Description description) {
- tearDown(description, e);
+ public void evaluate() throws Throwable {
+ createMockitoSession(base, description);
+ Throwable error = null;
+ try {
+ // TODO(b/296937563): need to add unit tests that make sure the session is
+ // always closed
+ base.evaluate();
+ } catch (Throwable t) {
+ error = t;
+ }
+ try {
+ tearDown(description, error);
+ } catch (Throwable t) {
+ if (error != null) {
+ Log.e(TAG, "Teardown failed for " + description.getDisplayName()
+ + ", but not throwing it because test also threw (" + error + ")", t);
+ } else {
+ error = t;
+ }
+ }
+ if (error != null) {
+ // TODO(b/296937563): ideally should also add unit tests to make sure the
+ // test error is thrown (in case tearDown() above fails)
+ throw error;
+ }
}
- }.apply(base, description);
+ };
}
- private void createMockitoSession(Description description) {
+ private void createMockitoSession(Statement base, Description description) {
+ // TODO(b/296937563): might be prudent to save the session statically so it's explicitly
+ // closed in case it fails to be created again if for some reason it was not closed by us
+ // (although that should not happen)
Log.v(TAG, "Creating session builder with strictness " + mStrictness);
StaticMockitoSessionBuilder mSessionBuilder = mockitoSession().strictness(mStrictness);
- setUpMockedClasses(mSessionBuilder);
+ setUpMockedClasses(description, mSessionBuilder);
if (mTestClassInstance != null) {
Log.v(TAG, "Initializing mocks on " + description + " using " + mSessionBuilder);
@@ -132,25 +200,22 @@ public abstract class AbstractExtendedMockitoRule<R extends AbstractExtendedMock
setUpMockBehaviors();
}
- private void setUpMockedClasses(StaticMockitoSessionBuilder sessionBuilder) {
+ private void setUpMockedClasses(Description description,
+ StaticMockitoSessionBuilder sessionBuilder) {
if (!mStaticMockFixtures.isEmpty()) {
for (StaticMockFixture fixture : mStaticMockFixtures) {
Log.v(TAG, "Calling setUpMockedClasses(" + sessionBuilder + ") on " + fixture);
fixture.setUpMockedClasses(sessionBuilder);
}
}
- for (Class<?> clazz: mMockedStaticClasses) {
+ for (Class<?> clazz: getMockedStaticClasses(description)) {
Log.v(TAG, "Calling mockStatic() on " + clazz);
sessionBuilder.mockStatic(clazz);
}
- for (Class<?> clazz: mSpiedStaticClasses) {
+ for (Class<?> clazz: getSpiedStaticClasses(description)) {
Log.v(TAG, "Calling spyStatic() on " + clazz);
sessionBuilder.spyStatic(clazz);
}
- if (mSessionBuilderConfigurator != null) {
- Log.v(TAG, "Visiting " + mSessionBuilderConfigurator + " with " + sessionBuilder);
- mSessionBuilderConfigurator.visit(sessionBuilder);
- }
}
private void setUpMockBehaviors() {
@@ -183,12 +248,13 @@ public abstract class AbstractExtendedMockitoRule<R extends AbstractExtendedMock
}
}
} finally {
- clearInlineMocks();
+ clearInlineMocks(description);
}
}
- private void clearInlineMocks() {
- if (!mClearInlineMocks) {
+ private void clearInlineMocks(Description description) {
+ boolean clearIt = getClearInlineMethodsAtTheEnd(description);
+ if (!clearIt) {
Log.d(TAG, "NOT calling clearInlineMocks() as set on builder");
return;
}
@@ -208,13 +274,12 @@ public abstract class AbstractExtendedMockitoRule<R extends AbstractExtendedMock
public static abstract class AbstractBuilder<R extends
AbstractExtendedMockitoRule<R, B>, B extends AbstractBuilder<R, B>> {
final Object mTestClassInstance;
- final List<Class<?>> mMockedStaticClasses = new ArrayList<>();
- final List<Class<?>> mSpiedStaticClasses = new ArrayList<>();
+ final Set<Class<?>> mMockedStaticClasses = new HashSet<>();
+ final Set<Class<?>> mSpiedStaticClasses = new HashSet<>();
@Nullable List<StaticMockFixture> mStaticMockFixtures;
Strictness mStrictness = Strictness.LENIENT;
@Nullable MockitoFramework mMockitoFramework;
@Nullable MockitoSession mMockitoSession;
- @Nullable SessionBuilderVisitor mSessionBuilderConfigurator;
@Nullable Runnable mAfterSessionFinishedCallback;
boolean mClearInlineMocks = true;
@@ -248,11 +313,9 @@ public abstract class AbstractExtendedMockitoRule<R extends AbstractExtendedMock
* com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder#mockStatic(Class)}.
*
* @throws IllegalStateException if the same class was already passed to
- * {@link #mockStatic(Class)} or {@link #spyStatic(Class)} or if
- * {@link #configureSessionBuilder(SessionBuilderVisitor)} was called before.
+ * {@link #mockStatic(Class)} or {@link #spyStatic(Class)}.
*/
public final B mockStatic(Class<?> clazz) {
- checkConfigureSessionBuilderNotCalled();
mMockedStaticClasses.add(checkClassNotMockedOrSpied(clazz));
return thisBuilder();
}
@@ -262,11 +325,9 @@ public abstract class AbstractExtendedMockitoRule<R extends AbstractExtendedMock
* com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder#spyStatic(Class)}.
*
* @throws IllegalStateException if the same class was already passed to
- * {@link #mockStatic(Class)} or {@link #spyStatic(Class)} or if
- * {@link #configureSessionBuilder(SessionBuilderVisitor)} was called before.
+ * {@link #mockStatic(Class)} or {@link #spyStatic(Class)}.
*/
public final B spyStatic(Class<?> clazz) {
- checkConfigureSessionBuilderNotCalled();
mSpiedStaticClasses.add(checkClassNotMockedOrSpied(clazz));
return thisBuilder();
}
@@ -288,27 +349,6 @@ public abstract class AbstractExtendedMockitoRule<R extends AbstractExtendedMock
return thisBuilder();
}
- // TODO(b/281577492): remove once CachedAppOptimizerTest doesn't use anymore
- /**
- * Alternative for {@link #spyStatic(Class)} / {@link #mockStatic(Class)}; typically used
- * when the same setup is shared by multiple tests.
- *
- * @deprecated use {@link #addStaticMockFixtures(Supplier...)} instead
- *
- * @throws IllegalStateException if {@link #mockStatic(Class)} or {@link #spyStatic(Class)}
- * was called before.
- */
- @Deprecated
- public final B configureSessionBuilder(
- SessionBuilderVisitor sessionBuilderConfigurator) {
- Preconditions.checkState(mMockedStaticClasses.isEmpty(),
- "mockStatic() already called");
- Preconditions.checkState(mSpiedStaticClasses.isEmpty(),
- "spyStatic() already called");
- mSessionBuilderConfigurator = Objects.requireNonNull(sessionBuilderConfigurator);
- return thisBuilder();
- }
-
/**
* Runs the given {@code runnable} after the session finished.
*
@@ -354,29 +394,78 @@ public abstract class AbstractExtendedMockitoRule<R extends AbstractExtendedMock
return (B) this;
}
- private void checkConfigureSessionBuilderNotCalled() {
- Preconditions.checkState(mSessionBuilderConfigurator == null,
- "configureSessionBuilder() already called");
- }
-
private Class<?> checkClassNotMockedOrSpied(Class<?> clazz) {
Objects.requireNonNull(clazz);
- Preconditions.checkState(!mMockedStaticClasses.contains(clazz),
- "class %s already mocked", clazz);
- Preconditions.checkState(!mSpiedStaticClasses.contains(clazz),
- "class %s already spied", clazz);
+ checkState(!mMockedStaticClasses.contains(clazz), "class %s already mocked", clazz);
+ checkState(!mSpiedStaticClasses.contains(clazz), "class %s already spied", clazz);
return clazz;
}
}
- /**
- * Visitor for {@link StaticMockitoSessionBuilder}.
- */
- public interface SessionBuilderVisitor {
+ // Copied from com.android.internal.util.Preconditions, as that method is not available on RVC
+ private static void checkState(boolean expression, String messageTemplate,
+ Object... messageArgs) {
+ if (!expression) {
+ throw new IllegalStateException(String.format(messageTemplate, messageArgs));
+ }
+ }
- /**
- * Visits it.
- */
- void visit(StaticMockitoSessionBuilder builder);
+ // TODO: make it public so it can be used by other modules
+ private static final class AnnotationFetcher<A extends Annotation, R extends Annotation> {
+
+ private final Class<A> mAnnotationType;
+ private final Class<R> mRepeatableType;
+ private final Function<R, A[]> mConverter;
+
+ AnnotationFetcher(Class<A> annotationType, Class<R> repeatableType,
+ Function<R, A[]> converter) {
+ mAnnotationType = annotationType;
+ mRepeatableType = repeatableType;
+ mConverter = converter;
+ }
+
+ private void add(Set<A> allAnnotations, R repeatableAnnotation) {
+ A[] repeatedAnnotations = mConverter.apply(repeatableAnnotation);
+ for (A repeatedAnnotation : repeatedAnnotations) {
+ allAnnotations.add(repeatedAnnotation);
+ }
+ }
+
+ Set<A> getAnnotations(Description description) {
+ Set<A> allAnnotations = new HashSet<>();
+
+ // Gets the annotations from the method first
+ Collection<Annotation> annotations = description.getAnnotations();
+ if (annotations != null) {
+ for (Annotation annotation : annotations) {
+ if (mAnnotationType.isInstance(annotation)) {
+ allAnnotations.add(mAnnotationType.cast(annotation));
+ }
+ if (mRepeatableType.isInstance(annotation)) {
+ add(allAnnotations, mRepeatableType.cast(annotation));
+ }
+ }
+ }
+
+ // Then superclasses
+ Class<?> clazz = description.getTestClass();
+ do {
+ A[] repeatedAnnotations = clazz.getAnnotationsByType(mAnnotationType);
+ if (repeatedAnnotations != null) {
+ for (A repeatedAnnotation : repeatedAnnotations) {
+ allAnnotations.add(repeatedAnnotation);
+ }
+ }
+ R[] repeatableAnnotations = clazz.getAnnotationsByType(mRepeatableType);
+ if (repeatableAnnotations != null) {
+ for (R repeatableAnnotation : repeatableAnnotations) {
+ add(allAnnotations, mRepeatableType.cast(repeatableAnnotation));
+ }
+ }
+ clazz = clazz.getSuperclass();
+ } while (clazz != null);
+
+ return allAnnotations;
+ }
}
}
diff --git a/java/com/android/modules/utils/testing/ExtendedMockitoRule.java b/java/com/android/modules/utils/testing/ExtendedMockitoRule.java
index 8eeaddd..fa7d7fa 100644
--- a/java/com/android/modules/utils/testing/ExtendedMockitoRule.java
+++ b/java/com/android/modules/utils/testing/ExtendedMockitoRule.java
@@ -18,6 +18,12 @@ package com.android.modules.utils.testing;
import com.android.modules.utils.testing.AbstractExtendedMockitoRule.AbstractBuilder;
import com.android.modules.utils.testing.ExtendedMockitoRule.Builder;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
/**
* Rule to make it easier to use Extended Mockito:
*
@@ -71,4 +77,30 @@ public final class ExtendedMockitoRule extends
return new ExtendedMockitoRule(this);
}
}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD, ElementType.TYPE})
+ @Repeatable(SpyStaticClasses.class)
+ public @interface SpyStatic {
+ Class<?> value();
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD, ElementType.TYPE})
+ public @interface SpyStaticClasses {
+ SpyStatic[] value();
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD, ElementType.TYPE})
+ @Repeatable(MockStaticClasses.class)
+ public @interface MockStatic {
+ Class<?> value();
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD, ElementType.TYPE})
+ public @interface MockStaticClasses {
+ MockStatic[] value();
+ }
}
diff --git a/javatests/com/android/modules/expresslog/Android.bp b/javatests/com/android/modules/expresslog/Android.bp
index dd52750..9396e17 100644
--- a/javatests/com/android/modules/expresslog/Android.bp
+++ b/javatests/com/android/modules/expresslog/Android.bp
@@ -37,40 +37,7 @@ android_test {
"android.test.runner",
],
- jni_libs: [
- "libexpresslog_test_jni",
- ],
-
test_suites: [
"general-tests",
],
}
-
-cc_library_shared {
- name: "libexpresslog_test_jni",
-
- sdk_version: "current",
- min_sdk_version: "30",
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
-
- "-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash",
- ],
- srcs: [
- "jni/onload.cpp",
- ],
- header_libs: [
- "liblog_headers",
- "libnativehelper_header_only",
- ],
- shared_libs: [
- "liblog",
- ],
- static_libs: [
- "libexpresslog_jni",
- "libtextclassifier_hash_static",
- ],
-}
diff --git a/javatests/com/android/modules/expresslog/ScaledRangeOptionsTest.java b/javatests/com/android/modules/expresslog/ScaledRangeOptionsTest.java
index 8defce7..1c42788 100644
--- a/javatests/com/android/modules/expresslog/ScaledRangeOptionsTest.java
+++ b/javatests/com/android/modules/expresslog/ScaledRangeOptionsTest.java
@@ -27,10 +27,6 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
@SmallTest
public class ScaledRangeOptionsTest {
- static {
- System.loadLibrary("expresslog_test_jni");
- }
-
private static final String TAG = ScaledRangeOptionsTest.class.getSimpleName();
@Test
diff --git a/javatests/com/android/modules/expresslog/UniformOptionsTest.java b/javatests/com/android/modules/expresslog/UniformOptionsTest.java
index 3cc03ec..cad4c3f 100644
--- a/javatests/com/android/modules/expresslog/UniformOptionsTest.java
+++ b/javatests/com/android/modules/expresslog/UniformOptionsTest.java
@@ -26,10 +26,6 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
@SmallTest
public class UniformOptionsTest {
- static {
- System.loadLibrary("expresslog_test_jni");
- }
-
private static final String TAG = UniformOptionsTest.class.getSimpleName();
@Test
diff --git a/javatests/com/android/modules/expresslog/jni/.clang-format b/javatests/com/android/modules/expresslog/jni/.clang-format
deleted file mode 100644
index cead3a0..0000000
--- a/javatests/com/android/modules/expresslog/jni/.clang-format
+++ /dev/null
@@ -1,17 +0,0 @@
-BasedOnStyle: Google
-AllowShortIfStatementsOnASingleLine: true
-AllowShortFunctionsOnASingleLine: false
-AllowShortLoopsOnASingleLine: true
-BinPackArguments: true
-BinPackParameters: true
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-ContinuationIndentWidth: 8
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-AccessModifierOffset: -4
-IncludeCategories:
- - Regex: '^"Log\.h"'
- Priority: -1
diff --git a/javatests/com/android/modules/expresslog/jni/onload.cpp b/javatests/com/android/modules/expresslog/jni/onload.cpp
deleted file mode 100644
index a112467..0000000
--- a/javatests/com/android/modules/expresslog/jni/onload.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2023 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 "TeX"
-
-#include <jni.h>
-#include <log/log.h>
-
-namespace android {
-extern int register_com_android_modules_expresslog_Utils(JNIEnv* env);
-} // namespace android
-
-using namespace android;
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
- JNIEnv* env = NULL;
- jint result = -1;
-
- if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
- ALOGE("GetEnv failed!");
- return result;
- }
- ALOG_ASSERT(env, "Could not retrieve the env!");
-
- register_com_android_modules_expresslog_Utils(env);
- return JNI_VERSION_1_4;
-}
diff --git a/javatests/com/android/modules/utils/build/Android.bp b/javatests/com/android/modules/utils/build/Android.bp
index 239530a..06ceb34 100644
--- a/javatests/com/android/modules/utils/build/Android.bp
+++ b/javatests/com/android/modules/utils/build/Android.bp
@@ -25,7 +25,7 @@ android_test {
"androidx.test.ext.junit",
"androidx.test.runner",
"modules-utils-build",
- "truth-prebuilt",
+ "truth",
],
test_suites: ["general-tests"],
}
diff --git a/javatests/com/android/modules/utils/testing/Android.bp b/javatests/com/android/modules/utils/testing/Android.bp
index 418239a..5da571b 100644
--- a/javatests/com/android/modules/utils/testing/Android.bp
+++ b/javatests/com/android/modules/utils/testing/Android.bp
@@ -34,7 +34,7 @@ android_test {
"androidx.test.runner",
"androidx.test.rules",
"platform-test-annotations",
- "truth-prebuilt",
+ "truth",
],
libs: [
diff --git a/javatests/com/android/modules/utils/testing/ExtendedMockitoRuleTest.java b/javatests/com/android/modules/utils/testing/ExtendedMockitoRuleTest.java
index cb54f9f..26d0cc3 100644
--- a/javatests/com/android/modules/utils/testing/ExtendedMockitoRuleTest.java
+++ b/javatests/com/android/modules/utils/testing/ExtendedMockitoRuleTest.java
@@ -31,6 +31,9 @@ import android.util.Log;
import androidx.annotation.Nullable;
+import com.android.modules.utils.testing.ExtendedMockitoRule.MockStatic;
+import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic;
+
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
@@ -46,20 +49,27 @@ import org.mockito.listeners.MockitoListener;
import org.mockito.plugins.MockitoPlugins;
import org.mockito.quality.Strictness;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
import java.util.function.Supplier;
@RunWith(MockitoJUnitRunner.class)
public final class ExtendedMockitoRuleTest {
+
public static final String TAG = ExtendedMockitoRuleTest.class.getSimpleName();
+ // Not a real test (i.e., it doesn't exist on this class), but it's passed to Description
+ private static final String TEST_METHOD_BEING_EXECUTED = "testAmI..OrNot";
+
private @Mock Statement mStatement;
- private @Mock Description mDescription;
private @Mock Runnable mRunnable;
- private @Mock ExtendedMockitoRule.SessionBuilderVisitor mSessionBuilderVisitor;
private @Mock StaticMockFixture mStaticMockFixture1;
private @Mock StaticMockFixture mStaticMockFixture2;
private @Mock StaticMockFixture mStaticMockFixture3;
+ private final Description mDescription = newTestMethod();
private final ClassUnderTest mClassUnderTest = new ClassUnderTest();
private final ExtendedMockitoRule.Builder mBuilder =
new ExtendedMockitoRule.Builder(mClassUnderTest);
@@ -89,12 +99,6 @@ public final class ExtendedMockitoRuleTest {
}
@Test
- public void testBuilder_configureSessionBuilder_null() {
- assertThrows(NullPointerException.class,
- () -> mBuilder.configureSessionBuilder(null));
- }
-
- @Test
public void testBuilder_mockStatic_null() {
assertThrows(NullPointerException.class, () -> mBuilder.mockStatic(null));
}
@@ -164,7 +168,8 @@ public final class ExtendedMockitoRuleTest {
@Test
public void testMocksStatic() throws Throwable {
- mBuilder.mockStatic(StaticClass.class).build().apply(new Statement() {
+ ExtendedMockitoRule rule = mBuilder.mockStatic(StaticClass.class).build();
+ rule.apply(new Statement() {
@Override
public void evaluate() throws Throwable {
doReturn("mocko()").when(() -> StaticClass.marco());
@@ -175,6 +180,12 @@ public final class ExtendedMockitoRuleTest {
.that(StaticClass.water()).isNull(); // not mocked
}
}, mDescription).evaluate();
+
+ Set<Class<?>> mockedClasses = rule.getMockedStaticClasses(mDescription);
+ assertWithMessage("rule.getMockedStaticClasses()").that(mockedClasses)
+ .containsExactly(StaticClass.class);
+ assertThrows(RuntimeException.class,
+ () -> mockedClasses.add(ExtendedMockitoRuleTest.class));
}
@Test
@@ -186,29 +197,79 @@ public final class ExtendedMockitoRuleTest {
@Test
public void testMocksStatic_multipleClasses() throws Throwable {
- mBuilder.mockStatic(StaticClass.class).mockStatic(AnotherStaticClass.class).build().apply(
- new Statement() {
- @Override
- public void evaluate() throws Throwable {
- doReturn("mocko()").when(() -> StaticClass.marco());
- doReturn("MOCKO()").when(() -> AnotherStaticClass.marco());
+ ExtendedMockitoRule rule = mBuilder.mockStatic(StaticClass.class)
+ .mockStatic(AnotherStaticClass.class).build();
+ rule.apply(new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ doReturn("mocko()").when(() -> StaticClass.marco());
+ doReturn("MOCKO()").when(() -> AnotherStaticClass.marco());
- assertWithMessage("StaticClass.marco()")
- .that(StaticClass.marco()).isEqualTo("mocko()");
- assertWithMessage("StaticClass.water()")
- .that(StaticClass.water()).isNull(); // not mocked
+ assertWithMessage("StaticClass.marco()")
+ .that(StaticClass.marco()).isEqualTo("mocko()");
+ assertWithMessage("StaticClass.water()")
+ .that(StaticClass.water()).isNull(); // not mocked
- assertWithMessage("AnotherStaticClass.marco()")
- .that(AnotherStaticClass.marco()).isEqualTo("MOCKO()");
- assertWithMessage("AnotherStaticClass.water()")
- .that(AnotherStaticClass.water()).isNull(); // not mocked
- }
- }, mDescription).evaluate();
+ assertWithMessage("AnotherStaticClass.marco()")
+ .that(AnotherStaticClass.marco()).isEqualTo("MOCKO()");
+ assertWithMessage("AnotherStaticClass.water()")
+ .that(AnotherStaticClass.water()).isNull(); // not mocked
+ }
+ }, mDescription).evaluate();
+
+ Set<Class<?>> mockedClasses = rule.getMockedStaticClasses(mDescription);
+ assertWithMessage("rule.getMockedStaticClasses()").that(mockedClasses)
+ .containsExactly(StaticClass.class, AnotherStaticClass.class);
+ assertThrows(RuntimeException.class,
+ () -> mockedClasses.add(ExtendedMockitoRuleTest.class));
+ }
+
+ @Test
+ public void testMockStatic_ruleAndAnnotation() throws Throwable {
+ ExtendedMockitoRule rule = mBuilder.mockStatic(StaticClass.class).build();
+
+ rule.apply(new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ doReturn("mocko()").when(() -> StaticClass.marco());
+ doReturn("MOCKO()").when(() -> AnotherStaticClass.marco());
+
+ assertWithMessage("StaticClass.marco()")
+ .that(StaticClass.marco()).isEqualTo("mocko()");
+ assertWithMessage("StaticClass.water()")
+ .that(StaticClass.water()).isNull(); // not mocked
+
+ assertWithMessage("AnotherStaticClass.marco()")
+ .that(AnotherStaticClass.marco()).isEqualTo("MOCKO()");
+ assertWithMessage("AnotherStaticClass.water()")
+ .that(AnotherStaticClass.water()).isNull(); // not mocked
+ }
+ }, newTestMethod(new MockStaticAnnotation(AnotherStaticClass.class))).evaluate();
+ }
+
+ // Ideally, we should test the annotations indirectly (i.e., by asserting their static classes
+ // are properly mocked, but pragmatically speaking, testing the getSpiedStatic() is enough - and
+ // much simpler
+ @Test
+ public void testMockStatic_fromEverywhere() throws Throwable {
+ ExtendedMockitoRule rule = mBuilder.mockStatic(StaticClass.class).build();
+
+ Set<Class<?>> mockedClasses = rule.getMockedStaticClasses(newTestMethod(SubClass.class,
+ new MockStaticAnnotation(AnotherStaticClass.class)));
+
+ assertWithMessage("rule.getMockedStaticClasses()").that(mockedClasses).containsExactly(
+ StaticClass.class, AnotherStaticClass.class, StaticClassMockedBySuperClass.class,
+ AnotherStaticClassMockedBySuperClass.class, StaticClassMockedBySubClass.class,
+ AnotherStaticClassMockedBySubClass.class);
+ assertThrows(RuntimeException.class,
+ () -> mockedClasses.add(ExtendedMockitoRuleTest.class));
}
@Test
public void testSpyStatic() throws Throwable {
- mBuilder.spyStatic(StaticClass.class).build().apply(new Statement() {
+ ExtendedMockitoRule rule = mBuilder.spyStatic(StaticClass.class).build();
+
+ rule.apply(new Statement() {
@Override
public void evaluate() throws Throwable {
doReturn("mocko()").when(() -> StaticClass.marco());
@@ -219,6 +280,11 @@ public final class ExtendedMockitoRuleTest {
.that(StaticClass.water()).isEqualTo("polo");
}
}, mDescription).evaluate();
+
+ Set<Class<?>> spiedClasses = rule.getSpiedStaticClasses(mDescription);
+ assertWithMessage("rule.getSpiedStaticClasses()").that(spiedClasses)
+ .containsExactly(StaticClass.class);
+ assertThrows(RuntimeException.class, () -> spiedClasses.add(ExtendedMockitoRuleTest.class));
}
@Test
@@ -230,8 +296,10 @@ public final class ExtendedMockitoRuleTest {
@Test
public void testSpyStatic_multipleClasses() throws Throwable {
- mBuilder.spyStatic(StaticClass.class).spyStatic(AnotherStaticClass.class).build()
- .apply(new Statement() {
+ ExtendedMockitoRule rule = mBuilder.spyStatic(StaticClass.class)
+ .spyStatic(AnotherStaticClass.class).build();
+
+ rule.apply(new Statement() {
@Override
public void evaluate() throws Throwable {
doReturn("mocko()").when(() -> StaticClass.marco());
@@ -248,12 +316,58 @@ public final class ExtendedMockitoRuleTest {
.that(AnotherStaticClass.water()).isEqualTo("POLO");
}
}, mDescription).evaluate();
+
+ Set<Class<?>> spiedClasses = rule.getSpiedStaticClasses(mDescription);
+ assertWithMessage("rule.getSpiedStaticClasses()").that(spiedClasses)
+ .containsExactly(StaticClass.class, AnotherStaticClass.class);
+ assertThrows(RuntimeException.class, () -> spiedClasses.add(ExtendedMockitoRuleTest.class));
+ }
+
+ @Test
+ public void testSpyStatic_ruleAndAnnotation() throws Throwable {
+ ExtendedMockitoRule rule = mBuilder.spyStatic(StaticClass.class).build();
+ rule.apply(new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ doReturn("mocko()").when(() -> StaticClass.marco());
+ doReturn("MOCKO()").when(() -> AnotherStaticClass.marco());
+
+ assertWithMessage("StaticClass.marco()")
+ .that(StaticClass.marco()).isEqualTo("mocko()");
+ assertWithMessage("StaticClass.water()")
+ .that(StaticClass.water()).isEqualTo("polo");
+
+ assertWithMessage("AnotherStaticClass.marco()")
+ .that(AnotherStaticClass.marco()).isEqualTo("MOCKO()");
+ assertWithMessage("AnotherStaticClass.water()")
+ .that(AnotherStaticClass.water()).isEqualTo("POLO");
+ }
+ }, newTestMethod(new SpyStaticAnnotation(AnotherStaticClass.class))).evaluate();
+ }
+
+ // Ideally, we should test the annotations indirectly (i.e., by asserting their static classes
+ // are properly spied, but pragmatically speaking, testing the getSpiedStatic() is enough - and
+ // much simpler
+ @Test
+ public void testSpyStatic_fromEverywhere() throws Throwable {
+ ExtendedMockitoRule rule = mBuilder.spyStatic(StaticClass.class).build();
+
+ Set<Class<?>> spiedClasses = rule.getSpiedStaticClasses(newTestMethod(SubClass.class,
+ new SpyStaticAnnotation(AnotherStaticClass.class)));
+
+ assertWithMessage("rule.getSpiedStaticClasses()").that(spiedClasses).containsExactly(
+ StaticClass.class, AnotherStaticClass.class, StaticClassSpiedBySuperClass.class,
+ AnotherStaticClassSpiedBySuperClass.class, StaticClassSpiedBySubClass.class,
+ AnotherStaticClassSpiedBySubClass.class);
+ assertThrows(RuntimeException.class, () -> spiedClasses.add(ExtendedMockitoRuleTest.class));
}
@Test
public void testMockAndSpyStatic() throws Throwable {
- mBuilder.mockStatic(StaticClass.class).spyStatic(AnotherStaticClass.class).build()
- .apply(new Statement() {
+ ExtendedMockitoRule rule = mBuilder.mockStatic(StaticClass.class)
+ .spyStatic(AnotherStaticClass.class).build();
+
+ rule.apply(new Statement() {
@Override
public void evaluate() throws Throwable {
doReturn("mocko()").when(() -> StaticClass.marco());
@@ -270,6 +384,18 @@ public final class ExtendedMockitoRuleTest {
.that(AnotherStaticClass.water()).isEqualTo("POLO");
}
}, mDescription).evaluate();
+
+ Set<Class<?>> spiedStaticClasses = rule.getSpiedStaticClasses(mDescription);
+ assertWithMessage("rule.getSpiedStaticClasses()").that(spiedStaticClasses)
+ .containsExactly(AnotherStaticClass.class);
+ assertThrows(RuntimeException.class,
+ () -> spiedStaticClasses.add(ExtendedMockitoRuleTest.class));
+
+ Set<Class<?>> mockedStaticClasses = rule.getMockedStaticClasses(mDescription);
+ assertWithMessage("rule.getMockedStaticClasses()").that(mockedStaticClasses)
+ .containsExactly(StaticClass.class);
+ assertThrows(RuntimeException.class,
+ () -> mockedStaticClasses.add(ExtendedMockitoRuleTest.class));
}
@Test
@@ -287,18 +413,6 @@ public final class ExtendedMockitoRuleTest {
}
@Test
- public void testSpyStatic_afterConfigureSessionBuilder() throws Throwable {
- assertThrows(IllegalStateException.class, () -> mBuilder
- .configureSessionBuilder(mSessionBuilderVisitor).spyStatic(StaticClass.class));
- }
-
- @Test
- public void testMockStatic_afterConfigureSessionBuilder() throws Throwable {
- assertThrows(IllegalStateException.class, () -> mBuilder
- .configureSessionBuilder(mSessionBuilderVisitor).mockStatic(StaticClass.class));
- }
-
- @Test
public void testAddStaticMockFixtures_once() throws Throwable {
InOrder inOrder = inOrder(mStaticMockFixture1, mStaticMockFixture2);
@@ -382,26 +496,6 @@ public final class ExtendedMockitoRuleTest {
}
@Test
- public void testConfigureSessionBuilder_afterMockStatic() throws Throwable {
- assertThrows(IllegalStateException.class, () -> mBuilder.mockStatic(StaticClass.class)
- .configureSessionBuilder(mSessionBuilderVisitor));
- }
-
- @Test
- public void testConfigureSessionBuilder_afterSpyStatic() throws Throwable {
- assertThrows(IllegalStateException.class, () -> mBuilder.spyStatic(StaticClass.class)
- .configureSessionBuilder(mSessionBuilderVisitor));
- }
-
- @Test
- public void testConfigureSessionBuilder() throws Throwable {
- mUnsafeBuilder.configureSessionBuilder(mSessionBuilderVisitor)
- .build().apply(mStatement, mDescription).evaluate();
-
- verify(mSessionBuilderVisitor).visit(notNull());
- }
-
- @Test
public void testAfterSessionFinished() throws Throwable {
mUnsafeBuilder.afterSessionFinished(mRunnable).build().apply(mStatement, mDescription)
.evaluate();
@@ -506,6 +600,16 @@ public final class ExtendedMockitoRuleTest {
assertWithMessage("mockito framework cleared").that(mockitoFramework.called).isTrue();
}
+ @Test
+ public void testGetClearInlineMethodsAtTheEnd() throws Throwable {
+ assertWithMessage("getClearInlineMethodsAtTheEnd() by default")
+ .that(mBuilder.build().getClearInlineMethodsAtTheEnd(mDescription)).isTrue();
+ assertWithMessage("getClearInlineMethodsAtTheEnd() when built with dontClearInlineMocks()")
+ .that(mBuilder.dontClearInlineMocks().build()
+ .getClearInlineMethodsAtTheEnd(mDescription))
+ .isFalse();
+ }
+
private void applyRuleOnTestThatDoesntUseExpectation(@Nullable Strictness strictness)
throws Throwable {
Log.d(TAG, "applyRuleOnTestThatDoesntUseExpectation(): strictness= " + strictness);
@@ -520,6 +624,15 @@ public final class ExtendedMockitoRuleTest {
}, mDescription).evaluate();
}
+ private static Description newTestMethod(Annotation... annotations) {
+ return newTestMethod(ClassUnderTest.class, annotations);
+ }
+
+ private static Description newTestMethod(Class<?> testClass, Annotation... annotations) {
+ return Description.createTestDescription(testClass, TEST_METHOD_BEING_EXECUTED,
+ annotations);
+ }
+
private static final class ClassUnderTest {
@Mock
public DumbObject mMock;
@@ -622,4 +735,103 @@ public final class ExtendedMockitoRuleTest {
throw e;
}
}
+
+ private abstract static class ClassAnnotation<A extends Annotation> implements Annotation {
+ private Class<A> mAnnotationType;
+ private Class<?> mClass;
+
+ private ClassAnnotation(Class<A> annotationType, Class<?> clazz) {
+ mAnnotationType = annotationType;
+ mClass = clazz;
+ }
+
+ @Override
+ public final Class<A> annotationType() {
+ return mAnnotationType;
+ }
+
+ public final Class<?> value() {
+ return mClass;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAnnotationType, mClass);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ClassAnnotation other = (ClassAnnotation) obj;
+ return Objects.equals(mAnnotationType, other.mAnnotationType)
+ && Objects.equals(mClass, other.mClass);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + mClass.getSimpleName() + "]";
+ }
+ }
+
+ private static final class SpyStaticAnnotation extends ClassAnnotation<SpyStatic>
+ implements SpyStatic {
+
+ private SpyStaticAnnotation(Class<?> clazz) {
+ super(SpyStatic.class, clazz);
+ }
+ }
+
+ private static final class MockStaticAnnotation extends ClassAnnotation<MockStatic>
+ implements MockStatic {
+
+ private MockStaticAnnotation(Class<?> clazz) {
+ super(MockStatic.class, clazz);
+ }
+ }
+
+ private static final class StaticClassMockedBySuperClass {
+ }
+
+ private static final class AnotherStaticClassMockedBySuperClass {
+ }
+ private static final class StaticClassSpiedBySuperClass {
+ }
+
+ private static final class AnotherStaticClassSpiedBySuperClass {
+ }
+
+ @SpyStatic(StaticClassSpiedBySuperClass.class)
+ @SpyStatic(AnotherStaticClassSpiedBySuperClass.class)
+ @MockStatic(StaticClassMockedBySuperClass.class)
+ @MockStatic(AnotherStaticClassMockedBySuperClass.class)
+ private static class SuperClass {
+
+ }
+
+ private static final class StaticClassMockedBySubClass {
+ }
+
+ private static final class AnotherStaticClassMockedBySubClass {
+ }
+
+ private static final class StaticClassSpiedBySubClass {
+ }
+
+ private static final class AnotherStaticClassSpiedBySubClass {
+ }
+
+ @SpyStatic(StaticClassSpiedBySubClass.class)
+ @SpyStatic(AnotherStaticClassSpiedBySubClass.class)
+ @MockStatic(StaticClassMockedBySubClass.class)
+ @MockStatic(AnotherStaticClassMockedBySubClass.class)
+ private static final class SubClass extends SuperClass{
+ }
} \ No newline at end of file
diff --git a/jni/expresslog/.clang-format b/jni/expresslog/.clang-format
deleted file mode 100644
index cead3a0..0000000
--- a/jni/expresslog/.clang-format
+++ /dev/null
@@ -1,17 +0,0 @@
-BasedOnStyle: Google
-AllowShortIfStatementsOnASingleLine: true
-AllowShortFunctionsOnASingleLine: false
-AllowShortLoopsOnASingleLine: true
-BinPackArguments: true
-BinPackParameters: true
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-ContinuationIndentWidth: 8
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-AccessModifierOffset: -4
-IncludeCategories:
- - Regex: '^"Log\.h"'
- Priority: -1
diff --git a/jni/expresslog/Android.bp b/jni/expresslog/Android.bp
deleted file mode 100644
index 7bef576..0000000
--- a/jni/expresslog/Android.bp
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// Copyright (C) 2023 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.
-
-// JNI library for Utils.hashString
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_static {
- name: "libexpresslog_jni",
-
- sdk_version: "current",
- min_sdk_version: "30",
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
-
- "-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash",
- ],
- srcs: [
- "com_android_modules_expresslog_Utils.cpp",
- ],
- header_libs: [
- "liblog_headers",
- "libnativehelper_header_only",
- "libtextclassifier_hash_headers",
- ],
- shared_libs: [
- "liblog",
- ],
- static_libs: [
- "libtextclassifier_hash_static",
- ],
- visibility: ["//visibility:public"],
- apex_available: [
- "//apex_available:anyapex",
- "//apex_available:platform",
- ],
-}
diff --git a/jni/expresslog/com_android_modules_expresslog_Utils.cpp b/jni/expresslog/com_android_modules_expresslog_Utils.cpp
deleted file mode 100644
index 973d946..0000000
--- a/jni/expresslog/com_android_modules_expresslog_Utils.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2023 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_NAMESPACE "TeX.tag."
-#define LOG_TAG "TeX"
-
-#include <log/log.h>
-#include <nativehelper/scoped_local_ref.h>
-#include <nativehelper/scoped_utf_chars.h>
-#include <utils/hash/farmhash.h>
-
-// ----------------------------------------------------------------------------
-// JNI Glue
-// ----------------------------------------------------------------------------
-
-static jclass gStringClass = nullptr;
-
-/**
- * Class: com_android_modules_expresslog_Utils
- * Method: hashString
- * Signature: (Ljava/lang/String;)J
- */
-static jlong hashString(JNIEnv* env, jclass /*class*/, jstring metricNameObj) {
- ScopedUtfChars name(env, metricNameObj);
- if (name.c_str() == nullptr) {
- return 0;
- }
-
- return static_cast<jlong>(farmhash::Fingerprint64(name.c_str(), name.size()));
-}
-
-static const JNINativeMethod gMethods[] = {
- {"hashString", "(Ljava/lang/String;)J", (void*)hashString},
-};
-
-namespace android {
-
-int register_com_android_modules_expresslog_Utils(JNIEnv* env) {
- static const char* const kUtilsClassName = "com/android/modules/expresslog/Utils";
- static const char* const kStringClassName = "java/lang/String";
-
- ScopedLocalRef<jclass> utilsCls(env, env->FindClass(kUtilsClassName));
- if (utilsCls.get() == nullptr) {
- ALOGE("jni expresslog registration failure, class not found '%s'", kUtilsClassName);
- return JNI_ERR;
- }
-
- jclass stringClass = env->FindClass(kStringClassName);
- if (stringClass == nullptr) {
- ALOGE("jni expresslog registration failure, class not found '%s'", kStringClassName);
- return JNI_ERR;
- }
- gStringClass = static_cast<jclass>(env->NewGlobalRef(stringClass));
- if (gStringClass == nullptr) {
- ALOGE("jni expresslog Unable to create global reference '%s'", kStringClassName);
- return JNI_ERR;
- }
-
- const jint count = sizeof(gMethods) / sizeof(gMethods[0]);
- int status = env->RegisterNatives(utilsCls.get(), gMethods, count);
- if (status < 0) {
- ALOGE("jni expresslog registration failure, status: %d", status);
- return JNI_ERR;
- }
- return JNI_VERSION_1_4;
-}
-
-} // namespace android