JfrUnit

repository·main·Indexed 43 days ago

https://github.com/moditect/jfrunit

A JUnit extension for asserting JDK Flight Recorder (JFR) events. It provides the jfrunit-core library (version 1.0.0.Alpha2) for writing event assertions in JUnit and the Spock Framework, as well as the jfr-constants-generator annotation processor to automate the creation of JFR event and type code.

Tokens
1.4K
Snippets
4
Records
6
Agent score
6%

What's inside JfrUnit

  1. Use jfr-constants-generator to generate JFR event code

    main

    The jfr-constants-generator is an annotation processor designed to automatically generate Java code for JDK Flight Recorder (JFR) events and types.

    When integrated into a project, the processor will generate classes based on your annotations. These generated classes are placed in the following directory within your project's target folder: target/annotations/generated-sources

  2. Install JfrUnit via Maven

    main

    To use JfrUnit in your project, add the jfrunit-core dependency to your pom.xml with test scope. This project requires OpenJDK 16 or later at runtime.

    <dependency>
      <groupId>org.moditect.jfrunit</groupId>
      <artifactId>jfrunit-core</artifactId>
      <version>1.0.0.Alpha2</version>
      <scope>test</scope>
    </dependency>
  3. Write JFR event assertions in JUnit

    main

    To test for specific JDK Flight Recorder (JFR) events, use the @JfrEventTest annotation on your test class and an instance of JfrEvents. You must enable the specific events you want to capture using the @EnableEvent annotation on your test methods. After performing the workload, call jfrEvents.awaitEvents() before making assertions.

    Note for Quarkus users: If you are using @QuarkusTest, do not add @JfrEventTest. The Quarkus framework will automatically manage the JFR recording callbacks.

    import org.moditect.jfrunit.*;
    
    import static org.moditect.jfrunit.JfrEventsAssert.*;
    import static org.moditect.jfrunit.ExpectedEvent.*;
    import org.moditect.jfrunit.events.JfrEventTypes;
    
    @JfrEventTest
    public class JfrTest {
    
        public JfrEvents jfrEvents = new JfrEvents();
    
        @Test
        @EnableEvent(GarbageCollection.EVENT_NAME)
        @EnableEvent(ThreadSleep.EVENT_NAME)
        public void shouldHaveGcAndSleepEvents() throws Exception {
            System.gc();
            Thread.sleep(1000);
    
            jfrEvents.awaitEvents();
    
            assertThat(jfrEvents).contains(JfrEventTypes.GARBAGE_COLLECTION);
            assertThat(jfrEvents).contains(
                    JfrEventTypes.THREAD_SLEEP.withTime(Duration.ofMillis(1000)));
        }
    }
  4. Use JfrUnit with the Spock Framework

    main

    JfrUnit supports the Spock Framework using a custom DSL. You can access events by name using jfrEvents['event.name'] or jfrEvents.list('event.name'), which returns a list of events.

    You can further filter these lists using dynamic methods like with, having, or notHaving. Additionally, RecordedEvent objects are extended with dynamic properties (e.g., event.time, event.bytesWritten), allowing for powerful aggregations.

    import org.moditect.jfrunit.JfrEvents
    import spock.lang.Specification
    
    import java.time.Duration
    
    class JfrSpec extends Specification {
    
        JfrEvents jfrEvents = new JfrEvents()
    
        @EnableEvent('jdk.GarbageCollection')
        @EnableEvent('jdk.ThreadSleep')
        def 'should Have GC And Sleep Events'() {
            when:
            System.gc()
            sleep(1000)
    
            then:
            jfrEvents['jdk.GarbageCollection']
            jfrEvents['jdk.ThreadSleep'].withTime(Duration.ofMillis(1000))
        }
    }
  5. Enable JFR events and configurations

    main

    You can control which JFR events are captured using the following annotations:

    1. @EnableEvent(String eventName): Enables a specific event type. You can use the * wildcard to match multiple types (e.g., @EnableEvent("jdk.GC*")).
    2. @EnableConfiguration(String configName): Instead of individual events, you can specify a JFR configuration file name (e.g., "default" or "profile"). These files are located in $JAVA_HOME/bin/jfr_.

    Complete list of built-in JFR event types can be found in the JFR documentation.

    // Using wildcards
    @Test
    @EnableEvent("jdk.GC*")
    @EnableEvent("jdk.G1*")
    public void someTest() throws Exception { ... }
    
    // Using a configuration profile
    @Test
    @EnableConfiguration("default")
    public void someTest() throws Exception { ... }