blob: 5501a165457df2550378d12a7c58c7cef7d713d5 [file] [log] [blame]
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.wear.protolayout.expression.pipeline;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
public class DynamicDataBiTransformNodeTest {
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock private DynamicTypeValueReceiverWithPreUpdate<Integer> mMockCallback;
private DynamicDataBiTransformNode<Integer, Integer, Integer> mSumNodeUnderTest;
@Before
public void setUp() {
mSumNodeUnderTest = new DynamicDataBiTransformNode<>(mMockCallback, Integer::sum);
}
@After
public void assertNoOtherCallbacks() {
// DynamicDataBiTransformNode is fragile, we want to make sure an unexpected callback wasn't
// used on one of the behaviors tested here.
verifyNoMoreInteractions(mMockCallback);
}
@Test
public void onPreStateUpdate_propagatesOnce() {
mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
verify(mMockCallback).onPreUpdate();
reset(mMockCallback);
// Next call should not cause another call to be passed through.
mSumNodeUnderTest.getRhsUpstreamCallback().onPreUpdate();
verify(mMockCallback, never()).onPreUpdate();
}
@Test
public void onPreStateUpdate_twice_ignoresSecondOne() {
mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
verify(mMockCallback).onPreUpdate();
reset(mMockCallback);
mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
mSumNodeUnderTest.getRhsUpstreamCallback().onPreUpdate();
// Doesn't pre-update again.
verify(mMockCallback, never()).onPreUpdate();
mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
mSumNodeUnderTest.getRhsUpstreamCallback().onData(6);
// Still propagates data.
verify(mMockCallback).onData(11);
}
@Test
public void onStateUpdate_invokesCallback() {
mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
mSumNodeUnderTest.getRhsUpstreamCallback().onPreUpdate();
mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
mSumNodeUnderTest.getRhsUpstreamCallback().onData(6);
verify(mMockCallback).onPreUpdate();
verify(mMockCallback).onData(11);
}
@Test
public void onStateUpdate_onlyOneSide_doesNotInvokeCallback() {
// Note: RHS has *not* been seeded with any data here...
mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
verify(mMockCallback).onPreUpdate();
verify(mMockCallback, never()).onData(any());
}
@Test
public void onStateUpdate_onPreStateUpdateNotCalled_stillPropagatesData() {
mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
mSumNodeUnderTest.getRhsUpstreamCallback().onData(6);
verify(mMockCallback, never()).onPreUpdate();
verify(mMockCallback).onData(11);
}
@Test
public void onStateUpdate_onStateUpdate_eachSidePendingUpdateHandledSeparately() {
// Seed with some real data
mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
mSumNodeUnderTest.getRhsUpstreamCallback().onData(6);
reset(mMockCallback);
mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
verify(mMockCallback).onPreUpdate();
mSumNodeUnderTest.getRhsUpstreamCallback().onData(10);
// The "is update pending" counters should be evaluated separately here, so the incoming
// update on the RHS should not count towards the state update from the LHS
verify(mMockCallback, never()).onData(any());
// And now, fulfilling the LHS should still cause an update to be fired.
mSumNodeUnderTest.getLhsUpstreamCallback().onData(20);
verify(mMockCallback).onData(30);
}
@Test
public void onStateUpdate_secondTimeOnlyOneSide_doesNotWaitForOtherSide() {
// Seed with some real data
mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
mSumNodeUnderTest.getRhsUpstreamCallback().onData(6);
reset(mMockCallback);
// Only on the left.
mSumNodeUnderTest.getLhsUpstreamCallback().onData(10);
// Not waiting for the right.
verify(mMockCallback).onData(16);
}
@Test
public void onInvalidated_propagatesAfterBothSides() {
mSumNodeUnderTest.getLhsUpstreamCallback().onInvalidated();
verifyNoMoreInteractions(mMockCallback);
mSumNodeUnderTest.getRhsUpstreamCallback().onData(5);
verify(mMockCallback).onInvalidated();
}
/** Same as {@link org.mockito.Mockito#reset} but suppresses the unchecked warning. */
@SuppressWarnings("unchecked")
private <T> void reset(T mock) {
org.mockito.Mockito.reset(mock);
}
}