Skip to content

Commit

Permalink
Create separate sandboxes for different graphics modes
Browse files Browse the repository at this point in the history
Previously, graphics shadow invalidation did not occur. When the graphics
mode switched, graphics shadows that had already been picked were reused. This
meant that transitions between graphics modes were very likely to cause
segfaults, as transitions between modes would result in a mixture of legacy and
native graphics shadow classes being used.

Add a new sandbox param for graphics mode. This will result in a separate set
of sandboxes to be created when the graphics mode switches.

RE #8073

PiperOrigin-RevId: 518349097
  • Loading branch information
hoisie authored and Copybara-Service committed Mar 22, 2023
1 parent b40b431 commit 4633ae4
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 6 deletions.
Expand Up @@ -24,6 +24,7 @@
import org.junit.runners.model.Statement;
import org.robolectric.android.AndroidSdkShadowMatcher;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.GraphicsMode;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
import org.robolectric.annotation.SQLiteMode;
Expand Down Expand Up @@ -289,9 +290,14 @@ protected AndroidSandbox getSandbox(FrameworkMethod method) {
? SQLiteMode.Mode.LEGACY
: roboMethod.configuration.get(SQLiteMode.Mode.class);

GraphicsMode.Mode graphicsMode =
roboMethod.configuration == null
? GraphicsMode.Mode.LEGACY
: roboMethod.configuration.get(GraphicsMode.Mode.class);

sdk.verifySupportedSdk(method.getDeclaringClass().getName());
return sandboxManager.getAndroidSandbox(
classLoaderConfig, sdk, resourcesMode, looperMode, sqliteMode);
classLoaderConfig, sdk, resourcesMode, looperMode, sqliteMode, graphicsMode);
}

@Override
Expand Down
Expand Up @@ -6,6 +6,7 @@
import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Named;
import org.robolectric.annotation.GraphicsMode;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.SQLiteMode;
import org.robolectric.internal.bytecode.InstrumentationConfiguration;
Expand Down Expand Up @@ -49,8 +50,10 @@ public synchronized AndroidSandbox getAndroidSandbox(
Sdk sdk,
ResourcesMode resourcesMode,
LooperMode.Mode looperMode,
SQLiteMode.Mode sqliteMode) {
SandboxKey key = new SandboxKey(instrumentationConfig, sdk, resourcesMode, looperMode);
SQLiteMode.Mode sqliteMode,
GraphicsMode.Mode graphicsMode) {
SandboxKey key =
new SandboxKey(instrumentationConfig, sdk, resourcesMode, looperMode, graphicsMode);

AndroidSandbox androidSandbox = sandboxesByKey.get(key);
if (androidSandbox == null) {
Expand Down Expand Up @@ -79,16 +82,19 @@ static class SandboxKey {
private final InstrumentationConfiguration instrumentationConfiguration;
private final ResourcesMode resourcesMode;
private final LooperMode.Mode looperMode;
private final GraphicsMode.Mode graphicsMode;

public SandboxKey(
InstrumentationConfiguration instrumentationConfiguration,
Sdk sdk,
ResourcesMode resourcesMode,
LooperMode.Mode looperMode) {
LooperMode.Mode looperMode,
GraphicsMode.Mode graphicsMode) {
this.sdk = sdk;
this.instrumentationConfiguration = instrumentationConfiguration;
this.resourcesMode = resourcesMode;
this.looperMode = looperMode;
this.graphicsMode = graphicsMode;
}

@Override
Expand All @@ -103,12 +109,14 @@ public boolean equals(Object o) {
return resourcesMode == that.resourcesMode
&& Objects.equals(sdk, that.sdk)
&& Objects.equals(instrumentationConfiguration, that.instrumentationConfiguration)
&& looperMode == that.looperMode;
&& looperMode == that.looperMode
&& graphicsMode == that.graphicsMode;
}

@Override
public int hashCode() {
return Objects.hash(sdk, instrumentationConfiguration, resourcesMode, looperMode);
return Objects.hash(
sdk, instrumentationConfiguration, resourcesMode, looperMode, graphicsMode);
}
}
}
@@ -0,0 +1,52 @@
package org.robolectric.plugins;

import static android.os.Build.VERSION_CODES.TIRAMISU;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.annotation.GraphicsMode.Mode.LEGACY;
import static org.robolectric.annotation.GraphicsMode.Mode.NATIVE;

import android.graphics.Matrix;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.GraphicsMode;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowLegacyMatrix;
import org.robolectric.shadows.ShadowMatrix;
import org.robolectric.shadows.ShadowNativeMatrix;

/**
* Tests methods that cause transitions to different graphics modes. This is to verify that shadow
* invalidation of graphics shadows occurs when the graphics mode changes.
*
* <p>Method order is important to ensure consistent transitions between LEGACY -> NATIVE -> LEGACY
* graphics.
*/
@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Config(sdk = TIRAMISU)
public class GraphicsModeTransitionTest {
@GraphicsMode(LEGACY)
@Test
public void test1Legacy() {
ShadowMatrix shadowMatrix = Shadow.extract(new Matrix());
assertThat(shadowMatrix).isInstanceOf(ShadowLegacyMatrix.class);
}

@GraphicsMode(NATIVE)
@Test
public void test2NativeAfterLegacy() {
ShadowMatrix shadowMatrix = Shadow.extract(new Matrix());
assertThat(shadowMatrix).isInstanceOf(ShadowNativeMatrix.class);
}

@GraphicsMode(LEGACY)
@Test
public void test3LegacyAfterNative() {
ShadowMatrix shadowMatrix = Shadow.extract(new Matrix());
assertThat(shadowMatrix).isInstanceOf(ShadowLegacyMatrix.class);
}
}

0 comments on commit 4633ae4

Please sign in to comment.