| /* |
| * Copyright (C) 2018 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 java.lang.invoke.WrongMethodTypeException; |
| |
| // Base class for VarHandle unit tests for accessor operations |
| public abstract class VarHandleUnitTest { |
| public static VarHandleUnitTestCollector DEFAULT_COLLECTOR = new VarHandleUnitTestCollector(); |
| |
| // Error log (lazily initialized on failure). |
| private StringBuilder lazyErrorLog = null; |
| |
| // Tracker of test events (starts, skips, ends) |
| private final VarHandleUnitTestCollector collector; |
| |
| public VarHandleUnitTest(VarHandleUnitTestCollector collector) { |
| this.collector = collector; |
| } |
| |
| public VarHandleUnitTest() { |
| this.collector = DEFAULT_COLLECTOR; |
| } |
| |
| // Method that can be overloaded to signify that a test should be |
| // run or skipped. Returns true if the test should be run and |
| // false if the test should be skipped. |
| public boolean checkGuard() { |
| return true; |
| } |
| |
| // Method that implementations should use to perform a specific test. |
| protected abstract void doTest() throws Exception; |
| |
| public final void assertTrue(boolean value) { |
| assertEquals(true, value); |
| } |
| |
| public final void assertFalse(boolean value) { |
| assertEquals(false, value); |
| } |
| |
| public final void assertEquals(boolean expected, boolean actual) { |
| assertEquals(Boolean.valueOf(expected), Boolean.valueOf(actual)); |
| } |
| |
| public final void assertEquals(byte expected, byte actual) { |
| assertEquals(Byte.valueOf(expected), Byte.valueOf(actual)); |
| } |
| |
| public final void assertEquals(char expected, char actual) { |
| assertEquals(Character.valueOf(expected), Character.valueOf(actual)); |
| } |
| |
| public final void assertEquals(short expected, short actual) { |
| assertEquals(Short.valueOf(expected), Short.valueOf(actual)); |
| } |
| |
| public final void assertEquals(int expected, int actual) { |
| assertEquals(Integer.valueOf(expected), Integer.valueOf(actual)); |
| } |
| |
| public final void assertEquals(long expected, long actual) { |
| assertEquals(Long.valueOf(expected), Long.valueOf(actual)); |
| } |
| |
| public final void assertEquals(float expected, float actual) { |
| assertEquals(Float.valueOf(expected), Float.valueOf(actual)); |
| } |
| |
| public final void assertEquals(double expected, double actual) { |
| assertEquals(Double.valueOf(expected), Double.valueOf(actual)); |
| } |
| |
| public final void assertEquals(Object expected, Object actual) { |
| if (expected == null) { |
| if (actual == null) { |
| return; |
| } |
| } else if (expected.equals(actual)) { |
| return; |
| } |
| failNotEquals("Failed assertion (expected != actual)", expected, actual); |
| } |
| |
| interface AccessorAccess { |
| void apply() throws Exception; |
| } |
| |
| void assertThrows(Class expectedException, AccessorAccess access) { |
| try { |
| access.apply(); |
| fail("Expected a " + expectedException + ", but not raised"); |
| } catch (Exception e) { |
| if (!expectedException.isInstance(e)) { |
| fail("Expected a " + expectedException + ", but got a " + e.getClass(), e); |
| } |
| } |
| } |
| |
| public final void assertThrowsAIOBE(AccessorAccess access) { |
| assertThrows(ArrayIndexOutOfBoundsException.class, access); |
| } |
| |
| public final void assertThrowsASE(AccessorAccess access) { |
| assertThrows(ArrayStoreException.class, access); |
| } |
| |
| public final void assertThrowsISE(AccessorAccess access) { |
| assertThrows(IllegalStateException.class, access); |
| } |
| |
| public final void assertThrowsIOOBE(AccessorAccess access) { |
| assertThrows(IndexOutOfBoundsException.class, access); |
| } |
| |
| public final void assertThrowsCCE(AccessorAccess access) { |
| assertThrows(ClassCastException.class, access); |
| } |
| |
| public final void assertThrowsNPE(AccessorAccess access) { |
| assertThrows(NullPointerException.class, access); |
| } |
| |
| public final void assertThrowsWMTE(AccessorAccess access) { |
| assertThrows(WrongMethodTypeException.class, access); |
| } |
| |
| public final void failUnreachable() { |
| fail("Unreachable code"); |
| } |
| |
| public final void run() { |
| collector.start(getClass().getSimpleName()); |
| if (!checkGuard()) { |
| collector.skip(); |
| return; |
| } |
| |
| try { |
| doTest(); |
| } catch (Exception e) { |
| fail("Unexpected exception", e); |
| e.printStackTrace(); |
| } finally { |
| if (lazyErrorLog == null) { |
| collector.success(); |
| } else { |
| collector.fail(lazyErrorLog.toString()); |
| } |
| } |
| } |
| |
| private void failNotEquals(String message, Object expected, Object actual) { |
| errorLog() |
| .append(message) |
| .append(": ") |
| .append(expected) |
| .append(" != ") |
| .append(actual) |
| .append(" in ") |
| .append(getSourceInfo()) |
| .append('\n'); |
| } |
| |
| private void fail(String message) { |
| errorLog().append(message).append(" in ").append(getSourceInfo()).append('\n'); |
| } |
| |
| private void fail(String message, String detail) { |
| errorLog() |
| .append(message) |
| .append(": ") |
| .append(detail) |
| .append(" in ") |
| .append(getSourceInfo()) |
| .append('\n'); |
| } |
| |
| private void fail(String message, Exception e) { |
| errorLog() |
| .append(message) |
| .append(": ") |
| .append(e.toString()) |
| .append(" in ") |
| .append(getSourceInfo(e)) |
| .append('\n'); |
| } |
| |
| private String getSourceInfo(Exception e) { |
| // Unit test has thrown an exception. Stack likely looks like |
| // runtime frames then unit test frames then |
| // VarHandleUnitFrames. |
| StackTraceElement[] stackTraceElements = e.getStackTrace(); |
| int index = 1; |
| for (int i = 1; i < stackTraceElements.length; ++i) { |
| if ("VarHandleUnitTest".equals(stackTraceElements[i].getClassName())) { |
| return stackTraceElements[i - 1].toString(); |
| } |
| } |
| return "Unknown"; |
| } |
| |
| private String getSourceInfo() { |
| // Gets source info for a failure such as an assertion. The |
| // test has called a method on VarHandleUnitTest so the stack |
| // looks like some frames in VarHandleUnitTest methods and then |
| // a frame in the test itself. |
| StackTraceElement[] stackTraceElements = new Exception().getStackTrace(); |
| for (StackTraceElement stackTraceElement : stackTraceElements) { |
| if (!"VarHandleUnitTest".equals(stackTraceElement.getClassName())) { |
| return stackTraceElement.toString(); |
| } |
| } |
| return "Unknown"; |
| } |
| |
| private StringBuilder errorLog() { |
| if (lazyErrorLog == null) { |
| lazyErrorLog = new StringBuilder(); |
| } |
| return lazyErrorLog; |
| } |
| } |