| /* |
| * Copyright 2018 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 com.android.car.arch.common; |
| |
| import static com.android.car.arch.common.LiveDataFunctions.coalesceNull; |
| import static com.android.car.arch.common.LiveDataFunctions.dataOf; |
| import static com.android.car.arch.common.LiveDataFunctions.distinct; |
| import static com.android.car.arch.common.LiveDataFunctions.emitsNull; |
| import static com.android.car.arch.common.LiveDataFunctions.falseLiveData; |
| import static com.android.car.arch.common.LiveDataFunctions.freezable; |
| import static com.android.car.arch.common.LiveDataFunctions.ifThenElse; |
| import static com.android.car.arch.common.LiveDataFunctions.not; |
| import static com.android.car.arch.common.LiveDataFunctions.nullLiveData; |
| import static com.android.car.arch.common.LiveDataFunctions.split; |
| import static com.android.car.arch.common.LiveDataFunctions.trueLiveData; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import androidx.core.util.Pair; |
| import androidx.lifecycle.LiveData; |
| import androidx.lifecycle.MutableLiveData; |
| |
| import com.android.car.arch.common.testing.CaptureObserver; |
| import com.android.car.arch.common.testing.InstantTaskExecutorRule; |
| import com.android.car.arch.common.testing.TestLifecycleOwner; |
| |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.robolectric.RobolectricTestRunner; |
| |
| import java.util.Objects; |
| import java.util.function.BiFunction; |
| import java.util.function.Function; |
| import java.util.function.Supplier; |
| |
| @RunWith(RobolectricTestRunner.class) |
| public class LiveDataFunctionsTest { |
| |
| @Rule |
| public final InstantTaskExecutorRule mRule = new InstantTaskExecutorRule(); |
| @Rule |
| public final TestLifecycleOwner mLifecycleOwner = new TestLifecycleOwner(); |
| |
| @Test |
| public void testNullLiveData() { |
| CaptureObserver<Object> observer = new CaptureObserver<>(); |
| nullLiveData().observe(mLifecycleOwner, observer); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isNull(); |
| assertThat(nullLiveData().getValue()).isNull(); |
| } |
| |
| @Test |
| public void testTrueLiveData() { |
| CaptureObserver<Boolean> observer = new CaptureObserver<>(); |
| trueLiveData().observe(mLifecycleOwner, observer); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isTrue(); |
| assertThat(trueLiveData().getValue()).isTrue(); |
| } |
| |
| @Test |
| public void testFalseLiveData() { |
| CaptureObserver<Boolean> observer = new CaptureObserver<>(); |
| falseLiveData().observe(mLifecycleOwner, observer); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isFalse(); |
| assertThat(falseLiveData().getValue()).isFalse(); |
| } |
| |
| @Test |
| public void testNot() { |
| testUnaryOperator( |
| LiveDataFunctions::not, |
| pair(trueLiveData(), false), |
| pair(falseLiveData(), true), |
| pair(nullLiveData(), null)); |
| |
| checkUninitialized(not(new MutableLiveData<>())); |
| } |
| |
| @Test |
| public void testEmitsNull() { |
| testUnaryOperator( |
| LiveDataFunctions::emitsNull, |
| pair(dataOf(new Object()), false), |
| pair(nullLiveData(), true)); |
| checkUninitialized(emitsNull(new MutableLiveData<>())); |
| } |
| |
| @Test |
| public void testDistinct() { |
| CaptureObserver<Integer> observer = new CaptureObserver<>(); |
| MutableLiveData<Integer> source = dataOf(0); |
| LiveData<Integer> distinct = distinct(source); |
| distinct.observe(mLifecycleOwner, observer); |
| observer.reset(); |
| |
| source.setValue(1); |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isEqualTo(1); |
| observer.reset(); |
| |
| source.setValue(1); |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| |
| source.setValue(2); |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isEqualTo(2); |
| } |
| |
| @Test |
| public void testFreezable() { |
| CaptureObserver<Integer> observer = new CaptureObserver<>(); |
| MutableLiveData<Boolean> isFrozen = dataOf(false); |
| MutableLiveData<Integer> source = dataOf(0); |
| LiveData<Integer> freezable = freezable(isFrozen, source); |
| freezable.observe(mLifecycleOwner, observer); |
| |
| // Initialized to correct value. |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isEqualTo(0); |
| observer.reset(); |
| |
| // Updates with source when not frozen. |
| source.setValue(1); |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isEqualTo(1); |
| observer.reset(); |
| |
| // Doesn't update when frozen. |
| isFrozen.setValue(true); |
| source.setValue(2); |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| |
| // Updates when unfrozen. |
| isFrozen.setValue(false); |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isEqualTo(2); |
| observer.reset(); |
| |
| // Doesn't notify if no changes while frozen. |
| isFrozen.setValue(true); |
| isFrozen.setValue(false); |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| } |
| |
| @Test |
| public void testAnd_truthTable() { |
| testBinaryOperator( |
| LiveDataFunctions::and, |
| pair(pair(trueLiveData(), trueLiveData()), true), |
| pair(pair(trueLiveData(), falseLiveData()), false), |
| pair(pair(trueLiveData(), nullLiveData()), null), |
| pair(pair(falseLiveData(), trueLiveData()), false), |
| pair(pair(falseLiveData(), falseLiveData()), false), |
| pair(pair(falseLiveData(), nullLiveData()), false), |
| pair(pair(nullLiveData(), trueLiveData()), null), |
| pair(pair(nullLiveData(), falseLiveData()), false), |
| pair(pair(nullLiveData(), nullLiveData()), null)); |
| } |
| |
| @Test |
| public void testAnd_uninitialized() { |
| MutableLiveData<Boolean> empty = new MutableLiveData<>(); |
| checkUninitializedBinary( |
| LiveDataFunctions::and, |
| pair(trueLiveData(), empty), |
| pair(falseLiveData(), empty), |
| pair(nullLiveData(), empty), |
| pair(empty, trueLiveData()), |
| pair(empty, falseLiveData()), |
| pair(empty, nullLiveData())); |
| } |
| |
| @Test |
| public void testAnd_changeValue() { |
| MutableLiveData<Boolean> source = new MutableLiveData<>(); |
| CaptureObserver<Boolean> observer = new CaptureObserver<>(); |
| |
| LiveDataFunctions.and(trueLiveData(), source).observe(mLifecycleOwner, observer); |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| |
| source.setValue(true); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isTrue(); |
| } |
| |
| @Test |
| public void testOr_truthTable() { |
| testBinaryOperator( |
| LiveDataFunctions::or, |
| pair(pair(trueLiveData(), trueLiveData()), true), |
| pair(pair(trueLiveData(), falseLiveData()), true), |
| pair(pair(trueLiveData(), nullLiveData()), true), |
| pair(pair(falseLiveData(), trueLiveData()), true), |
| pair(pair(falseLiveData(), falseLiveData()), false), |
| pair(pair(falseLiveData(), nullLiveData()), null), |
| pair(pair(nullLiveData(), trueLiveData()), true), |
| pair(pair(nullLiveData(), falseLiveData()), null), |
| pair(pair(nullLiveData(), nullLiveData()), null)); |
| } |
| |
| @Test |
| public void testOr_uninitialized() { |
| LiveData<Boolean> empty = new MutableLiveData<>(); |
| checkUninitializedBinary( |
| LiveDataFunctions::or, |
| pair(trueLiveData(), empty), |
| pair(falseLiveData(), empty), |
| pair(nullLiveData(), empty), |
| pair(empty, trueLiveData()), |
| pair(empty, falseLiveData()), |
| pair(empty, nullLiveData())); |
| } |
| |
| @Test |
| public void testOr_changeValue() { |
| MutableLiveData<Boolean> source = new MutableLiveData<>(); |
| CaptureObserver<Boolean> observer = new CaptureObserver<>(); |
| |
| LiveDataFunctions.or(trueLiveData(), source).observe(mLifecycleOwner, observer); |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| |
| source.setValue(true); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isTrue(); |
| } |
| |
| @Test |
| public void testIff_truthTable() { |
| Object object = new Object(); |
| testBinaryOperator( |
| (predicate, value) -> LiveDataFunctions.iff(predicate, Boolean::booleanValue, |
| value), |
| pair(pair(trueLiveData(), dataOf(object)), object), |
| pair(pair(falseLiveData(), dataOf(object)), null), |
| pair(pair(nullLiveData(), dataOf(object)), null)); |
| } |
| |
| @Test |
| public void testIff_uninitialized() { |
| checkUninitializedBinary( |
| (predicate, value) -> LiveDataFunctions.iff(predicate, Boolean::booleanValue, |
| value), |
| pair(new MutableLiveData<>(), dataOf(new Object())), |
| pair(falseLiveData(), new MutableLiveData<>()), |
| pair(trueLiveData(), new MutableLiveData<>())); |
| } |
| |
| @Test |
| public void testIff_changePredicate() { |
| MutableLiveData<Boolean> predicate = new MutableLiveData<>(); |
| MutableLiveData<Object> value = new MutableLiveData<>(); |
| Object valueObject = new Object(); |
| value.setValue(valueObject); |
| CaptureObserver<Object> observer = new CaptureObserver<>(); |
| |
| LiveDataFunctions.iff(predicate, Boolean::booleanValue, value) |
| .observe(mLifecycleOwner, observer); |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| |
| predicate.setValue(false); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isNull(); |
| observer.reset(); |
| |
| predicate.setValue(true); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(valueObject); |
| observer.reset(); |
| |
| predicate.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isNull(); |
| } |
| |
| @Test |
| public void testIff_changeValue() { |
| LiveData<Boolean> predicate = trueLiveData(); |
| MutableLiveData<Object> value = new MutableLiveData<>(); |
| Object firstObject = new Object(); |
| CaptureObserver<Object> observer = new CaptureObserver<>(); |
| |
| LiveDataFunctions.iff(predicate, Boolean::booleanValue, value) |
| .observe(mLifecycleOwner, observer); |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| |
| value.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isNull(); |
| observer.reset(); |
| |
| value.setValue(firstObject); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(firstObject); |
| observer.reset(); |
| |
| value.setValue(new Object()); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isNotSameAs(firstObject); |
| } |
| |
| @Test |
| public void testIff_changeValue_doesntNotifyForIrrelevantChanges() { |
| MutableLiveData<Object> irrelevantValue = new MutableLiveData<>(); |
| irrelevantValue.setValue(new Object()); |
| CaptureObserver<Object> observer = new CaptureObserver<>(); |
| |
| // irrelevantValue irrelevant because iff() always emits null when predicate is false. |
| LiveDataFunctions.iff(falseLiveData(), Boolean::booleanValue, irrelevantValue) |
| .observe(mLifecycleOwner, observer); |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| observer.reset(); |
| |
| irrelevantValue.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| } |
| |
| @Test |
| public void testIfThenElse_liveDataParams_truthTable() { |
| Object trueObject = new Object(); |
| Object falseObject = new Object(); |
| |
| LiveData<Object> trueObjectData = dataOf(trueObject); |
| LiveData<Object> falseObjectData = dataOf(falseObject); |
| |
| testOperator(arg -> () -> |
| ifThenElse(arg.mPredicate, Boolean::booleanValue, arg.mTrueData, |
| arg.mFalseData), |
| pair(new IfThenElseDataParams<>(trueLiveData(), trueObjectData, falseObjectData), |
| trueObject), |
| pair(new IfThenElseDataParams<>(falseLiveData(), trueObjectData, falseObjectData), |
| falseObject), |
| pair(new IfThenElseDataParams<>(dataOf(null), trueObjectData, falseObjectData), |
| null)); |
| } |
| |
| @Test |
| public void testIfThenElse_liveDataParams_uninitialized() { |
| Object trueObject = new Object(); |
| Object falseObject = new Object(); |
| |
| LiveData<Object> trueObjectData = dataOf(trueObject); |
| LiveData<Object> falseObjectData = dataOf(falseObject); |
| |
| checkUninitialized( |
| ifThenElse( |
| new MutableLiveData<>(), Boolean::booleanValue, trueObjectData, |
| falseObjectData)); |
| checkUninitialized( |
| ifThenElse( |
| trueLiveData(), Boolean::booleanValue, new MutableLiveData<>(), |
| falseObjectData)); |
| checkUninitialized( |
| ifThenElse( |
| falseLiveData(), Boolean::booleanValue, trueObjectData, |
| new MutableLiveData<>())); |
| } |
| |
| @Test |
| public void testIfThenElse_liveDataParams_changePredicate() { |
| Object trueObject = new Object(); |
| Object falseObject = new Object(); |
| |
| LiveData<Object> trueObjectData = dataOf(trueObject); |
| LiveData<Object> falseObjectData = dataOf(falseObject); |
| |
| MutableLiveData<Boolean> predicate = new MutableLiveData<>(); |
| CaptureObserver<Object> observer = new CaptureObserver<>(); |
| |
| ifThenElse(predicate, Boolean::booleanValue, trueObjectData, |
| falseObjectData) |
| .observe(mLifecycleOwner, observer); |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| |
| predicate.setValue(false); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(falseObject); |
| observer.reset(); |
| |
| predicate.setValue(true); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(trueObject); |
| observer.reset(); |
| |
| predicate.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isNull(); |
| } |
| |
| @Test |
| public void testIfThenElse_liveDataParams_changeValue_value() { |
| Object trueObject = new Object(); |
| Object falseObject = new Object(); |
| |
| MutableLiveData<Object> trueObjectData = new MutableLiveData<>(); |
| LiveData<Object> falseObjectData = dataOf(falseObject); |
| |
| LiveData<Boolean> predicate = trueLiveData(); |
| CaptureObserver<Object> observer = new CaptureObserver<>(); |
| |
| ifThenElse(predicate, Boolean::booleanValue, trueObjectData, |
| falseObjectData) |
| .observe(mLifecycleOwner, observer); |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| |
| trueObjectData.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isNull(); |
| observer.reset(); |
| |
| trueObjectData.setValue(trueObject); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(trueObject); |
| observer.reset(); |
| |
| trueObjectData.setValue(new Object()); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isNotSameAs(trueObject); |
| } |
| |
| @Test |
| public void testIfThenElse_liveDataParams_changeValue_doesntNotifyForIrrelevantChanges() { |
| MutableLiveData<Object> irrelevantValue = new MutableLiveData<>(); |
| irrelevantValue.setValue(new Object()); |
| CaptureObserver<Object> observer = new CaptureObserver<>(); |
| |
| // irrelevantValue irrelevant because ifThenElse() is backed by other param when |
| // predicate is |
| // false. |
| ifThenElse( |
| falseLiveData(), Boolean::booleanValue, irrelevantValue, dataOf(new Object())) |
| .observe(mLifecycleOwner, observer); |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| observer.reset(); |
| |
| irrelevantValue.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| } |
| |
| @Test |
| public void testIfThenElse_valueParams_truthTable() { |
| Object trueObject = new Object(); |
| Object falseObject = new Object(); |
| |
| testOperator(arg -> () -> ifThenElse(arg.mPredicate, Boolean::booleanValue, arg.mTrueValue, |
| arg.mFalseValue), |
| pair(new IfThenElseValueParams<>(trueLiveData(), trueObject, falseObject), |
| trueObject), |
| pair(new IfThenElseValueParams<>(falseLiveData(), trueObject, falseObject), |
| falseObject), |
| pair(new IfThenElseValueParams<>(dataOf(null), trueObject, falseObject), null)); |
| } |
| |
| @Test |
| public void testIfThenElse_valueParams_uninitialized() { |
| Object trueObject = new Object(); |
| Object falseObject = new Object(); |
| |
| checkUninitialized( |
| ifThenElse(new MutableLiveData<>(), Boolean::booleanValue, trueObject, |
| falseObject)); |
| } |
| |
| @Test |
| public void testIfThenElse_valueParams_changePredicate() { |
| Object trueObject = new Object(); |
| Object falseObject = new Object(); |
| |
| MutableLiveData<Boolean> predicate = new MutableLiveData<>(); |
| CaptureObserver<Object> observer = new CaptureObserver<>(); |
| |
| ifThenElse(predicate, Boolean::booleanValue, trueObject, falseObject) |
| .observe(mLifecycleOwner, observer); |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| |
| predicate.setValue(false); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(falseObject); |
| observer.reset(); |
| |
| predicate.setValue(true); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(trueObject); |
| observer.reset(); |
| |
| predicate.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isNull(); |
| } |
| |
| @Test |
| public void testCoalesceNull_liveDataParams_truthTable() { |
| TestObject sourceObject = new TestObject(); |
| TestObject fallbackObject = new TestObject(); |
| |
| LiveData<TestObject> sourceData = dataOf(sourceObject); |
| LiveData<TestObject> fallbackData = dataOf(fallbackObject); |
| testBinaryOperator(LiveDataFunctions::coalesceNull, |
| pair(pair(sourceData, fallbackData), sourceObject), |
| pair(pair(sourceData, nullLiveData()), sourceObject), |
| // uninitialized fallback is fine. |
| pair(pair(sourceData, new MutableLiveData<>()), sourceObject), |
| pair(pair(nullLiveData(), fallbackData), fallbackObject), |
| pair(pair(nullLiveData(), nullLiveData()), null)); |
| } |
| |
| @Test |
| public void testCoalesceNull_liveDataParams_uninitialized() { |
| TestObject fallbackObject = new TestObject(); |
| LiveData<TestObject> fallbackData = dataOf(fallbackObject); |
| |
| checkUninitialized(coalesceNull(new MutableLiveData<TestObject>(), fallbackData)); |
| } |
| |
| @Test |
| public void testCoalesceNull_liveDataParams_changeSource() { |
| TestObject firstSourceObject = new TestObject(); |
| TestObject secondSourceObject = new TestObject(); |
| TestObject fallbackObject = new TestObject(); |
| |
| MutableLiveData<TestObject> sourceData = dataOf(null); |
| LiveData<TestObject> fallbackData = dataOf(fallbackObject); |
| |
| CaptureObserver<TestObject> observer = new CaptureObserver<>(); |
| LiveData<TestObject> data = coalesceNull(sourceData, fallbackData); |
| |
| data.observe(mLifecycleOwner, observer); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(fallbackObject); |
| observer.reset(); |
| |
| sourceData.setValue(firstSourceObject); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(firstSourceObject); |
| observer.reset(); |
| |
| sourceData.setValue(secondSourceObject); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(secondSourceObject); |
| observer.reset(); |
| |
| sourceData.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(fallbackObject); |
| observer.reset(); |
| } |
| |
| @Test |
| public void testCoalesceNull_liveDataParams_changeFallback() { |
| TestObject firstFallbackObject = new TestObject(); |
| TestObject secondFallbackObject = new TestObject(); |
| |
| LiveData<TestObject> sourceData = nullLiveData(); |
| MutableLiveData<TestObject> fallbackData = dataOf(null); |
| |
| CaptureObserver<TestObject> observer = new CaptureObserver<>(); |
| LiveData<TestObject> data = coalesceNull(sourceData, fallbackData); |
| |
| data.observe(mLifecycleOwner, observer); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isNull(); |
| observer.reset(); |
| |
| fallbackData.setValue(firstFallbackObject); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(firstFallbackObject); |
| observer.reset(); |
| |
| fallbackData.setValue(secondFallbackObject); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(secondFallbackObject); |
| observer.reset(); |
| |
| fallbackData.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(null); |
| observer.reset(); |
| } |
| |
| @Test |
| public void testCoalesceNull_liveDataParams_changeFallback_doesntNotifyForIrrelevantChanges() { |
| TestObject sourceObject = new TestObject(); |
| TestObject fallbackObject = new TestObject(); |
| |
| LiveData<TestObject> sourceData = dataOf(sourceObject); |
| // Irrelevant because sourceData is always non-null |
| MutableLiveData<TestObject> irrelevantData = dataOf(fallbackObject); |
| |
| CaptureObserver<TestObject> observer = new CaptureObserver<>(); |
| LiveData<TestObject> data = coalesceNull(sourceData, irrelevantData); |
| data.observe(mLifecycleOwner, observer); |
| observer.reset(); |
| |
| irrelevantData.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| } |
| |
| @Test |
| public void testCoalesceNull_valueParams_truthTable() { |
| Object sourceObject = new Object(); |
| Object fallbackObject = new Object(); |
| |
| LiveData<Object> sourceData = dataOf(sourceObject); |
| |
| testOperator(args -> () -> coalesceNull(Objects.requireNonNull(args.first), args.second), |
| pair(pair(sourceData, fallbackObject), sourceObject), |
| pair(pair(sourceData, null), sourceObject), |
| pair(pair(nullLiveData(), fallbackObject), fallbackObject), |
| pair(pair(nullLiveData(), null), null)); |
| |
| } |
| |
| @Test |
| public void testCoalesceNull_valueParams_uninitialized() { |
| // Values contained in SoftReference don't actually matter. SoftReference is just used as |
| // an easily instantiable type. Object cannot be used because LiveData extends Object, |
| // and thus some method calls would become ambiguous due to overloads. |
| Object fallbackObject = new Object(); |
| |
| checkUninitialized(coalesceNull(new MutableLiveData<>(), fallbackObject)); |
| } |
| |
| @Test |
| public void testCoalesceNull_valueParams_changeSource() { |
| // Values contained in SoftReference don't actually matter. SoftReference is just used as |
| // an easily instantiable type. Object cannot be used because LiveData extends Object, |
| // and thus some method calls would become ambiguous. |
| Object firstSourceObject = new Object(); |
| Object secondSourceObject = new Object(); |
| Object fallbackObject = new Object(); |
| |
| MutableLiveData<Object> sourceData = dataOf(null); |
| |
| CaptureObserver<Object> observer = new CaptureObserver<>(); |
| LiveData<Object> data = coalesceNull(sourceData, fallbackObject); |
| |
| data.observe(mLifecycleOwner, observer); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(fallbackObject); |
| observer.reset(); |
| |
| sourceData.setValue(firstSourceObject); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(firstSourceObject); |
| observer.reset(); |
| |
| sourceData.setValue(secondSourceObject); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(secondSourceObject); |
| observer.reset(); |
| |
| sourceData.setValue(null); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isSameAs(fallbackObject); |
| observer.reset(); |
| } |
| |
| @Test |
| public void testSplit() { |
| Object first = new Object(); |
| Object second = new Object(); |
| |
| MutableLiveData<Object> firstData = new MutableLiveData<>(); |
| MutableLiveData<Object> secondData = new MutableLiveData<>(); |
| firstData.setValue(first); |
| secondData.setValue(second); |
| |
| Object[] observedValues = new Object[2]; |
| boolean[] notified = new boolean[1]; |
| |
| LiveDataFunctions.pair(firstData, secondData) |
| .observe( |
| mLifecycleOwner, |
| split( |
| (left, right) -> { |
| notified[0] = true; |
| observedValues[0] = left; |
| observedValues[1] = right; |
| })); |
| |
| assertThat(notified[0]).isTrue(); |
| assertThat(observedValues[0]).isSameAs(first); |
| assertThat(observedValues[1]).isSameAs(second); |
| } |
| |
| @Test |
| public void testSplit_null() { |
| Object[] observedValues = new Object[2]; |
| boolean[] notified = new boolean[1]; |
| |
| dataOf((Pair<Object, Object>) null) |
| .observe( |
| mLifecycleOwner, |
| split( |
| (left, right) -> { |
| notified[0] = true; |
| observedValues[0] = left; |
| observedValues[1] = right; |
| })); |
| |
| assertThat(notified[0]).isTrue(); |
| assertThat(observedValues[0]).isNull(); |
| assertThat(observedValues[1]).isNull(); |
| } |
| |
| @Test |
| public void testCombine() { |
| Object first = new Object(); |
| Object second = new Object(); |
| |
| MutableLiveData<Object> firstData = new MutableLiveData<>(); |
| MutableLiveData<Object> secondData = new MutableLiveData<>(); |
| firstData.setValue(first); |
| secondData.setValue(second); |
| |
| CaptureObserver<Pair<Object, Object>> observer = new CaptureObserver<>(); |
| LiveDataFunctions.combine(firstData, secondData, Pair::new).observe(mLifecycleOwner, |
| observer); |
| |
| Pair<Object, Object> observedValue = observer.getObservedValue(); |
| assertThat(observedValue).isNotNull(); |
| assertThat(observedValue.first).isSameAs(first); |
| assertThat(observedValue.second).isSameAs(second); |
| |
| Object third = new Object(); |
| firstData.setValue(third); |
| |
| observedValue = observer.getObservedValue(); |
| assertThat(observedValue).isNotNull(); |
| assertThat(observedValue.first).isSameAs(third); |
| assertThat(observedValue.second).isSameAs(second); |
| } |
| |
| private static class IfThenElseDataParams<T> { |
| final LiveData<Boolean> mPredicate; |
| final LiveData<T> mTrueData; |
| final LiveData<T> mFalseData; |
| |
| private IfThenElseDataParams( |
| LiveData<Boolean> predicate, LiveData<T> trueData, LiveData<T> falseData) { |
| this.mPredicate = predicate; |
| this.mTrueData = trueData; |
| this.mFalseData = falseData; |
| } |
| } |
| |
| private static class IfThenElseValueParams<T> { |
| final LiveData<Boolean> mPredicate; |
| final T mTrueValue; |
| final T mFalseValue; |
| |
| private IfThenElseValueParams(LiveData<Boolean> predicate, T trueValue, T falseValue) { |
| this.mPredicate = predicate; |
| this.mTrueValue = trueValue; |
| this.mFalseValue = falseValue; |
| } |
| } |
| |
| private <R> void testOperator(Supplier<LiveData<R>> op, R result) { |
| CaptureObserver<R> observer = new CaptureObserver<>(); |
| LiveData<R> data = op.get(); |
| |
| data.observe(mLifecycleOwner, observer); |
| |
| assertThat(observer.hasBeenNotified()).isTrue(); |
| assertThat(observer.getObservedValue()).isEqualTo(result); |
| assertThat(data.getValue()).isEqualTo(result); |
| } |
| |
| @SafeVarargs // args are never written to |
| private final <P, R> void testOperator( |
| Function<P, Supplier<LiveData<R>>> ops, Pair<P, R>... args) { |
| for (Pair<P, R> arg : args) { |
| testOperator(ops.apply(arg.first), arg.second); |
| } |
| } |
| |
| @SafeVarargs // args are never written to |
| private final <T, R> void testUnaryOperator( |
| Function<LiveData<T>, LiveData<R>> op, Pair<LiveData<T>, R>... args) { |
| testOperator(arg -> () -> op.apply(arg), args); |
| } |
| |
| @SafeVarargs // args are never written to |
| private final <A, B, R> void testBinaryOperator( |
| BiFunction<LiveData<A>, LiveData<B>, LiveData<R>> op, |
| Pair<Pair<LiveData<A>, LiveData<B>>, R>... args) { |
| testOperator(arg -> () -> op.apply(arg.first, arg.second), args); |
| } |
| |
| private <T, R> Pair<T, R> pair(T first, R second) { |
| return new Pair<>(first, second); |
| } |
| |
| private <T> void checkUninitialized(LiveData<T> liveData) { |
| CaptureObserver<T> observer = new CaptureObserver<>(); |
| |
| liveData.observe(mLifecycleOwner, observer); |
| |
| assertThat(observer.hasBeenNotified()).isFalse(); |
| assertThat(liveData.getValue()).isNull(); |
| } |
| |
| @SafeVarargs // args are never written to |
| private final <A, B> void checkUninitializedBinary( |
| BiFunction<LiveData<A>, LiveData<B>, LiveData<?>> op, |
| Pair<LiveData<A>, LiveData<B>>... args) { |
| for (Pair<LiveData<A>, LiveData<B>> arg : args) { |
| checkUninitialized(op.apply(arg.first, arg.second)); |
| } |
| } |
| |
| /** |
| * Used as an easily instantiable type where Object cannot be used because LiveData extends |
| * Object, and thus some method calls would become ambiguous. |
| **/ |
| private class TestObject { |
| } |
| } |