All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.internal.faking.FakeClasses Maven / Gradle / Ivy

Go to download

JMockit is a Java toolkit for automated developer testing. It contains APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external APIs; JUnit (4 & 5) and TestNG test runners are supported. It also contains an advanced code coverage tool.

The newest version!
/*
 * Copyright (c) 2006 JMockit developers
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
 */
package mockit.internal.faking;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Map.Entry;

import mockit.MockUp;
import mockit.internal.util.ClassLoad;

public final class FakeClasses {
    private static final Field INVOKED_INSTANCE_FIELD;
    private static final Method ON_TEAR_DOWN_METHOD;

    static {
        try {
            INVOKED_INSTANCE_FIELD = MockUp.class.getDeclaredField("invokedInstance");
            INVOKED_INSTANCE_FIELD.setAccessible(true);

            ON_TEAR_DOWN_METHOD = MockUp.class.getDeclaredMethod("onTearDown");
            ON_TEAR_DOWN_METHOD.setAccessible(true);
        } catch (NoSuchFieldException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private static void notifyOfTearDown(@NonNull MockUp mockUp) {
        try {
            ON_TEAR_DOWN_METHOD.invoke(mockUp);
        } catch (IllegalAccessException ignore) {
        } catch (InvocationTargetException e) {
            e.getCause().printStackTrace();
        }
    }

    public static final class MockUpInstances {
        @NonNull
        public final MockUp initialMockUp;
        boolean hasMockupsForSingleInstances;

        MockUpInstances(@NonNull MockUp initialMockUp) {
            this.initialMockUp = initialMockUp;
            hasMockupsForSingleInstances = false;
        }

        public boolean hasMockUpsForSingleInstances() {
            return hasMockupsForSingleInstances;
        }

        void notifyMockUpOfTearDown() {
            notifyOfTearDown(initialMockUp);
        }
    }

    @NonNull
    private final Map> startupMocks;
    @NonNull
    private final Map, MockUpInstances> mockupClassesToMockupInstances;
    @NonNull
    private final Map> mockedToMockupInstances;
    @NonNull
    public final FakeStates fakeStates;

    public FakeClasses() {
        startupMocks = new IdentityHashMap<>(8);
        mockupClassesToMockupInstances = new IdentityHashMap<>();
        mockedToMockupInstances = new IdentityHashMap<>();
        fakeStates = new FakeStates();
    }

    public void addFake(@NonNull String mockClassDesc, @NonNull MockUp mockUp) {
        startupMocks.put(mockClassDesc, mockUp);
    }

    public void addFake(@NonNull MockUp mockUp) {
        Class mockUpClass = mockUp.getClass();
        MockUpInstances newData = new MockUpInstances(mockUp);
        mockupClassesToMockupInstances.put(mockUpClass, newData);
    }

    public void addFake(@NonNull MockUp mockUp, @NonNull Object mockedInstance) {
        MockUp previousMockup = mockedToMockupInstances.put(mockedInstance, mockUp);
        assert previousMockup == null;

        MockUpInstances mockUpInstances = mockupClassesToMockupInstances.get(mockUp.getClass());
        mockUpInstances.hasMockupsForSingleInstances = true;
    }

    @Nullable
    public MockUp getFake(@NonNull String mockUpClassDesc, @Nullable Object mockedInstance) {
        if (mockedInstance != null) {
            MockUp mockUpForSingleInstance = mockedToMockupInstances.get(mockedInstance);

            if (mockUpForSingleInstance != null) {
                return mockUpForSingleInstance;
            }
        }

        MockUp startupMock = startupMocks.get(mockUpClassDesc);

        if (startupMock != null) {
            return startupMock;
        }

        Class mockUpClass = ClassLoad.loadByInternalName(mockUpClassDesc);
        MockUpInstances mockUpInstances = mockupClassesToMockupInstances.get(mockUpClass);
        Object invokedInstance = mockedInstance;

        if (mockedInstance == null) {
            invokedInstance = Void.class;
        } else if (mockUpInstances.hasMockUpsForSingleInstances()) {
            return null;
        }

        try {
            INVOKED_INSTANCE_FIELD.set(mockUpInstances.initialMockUp, invokedInstance);
        } catch (IllegalAccessException ignore) {
        }

        return mockUpInstances.initialMockUp;
    }

    @Nullable
    public MockUpInstances findPreviouslyAppliedMockUps(@NonNull MockUp newMockUp) {
        Class mockUpClass = newMockUp.getClass();
        MockUpInstances mockUpInstances = mockupClassesToMockupInstances.get(mockUpClass);

        if (mockUpInstances != null && mockUpInstances.hasMockupsForSingleInstances) {
            fakeStates.copyFakeStates(mockUpInstances.initialMockUp, newMockUp);
        }

        return mockUpInstances;
    }

    private void discardMockupInstances(@NonNull Map> previousMockInstances) {
        if (!previousMockInstances.isEmpty()) {
            mockedToMockupInstances.entrySet().retainAll(previousMockInstances.entrySet());
        } else if (!mockedToMockupInstances.isEmpty()) {
            mockedToMockupInstances.clear();
        }
    }

    private void discardMockupInstancesExceptPreviousOnes(@NonNull Map, Boolean> previousMockupClasses) {
        updatePreviousMockups(previousMockupClasses);

        for (Entry, MockUpInstances> mockupClassAndInstances : mockupClassesToMockupInstances.entrySet()) {
            Class mockupClass = mockupClassAndInstances.getKey();

            if (!previousMockupClasses.containsKey(mockupClass)) {
                MockUpInstances mockUpInstances = mockupClassAndInstances.getValue();
                mockUpInstances.notifyMockUpOfTearDown();
            }
        }

        mockupClassesToMockupInstances.keySet().retainAll(previousMockupClasses.keySet());
    }

    private void updatePreviousMockups(@NonNull Map, Boolean> previousMockupClasses) {
        for (Entry, Boolean> mockupClassAndData : previousMockupClasses.entrySet()) {
            Class mockupClass = mockupClassAndData.getKey();
            MockUpInstances mockUpData = mockupClassesToMockupInstances.get(mockupClass);
            mockUpData.hasMockupsForSingleInstances = mockupClassAndData.getValue();
        }
    }

    private void discardAllMockupInstances() {
        if (!mockupClassesToMockupInstances.isEmpty()) {
            for (MockUpInstances mockUpInstances : mockupClassesToMockupInstances.values()) {
                mockUpInstances.notifyMockUpOfTearDown();
            }

            mockupClassesToMockupInstances.clear();
        }
    }

    public void discardStartupFakes() {
        for (MockUp startupMockup : startupMocks.values()) {
            notifyOfTearDown(startupMockup);
        }
    }

    public final class SavePoint {
        @NonNull
        private final Map> previousMockInstances;
        @NonNull
        private final Map, Boolean> previousMockupClasses;

        public SavePoint() {
            previousMockInstances = new IdentityHashMap<>(mockedToMockupInstances);
            previousMockupClasses = new IdentityHashMap<>();

            for (Entry, MockUpInstances> mockUpClassAndData : mockupClassesToMockupInstances.entrySet()) {
                Class mockUpClass = mockUpClassAndData.getKey();
                MockUpInstances mockUpData = mockUpClassAndData.getValue();
                previousMockupClasses.put(mockUpClass, mockUpData.hasMockupsForSingleInstances);
            }
        }

        public void rollback() {
            discardMockupInstances(previousMockInstances);

            if (!previousMockupClasses.isEmpty()) {
                discardMockupInstancesExceptPreviousOnes(previousMockupClasses);
            } else {
                discardAllMockupInstances();
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy