Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 3 additions & 26 deletions src/main/java/org/carlmontrobotics/lib199/Mocks.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package org.carlmontrobotics.lib199;

import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.mockito.MockSettings;
Expand All @@ -20,25 +16,6 @@

public final class Mocks {

private static final CopyOnWriteArrayList<WeakReference<Object>> MOCKS = new CopyOnWriteArrayList<>();
private static final Predicate<WeakReference<?>> IS_REFERENCE_CLEARED = reference -> reference.get() == null;
private static final Consumer<WeakReference<Object>> CLEAR_INVOCATIONS_ON_REFERENCED_MOCK = reference -> Mockito.clearInvocations(reference.get());
private static final Predicate<WeakReference<Object>> CLEAR_INVOCATIONS_ON_REFERENCED_MOCK_IF_REFERENCE_NOT_CLEARED = reference -> {
if(IS_REFERENCE_CLEARED.test(reference)) return true;
CLEAR_INVOCATIONS_ON_REFERENCED_MOCK.accept(reference);
return false;
};

static {
// Use a single predicate so that clearing references and invocations is an atomic operation
// Otherwise, we could (rarely) run into:
// 1) Mock is added
// 2) Garbage collected references are removed
// 3) Mock is garbage collected
// 4) Mock invocations are cleared -> throws NullPointerException
Lib199Subsystem.registerAsyncPeriodic(() -> MOCKS.removeIf(CLEAR_INVOCATIONS_ON_REFERENCED_MOCK_IF_REFERENCE_NOT_CLEARED));
}

/**
* Attempts to create an instance of a class in which some or all of the classes methods are replaced with a mocked implementation
* @param <T> the class type which will be mocked
Expand Down Expand Up @@ -103,6 +80,7 @@ public static <T, U> T createMock(Class<T> classToMock, U implClass, Answer<Obje
settings = Mockito.withSettings().extraInterfaces(interfaces);
}
settings = settings.defaultAnswer(new MockAnswer<>(methods, implClass, defaultAnswer));
settings = settings.stubOnly(); // Because recording invocations would cause memory usage to grow without bound
T mock = mock(classToMock, settings);
return mock;
}
Expand Down Expand Up @@ -157,15 +135,14 @@ public static <T> T mock(Class<T> classToMock, MockSettings mockSettings) {
}

/**
* Registers a Mockito mock and periodically calls {@link Mockito#clearInvocations(Object...)} on it to prevent memory leaks
* No longer does anything.
*
* @param <T> The type of the mock
* @param t The mock
* @return The mock
*/
@Deprecated
public static <T> T reportMock(T t) {
// Wrap in a WeakReference to prevent memory leaks on objects with no more references
if(Mockito.mockingDetails(t).isMock()) MOCKS.add(new WeakReference<Object>(t));
return t;
}

Expand Down