test/jdk/jdk/jfr/api/consumer/TestRecordedObject.java
author egahlin
Tue, 15 May 2018 20:24:34 +0200
changeset 50113 caf115bb98ad
child 51214 67736b4846a0
permissions -rw-r--r--
8199712: Flight Recorder Reviewed-by: coleenp, ihse, erikj, dsamersoff, mseledtsov, egahlin, mgronlun Contributed-by: erik.gahlin@oracle.com, markus.gronlund@oracle.com

/*
 * Copyright (c) 2018, 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 jdk.jfr.api.consumer;

import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.StackTrace;
import jdk.jfr.Timespan;
import jdk.jfr.Timestamp;
import jdk.jfr.Unsigned;
import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedObject;
import jdk.jfr.consumer.RecordedThread;
import jdk.test.lib.Asserts;
import jdk.test.lib.jfr.Events;

/*
 * @test
 * @summary Verifies the methods of the RecordedObject
 * @key jfr
 * @library /test/lib
 * @run main/othervm jdk.jfr.api.consumer.TestRecordedObject
 */
public class TestRecordedObject {

    private final static boolean BOOLEAN_VALUE = true;
    private final static byte VALUE = 47;
    private final static String STRING_VALUE = "47";
    private final static Class<?> CLASS_VALUE = String.class;
    private final static Thread THREAD_VALUE = Thread.currentThread();
    private final static Instant INSTANT_VALUE = Instant.now();
    private final static Duration DURATION_VALUE = Duration.ofSeconds(47);

    @StackTrace(false)
    static final class EventWithValues extends Event {
        boolean booleanField = BOOLEAN_VALUE;
        byte byteField = VALUE;
        char charField = VALUE;
        short shortField = VALUE;
        int intField = VALUE;
        long longField = VALUE;
        float floatField = VALUE;
        double doubleField = VALUE;
        String stringField = STRING_VALUE;
        Class<?> classField = CLASS_VALUE;
        Thread threadField = THREAD_VALUE;
        @Timespan(Timespan.NANOSECONDS)
        long durationField = DURATION_VALUE.toNanos();
        @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH)
        long instantField = INSTANT_VALUE.toEpochMilli();
        Thread nullField = null;
        Class<?> nullField2 = null;

        @Timespan(Timespan.MICROSECONDS)
        long durationMicros = DURATION_VALUE.toNanos() / 1000;

        @Timespan(Timespan.MILLISECONDS)
        long durationMillis = DURATION_VALUE.toMillis();

        @Timespan(Timespan.SECONDS)
        long durationSeconds = DURATION_VALUE.toSeconds();

