-
Notifications
You must be signed in to change notification settings - Fork 6k
Let first frame listeners self remove #11014
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package io.flutter.embedding.engine; | ||
|
|
||
| import org.junit.Before; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
| import org.robolectric.RobolectricTestRunner; | ||
| import org.robolectric.annotation.Config; | ||
|
|
||
| import io.flutter.embedding.engine.FlutterJNI; | ||
| import io.flutter.embedding.engine.renderer.FlutterRenderer; | ||
| import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; | ||
|
|
||
| import static org.junit.Assert.assertEquals; | ||
| import static org.junit.Assert.assertFalse; | ||
| import static org.junit.Assert.assertNull; | ||
| import static org.junit.Assert.assertTrue; | ||
| import static org.mockito.Mockito.mock; | ||
|
|
||
| import java.util.concurrent.atomic.AtomicInteger; | ||
|
|
||
| @Config(manifest=Config.NONE) | ||
| @RunWith(RobolectricTestRunner.class) | ||
| public class FlutterJNITest { | ||
| FlutterJNI jniUnderTest; | ||
|
|
||
| @Before | ||
| public void setUp() { | ||
| jniUnderTest = new FlutterJNI(); | ||
| } | ||
|
|
||
| @Test | ||
| public void itAllowsFirstFrameListenersToRemoveThemselvesInline() { | ||
| // --- Test Setup --- | ||
| AtomicInteger callbackCalled = new AtomicInteger(0); | ||
| OnFirstFrameRenderedListener callback = new OnFirstFrameRenderedListener() { | ||
| @Override | ||
| public void onFirstFrameRendered() { | ||
| callbackCalled.incrementAndGet(); | ||
| jniUnderTest.removeOnFirstFrameRenderedListener(this); | ||
| }; | ||
| }; | ||
| jniUnderTest.addOnFirstFrameRenderedListener(callback); | ||
|
|
||
| // --- Execute Test --- | ||
| jniUnderTest.onFirstFrame(); | ||
|
|
||
| // --- Verify Results --- | ||
| assertEquals(1, callbackCalled.get()); | ||
|
|
||
| // --- Execute Test --- | ||
| // The callback removed itself from the listener list. A second call doesn't call the callback. | ||
| jniUnderTest.onFirstFrame(); | ||
|
|
||
| // --- Verify Results --- | ||
| assertEquals(1, callbackCalled.get()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package io.flutter.embedding.engine.renderer; | ||
|
|
||
| import org.junit.Before; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
| import org.robolectric.RobolectricTestRunner; | ||
| import org.robolectric.annotation.Config; | ||
|
|
||
| import io.flutter.embedding.engine.FlutterJNI; | ||
| import io.flutter.embedding.engine.renderer.FlutterRenderer; | ||
| import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; | ||
|
|
||
| import static org.junit.Assert.assertEquals; | ||
| import static org.junit.Assert.assertFalse; | ||
| import static org.junit.Assert.assertNull; | ||
| import static org.junit.Assert.assertTrue; | ||
| import static org.mockito.Mockito.mock; | ||
|
|
||
| import java.util.concurrent.atomic.AtomicInteger; | ||
|
|
||
| @Config(manifest=Config.NONE) | ||
| @RunWith(RobolectricTestRunner.class) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this need to be robolectric?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FlutterRenderer imports android classes |
||
| public class FlutterRendererTest { | ||
| FlutterJNITest jniUnderTest; | ||
| FlutterRenderer rendererUnderTest; | ||
|
|
||
| @Before | ||
| public void setUp() { | ||
| jniUnderTest = new FlutterJNITest(); | ||
| rendererUnderTest = new FlutterRenderer(jniUnderTest); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I recommend instantiating the object under test within each test. You never know when a test is going to be introduced that requires some kind of unusual instantiation, and when it is, that developer will be forced to change every other test to make that happen. Better to just let each test configure the object under test as it desired.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I don't think we need to suffix things with "test"...everything in here is related to a test.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| @Test | ||
| public void itAllowsFirstFrameListenersToRemoveThemselvesInline() { | ||
| // --- Test Setup --- | ||
| AtomicInteger callbackCalled = new AtomicInteger(0); | ||
| OnFirstFrameRenderedListener callback = new OnFirstFrameRenderedListener() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can probably accomplish this with a mock with less code and complexity than what you have here...
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mockito is unfortunately a bit less capable for these level triggered stuff (mostly due to java) An equivalent here will turn into @Mock OnFirstFrameRenderedListener callback;
when(callback. onFirstFrameRendered()).thenAnswer(
new Answer() {
public Object answer(InvocationOnMock invocation) {
rendererUnderTest.removeOnFirstFrameRenderedListener(callback);
}
});which is even less directly readable. |
||
| @Override | ||
| public void onFirstFrameRendered() { | ||
| callbackCalled.incrementAndGet(); | ||
| rendererUnderTest.removeOnFirstFrameRenderedListener(this); | ||
| }; | ||
| }; | ||
| rendererUnderTest.addOnFirstFrameRenderedListener(callback); | ||
|
|
||
| // --- Execute Test --- | ||
| jniUnderTest.onFirstFrame(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would recommend that in all tests we clearly demarcate between setup/execution/verification. In this case everything up to and including the In addition to placing a blank line and maybe a comment between these 2 lines above, you could also slightly refactor what comes below. I dont think you actually need to interleave 2
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's still worth checking for the actual timing (e.g. that we don't have false positives on onFirstFrame only calling listeners on second invocation or something) |
||
|
|
||
| // --- Verify Results --- | ||
| assertEquals(1, callbackCalled.get()); | ||
|
|
||
| // --- Execute Test --- | ||
| // The callback removed itself from the listener list. A second call doesn't call the callback. | ||
| jniUnderTest.onFirstFrame(); | ||
|
|
||
| // --- Verify Results --- | ||
| assertEquals(1, callbackCalled.get()); | ||
| } | ||
|
|
||
| private static class FlutterJNITest extends FlutterJNI { | ||
| protected void onFirstFrame() { | ||
| // Exposed here for tests. | ||
| super.onFirstFrame(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this here?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Discussed offline. Probably won't need this class once I move the package up one level at which point it'll be in the same package as the FlutterJNI. |
||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This leads me to believe that this is not a test of
FlutterRendererbut rather a test ofFlutterJNI. I think something needs to change in this PR to achieve correctness in that regard. If you're truly unit testingFlutterRendererthen you shouldn't have to make this protected. If you're testingFlutterJNIthen the test suite should be switched accordingly.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a couple of layers of testings we'll need either way.