TypePollutionTest.java

package com.fasterxml.jackson.databind.typepollution;

import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import io.micronaut.test.typepollution.FocusListener;
import io.micronaut.test.typepollution.ThresholdFocusListener;
import io.micronaut.test.typepollution.TypePollutionTransformer;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.function.Executable;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;

public class TypePollutionTest {
    private static final int THRESHOLD = 1000;
    private ThresholdFocusListener focusListener;

    @BeforeAll
    static void setupAgent() {
        TypePollutionTransformer.install(net.bytebuddy.agent.ByteBuddyAgent.install());
    }

    @BeforeEach
    void setUp() {
        focusListener = new ThresholdFocusListener();
        FocusListener.setFocusListener(focusListener);
    }

    @AfterEach
    void verifyNoTypeThrashing() {
        FocusListener.setFocusListener(null);
        Assertions.assertTrue(focusListener.checkThresholds(THRESHOLD), "Threshold exceeded, check logs.");
    }

    private void doTest(Executable r) throws Throwable {
        for (int i = 0; i < THRESHOLD * 2; i++) {
            r.execute();
        }
    }

    @Test
    @Disabled
    public void sample() throws Throwable {
        // example test. If you enable this, it will fail, and you can see what a type pollution error looks like.

        interface A {
        }

        interface B {
        }

        class Concrete implements A, B {
        }

        Object c = new Concrete();
        AtomicInteger j = new AtomicInteger();
        doTest(() -> {
            if (c instanceof A) {
                j.incrementAndGet();
            }
            if (c instanceof B) {
                j.incrementAndGet();
            }
        });
        System.out.println(j);
    }

    @Test
    public void deserializeRecordWithList() throws Throwable {
        ObjectMapper mapper = JsonMapper.builder().build();
        ListRecord input = new ListRecord(List.of("foo"));
        String json = mapper.writeValueAsString(input);
        doTest(() -> Assertions.assertEquals(input, mapper.readValue(json, ListRecord.class)));
    }

    record ListRecord(List<String> list) {
    }

    @Test
    public void deserializeRecordWithSet() throws Throwable {
        ObjectMapper mapper = JsonMapper.builder().build();
        SetRecord input = new SetRecord(Set.of("foo"));
        String json = mapper.writeValueAsString(input);
        doTest(() -> Assertions.assertEquals(input, mapper.readValue(json, SetRecord.class)));
    }

    record SetRecord(Set<String> list) {
    }
}