        @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH)
        long instantMillis = 1000;

        @Timestamp(Timespan.TICKS)
        long instantTicks = 0;

        @Unsigned
        byte unsignedByte = Byte.MIN_VALUE;
        @Unsigned
        char unsignedChar = 'q';
        @Unsigned
        short unsignedShort = Short.MIN_VALUE;
        @Unsigned
        int unsignedInt = Integer.MIN_VALUE;
        @Unsigned
        long unsignedLong = Long.MIN_VALUE; // unsigned should be ignored
        @Unsigned
        float unsignedFloat = Float.MIN_VALUE; // unsigned should be ignored
        @Unsigned
        double unsignedDouble = Double.MIN_VALUE; // unsigned should be ignored

    }

    private final static Set<String> ALL = createAll();

    public static void main(String[] args) throws Throwable {

        RecordedObject event = makeRecordedObject();

        // Primitives
        testGetBoolean(event);
        testGetByte(event);
        testGetChar(event);
        testGetShort(event);
        testGetInt(event);
        testGetLong(event);
        testGetDouble(event);
        testGetFloat(event);

        // // Complex types
        testGetString(event);
        testGetInstant(event);
        testGetDuration(event);
        testGetThread(event);
        testGetClass(event);

        // Misc.
        testNestedNames(event);
        testTimeUnits(event);
        testUnsigned(event);
    }

    private static void testUnsigned(RecordedObject event) {
        // Unsigned byte value
        Asserts.assertEquals(event.getByte("unsignedByte"), Byte.MIN_VALUE);
        Asserts.assertEquals(event.getInt("unsignedByte"), Byte.toUnsignedInt(Byte.MIN_VALUE));
        Asserts.assertEquals(event.getLong("unsignedByte"), Byte.toUnsignedLong(Byte.MIN_VALUE));
        Asserts.assertEquals(event.getShort("unsignedByte"), (short)Byte.toUnsignedInt(Byte.MIN_VALUE));

        // Unsigned char, nothing should happen, it is unsigned
        Asserts.assertEquals(event.getChar("unsignedChar"), 'q');
        Asserts.assertEquals(event.getInt("unsignedChar"), (int)'q');
        Asserts.assertEquals(event.getLong("unsignedChar"), (long)'q');

        // Unsigned short
        Asserts.assertEquals(event.getShort("unsignedShort"), Short.MIN_VALUE);
        Asserts.assertEquals(event.getInt("unsignedShort"), Short.toUnsignedInt(Short.MIN_VALUE));
        Asserts.assertEquals(event.getLong("unsignedShort"), Short.toUnsignedLong(Short.MIN_VALUE));

        // Unsigned int
        Asserts.assertEquals(event.getInt("unsignedInt"), Integer.MIN_VALUE);
        Asserts.assertEquals(event.getLong("unsignedInt"), Integer.toUnsignedLong(Integer.MIN_VALUE));

        // Unsigned long, nothing should happen
        Asserts.assertEquals(event.getLong("unsignedLong"), Long.MIN_VALUE);

        // Unsigned float, nothing should happen
        Asserts.assertEquals(event.getFloat("unsignedFloat"), Float.MIN_VALUE);

        // Unsigned double, nothing should happen
        Asserts.assertEquals(event.getDouble("unsignedDouble"), Double.MIN_VALUE);
    }

    private static void testTimeUnits(RecordedObject event) {
        Asserts.assertEquals(event.getDuration("durationMicros"), DURATION_VALUE);
        Asserts.assertEquals(event.getDuration("durationMillis"), DURATION_VALUE);
        Asserts.assertEquals(event.getDuration("durationSeconds"), DURATION_VALUE);
        Asserts.assertEquals(event.getInstant("instantMillis").toEpochMilli(), 1000L);
        if (!event.getInstant("instantTicks").isBefore(INSTANT_VALUE)) {
            throw new AssertionError("Expected start time of JVM to before call to Instant.now()");
        }
    }

    private static void testNestedNames(RecordedObject event) {
        RecordedThread t = event.getValue("threadField");

        // Nested with getValue
        try {
            event.getValue("nullField.javaName");
            throw new AssertionError("Expected NullPointerException");
        } catch (NullPointerException npe) {
            // OK, expected;
        }
        try {
            event.getValue("nullField.does.not.exist");
            throw new AssertionError("Expected IllegalArgumentException");
        } catch (IllegalArgumentException iae) {
            // OK, expected;
        }

        // Nested getLong
        try {
            event.getLong("nullField.javaName");
            throw new AssertionError("Expected NullPointerException");
        } catch (NullPointerException npe) {
            // OK, expected;
        }
        try {
            event.getLong("nullField.does.not.exist");
            throw new AssertionError("Expected IllegalArgumentException");
        } catch (IllegalArgumentException npe) {
            // OK, expected;
        }
        if (t.getOSThreadId() != event.getLong("threadField.osThreadId")) {
            throw new AssertionError("Incorrect result from nested long value");
        }

        // Nested getString
        try {
            event.getString("nullField.osThreadId");
            throw new AssertionError("Expected IllegalArgumentException");
        } catch (IllegalArgumentException npe) {
            // OK, expected;
        }
        try {
            event.getLong("nullField.does.not.exist");
            throw new AssertionError("Expected IllegalArgumentException");
        } catch (IllegalArgumentException npe) {
            // OK, expected;
        }
        if (!t.getJavaName().equals(event.getString("threadField.javaName"))) {
            throw new AssertionError("Incorrect result from nested long value");
        }

        // Nested getClass
        try {
            event.getClass("nullField.osThreadId");
            throw new AssertionError("Expected IllegalArgumentException");
        } catch (IllegalArgumentException npe) {
            // OK, expected;
        }
        try {
            event.getClass("nullField.does.not.exist");
            throw new AssertionError("Expected IllegalArgumentException");
        } catch (IllegalArgumentException npe) {
            // OK, expected;
        }

        // Nested getThread
        try {
            event.getThread("nullField2.name");
            throw new AssertionError("Expected IllegalArgumentException");
        } catch (IllegalArgumentException npe) {
            // OK, expected;
        }
        try {
            event.getThread("nullField2.does.not.exist");
            throw new AssertionError("Expected IllegalArgumentException");
        } catch (IllegalArgumentException npe) {
            // OK, expected;
        }
    }

    private static void testGetBoolean(RecordedObject e) {
        assertGetter(x -> e.getBoolean(x), BOOLEAN_VALUE, "boolean");
    }

    private static void testGetByte(RecordedObject e) {
        assertGetter(x -> e.getByte(x), (byte) VALUE, "byte");
    }

    private static void testGetChar(RecordedObject e) {
        assertGetter(x -> e.getChar(x), (char) VALUE, "char");
    }

    private static void testGetShort(RecordedObject e) {
        assertGetter(x -> e.getShort(x), (short) VALUE, "byte", "short");
    }

    private static void testGetInt(RecordedObject e) {
        assertGetter(x -> e.getInt(x), (int) VALUE, "byte", "char", "short", "int");
    }

    private static void testGetLong(RecordedObject e) {
        assertGetter(x -> e.getLong(x), (long) VALUE, "byte", "char", "short", "int", "long");
    }

    private static void testGetFloat(RecordedObject e) {
        assertGetter(x -> e.getFloat(x), (float) VALUE, "byte", "char", "short", "int", "long", "float");
    }

    private static void testGetDouble(RecordedObject e) {
        assertGetter(x -> e.getDouble(x), (double) VALUE, "byte", "char", "short", "int", "long", "float", "double");
    }

    private static void testGetString(RecordedObject e) {
        assertGetter(x -> e.getString(x), STRING_VALUE, "string");
    }

    private static void testGetInstant(RecordedObject e) {
        assertGetter(x -> e.getInstant(x), Instant.ofEpochMilli(INSTANT_VALUE.toEpochMilli()), "instant");
    }

    private static void testGetDuration(RecordedObject e) {
        assertGetter(x -> e.getDuration(x), DURATION_VALUE, "duration");
    }

    private static void testGetThread(RecordedObject e) {
        RecordedThread thread = e.getValue("threadField");
        if (!thread.getJavaName().equals(THREAD_VALUE.getName())) {
            throw new AssertionError("Expected thread to have name " + THREAD_VALUE.getName());
        }
        assertGetter(x -> {
            // OK to access nullField if it is correct type
            // Chose a second null field with class type
            if ("nullField".equals(x)) {
                return e.getThread("nullField2");
            } else {
                return e.getThread(x);
            }

        }, thread, "thread");
    }

    private static void testGetClass(RecordedObject e) {
        RecordedClass clazz = e.getValue("classField");
        if (!clazz.getName().equals(CLASS_VALUE.getName())) {
            throw new AssertionError("Expected class to have name " + CLASS_VALUE.getName());
        }
        assertGetter(x -> e.getClass(x), clazz, "class");
    }

    private static <T> void assertGetter(Function<String, T> f, T expectedValue, String... validTypes) {
        Set<String> valids = new HashSet<String>(Arrays.asList(validTypes));
        Set<String> invalids = new HashSet<String>(ALL);
        invalids.removeAll(valids);
        for (String valid : valids) {
            T result = f.apply(valid + "Field");
            if (!expectedValue.equals(result)) {
                throw new AssertionError("Incorrect return value " + result + ". Expected " + expectedValue);
            }
        }
        for (String invalid : invalids) {
            try {
                f.apply(invalid + "Field");
            } catch (IllegalArgumentException iae) {
                // OK, as expected
            } catch (Exception e) {
                throw new AssertionError("Unexpected exception for invalid field " + invalid + ". " + e.getClass().getName() + " : " + e.getMessage(), e);
            }
        }
        String[] illegals = { "missingField", "nullField.javaName.does.not.exist", "nullField" };
        for (String illegal : illegals) {
            try {
                f.apply(illegal);
                throw new AssertionError("Expected IllegalArgumentException when accessing " + illegal);
            } catch (IllegalArgumentException iae) {
                // OK, as expected
            } catch (Exception e) {
                throw new AssertionError("Expected IllegalArgumentException. Got " + e.getClass().getName() + " : " + e.getMessage(), e);
            }
        }
        try {
            f.apply(null);
            throw new AssertionError("Expected NullpointerException exception when passing in null value");
        } catch (NullPointerException iae) {
            // OK, as expected
        } catch (Exception e) {
            throw new AssertionError("Expected NullpointerException. Got " + e.getClass().getName() + " : " + e.getMessage(), e);
        }
    }

    private static RecordedObject makeRecordedObject() throws IOException {
        Recording r = new Recording();
        r.start();
        EventWithValues t = new EventWithValues();
        t.commit();
        r.stop();
        List<RecordedEvent> events = Events.fromRecording(r);
        Events.hasEvents(events);
        return events.get(0);
    }

    private static Set<String> createAll() {
        Set<String> set = new HashSet<>();
        set.add("boolean");
        set.add("byte");
        set.add("char");
        set.add("short");
        set.add("int");
        set.add("long");
        set.add("float");
        set.add("double");
        set.add("string");
        set.add("class");
        set.add("thread");
        set.add("instant");
        set.add("duration");
        return set;
    }
}