package com.zhuinden.simplestack; /* * Copyright 2013 Square Inc. * * 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. */ import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; import org.junit.Before; import org.junit.Test; import java.util.Arrays; import java.util.Iterator; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.MockitoAnnotations.initMocks; public class FlowTest { static class Uno implements Parcelable { public Uno() { } protected Uno(Parcel in) { } public static final Creator<Uno> CREATOR = new Creator<Uno>() { @Override public Uno createFromParcel(Parcel in) { return new Uno(in); } @Override public Uno[] newArray(int size) { return new Uno[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { } } static class Dos implements Parcelable { public Dos() { } protected Dos(Parcel in) { } public static final Creator<Dos> CREATOR = new Creator<Dos>() { @Override public Dos createFromParcel(Parcel in) { return new Dos(in); } @Override public Dos[] newArray(int size) { return new Dos[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { } } static class Tres implements Parcelable { public Tres() { } protected Tres(Parcel in) { } public static final Creator<Tres> CREATOR = new Creator<Tres>() { @Override public Tres createFromParcel(Parcel in) { return new Tres(in); } @Override public Tres[] newArray(int size) { return new Tres[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { } } final TestKey able = new TestKey("Able"); final TestKey baker = new TestKey("Baker"); final TestKey charlie = new TestKey("Charlie"); final TestKey delta = new TestKey("Delta"); List<Object> lastStack; int lastDirection; class FlowDispatcher implements StateChanger { @Override public void handleStateChange(@NonNull StateChange stateChange, @NonNull StateChanger.Callback callback) { lastStack = stateChange.getNewState(); lastDirection = stateChange.getDirection(); callback.stateChangeComplete(); } } @Before public void setUp() { initMocks(this); } @Test public void oneTwoThree() { List<Object> history = HistoryBuilder.single(new Uno()); Backstack flow = new Backstack(history); flow.setStateChanger(new FlowDispatcher(), Backstack.INITIALIZE); flow.goTo(new Dos()); assertThat(lastStack.get(lastStack.size() - 1)).isInstanceOf(Dos.class); assertThat(lastDirection).isSameAs(StateChange.FORWARD); flow.goTo(new Tres()); assertThat(lastStack.get(lastStack.size() - 1)).isInstanceOf(Tres.class); assertThat(lastDirection).isSameAs(StateChange.FORWARD); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isInstanceOf(Dos.class); assertThat(lastDirection).isSameAs(StateChange.BACKWARD); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isInstanceOf(Uno.class); assertThat(lastDirection).isSameAs(StateChange.BACKWARD); assertThat(flow.goBack()).isFalse(); } @Test public void historyChangesAfterListenerCall() { final List<Object> firstHistory = HistoryBuilder.single(new Uno()); class Ourrobouros implements StateChanger { Backstack flow = new Backstack(firstHistory); { flow.setStateChanger(this, Backstack.INITIALIZE); } @Override public void handleStateChange(@NonNull StateChange stateChange, @NonNull StateChanger.Callback onComplete) { assertThat(firstHistory).hasSameSizeAs(flow.getHistory()); Iterator<Object> original = firstHistory.iterator(); for(Object o : flow.getHistory()) { assertThat(o).isEqualTo(original.next()); } onComplete.stateChangeComplete(); } } Ourrobouros listener = new Ourrobouros(); listener.flow.goTo(new Dos()); } @Test public void historyPushAllIsPushy() { List<Object> history = HistoryBuilder.from(Arrays.<Parcelable>asList(able, baker, charlie)).build(); assertThat(history.size()).isEqualTo(3); Backstack flow = new Backstack(history); flow.setStateChanger(new FlowDispatcher(), Backstack.INITIALIZE); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(baker); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(able); assertThat(flow.goBack()).isFalse(); } @Test public void setHistoryWorks() { List<Object> history = HistoryBuilder.from(Arrays.<Parcelable>asList(able, baker)).build(); Backstack flow = new Backstack(history); FlowDispatcher handleStateChangeer = new FlowDispatcher(); flow.setStateChanger(handleStateChangeer, Backstack.INITIALIZE); List<Object> newHistory = HistoryBuilder.from(Arrays.<Parcelable>asList(charlie, delta)).build(); flow.setHistory(newHistory, StateChange.FORWARD); assertThat(lastDirection).isSameAs(StateChange.FORWARD); assertThat(lastStack.get(lastStack.size() - 1)).isSameAs(delta); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isSameAs(charlie); assertThat(flow.goBack()).isFalse(); } @Test public void setObjectGoesBack() { List<Object> history = HistoryBuilder.from(Arrays.<Parcelable>asList(able, baker, charlie, delta)).build(); Backstack flow = new Backstack(history); flow.setStateChanger(new FlowDispatcher(), Backstack.INITIALIZE); assertThat(history.size()).isEqualTo(4); flow.goTo(charlie); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(charlie); assertThat(lastStack.size()).isEqualTo(3); assertThat(lastDirection).isEqualTo(StateChange.BACKWARD); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(baker); assertThat(lastDirection).isEqualTo(StateChange.BACKWARD); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(able); assertThat(lastDirection).isEqualTo(StateChange.BACKWARD); assertThat(flow.goBack()).isFalse(); } @Test public void setObjectToMissingObjectPushes() { List<Object> history = HistoryBuilder.from(Arrays.<Parcelable>asList(able, baker)).build(); Backstack flow = new Backstack(history); flow.setStateChanger(new FlowDispatcher(), Backstack.INITIALIZE); assertThat(history.size()).isEqualTo(2); flow.goTo(charlie); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(charlie); assertThat(lastStack.size()).isEqualTo(3); assertThat(lastDirection).isEqualTo(StateChange.FORWARD); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(baker); assertThat(lastDirection).isEqualTo(StateChange.BACKWARD); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(able); assertThat(lastDirection).isEqualTo(StateChange.BACKWARD); assertThat(flow.goBack()).isFalse(); } @Test public void setObjectKeepsOriginal() { List<Object> history = HistoryBuilder.from(Arrays.<Parcelable>asList(able, baker)).build(); Backstack flow = new Backstack(history); flow.setStateChanger(new FlowDispatcher(), Backstack.INITIALIZE); assertThat(history.size()).isEqualTo(2); flow.goTo(new TestKey("Able")); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(new TestKey("Able")); assertThat(lastStack.get(lastStack.size() - 1) == able).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isSameAs(able); assertThat(lastStack.size()).isEqualTo(1); assertThat(lastDirection).isEqualTo(StateChange.BACKWARD); } @Test public void replaceHistoryResultsInLengthOneHistory() { List<Object> history = HistoryBuilder.from(Arrays.<Parcelable>asList(able, baker, charlie)).build(); Backstack flow = new Backstack(history); flow.setStateChanger(new FlowDispatcher(), Backstack.INITIALIZE); assertThat(history.size()).isEqualTo(3); flow.setHistory(HistoryBuilder.single(delta), StateChange.REPLACE); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(new TestKey("Delta")); assertThat(lastStack.get(lastStack.size() - 1) == delta).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isSameAs(delta); assertThat(lastStack.size()).isEqualTo(1); assertThat(lastDirection).isEqualTo(StateChange.REPLACE); } @Test public void replaceTopDoesNotAlterHistoryLength() { List<Object> history = HistoryBuilder.from(Arrays.<Parcelable>asList(able, baker, charlie)).build(); Backstack flow = new Backstack(history); flow.setStateChanger(new FlowDispatcher(), Backstack.INITIALIZE); assertThat(history.size()).isEqualTo(3); flow.setHistory(HistoryBuilder.from(flow).removeLast().add(delta).build(), StateChange.REPLACE); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(new TestKey("Delta")); assertThat(lastStack.get(lastStack.size() - 1) == delta).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isSameAs(delta); assertThat(lastStack.size()).isEqualTo(3); assertThat(lastDirection).isEqualTo(StateChange.REPLACE); } @SuppressWarnings({"deprecation", "CheckResult"}) @Test public void setHistoryKeepsOriginals() { TestKey able = new TestKey("Able"); TestKey baker = new TestKey("Baker"); TestKey charlie = new TestKey("Charlie"); TestKey delta = new TestKey("Delta"); List<Object> history = HistoryBuilder.from(Arrays.<Parcelable>asList(able, baker, charlie, delta)).build(); Backstack flow = new Backstack(history); flow.setStateChanger(new FlowDispatcher(), Backstack.INITIALIZE); assertThat(history.size()).isEqualTo(4); TestKey echo = new TestKey("Echo"); TestKey foxtrot = new TestKey("Foxtrot"); List<Object> newHistory = HistoryBuilder.from(Arrays.<Parcelable>asList(able, baker, echo, foxtrot)).build(); flow.setHistory(newHistory, StateChange.REPLACE); assertThat(lastStack.size()).isEqualTo(4); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(foxtrot); flow.goBack(); assertThat(lastStack.size()).isEqualTo(3); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(echo); flow.goBack(); assertThat(lastStack.size()).isEqualTo(2); assertThat(lastStack.get(lastStack.size() - 1)).isSameAs(baker); flow.goBack(); assertThat(lastStack.size()).isEqualTo(1); assertThat(lastStack.get(lastStack.size() - 1)).isSameAs(able); } static class Picky implements Parcelable { final String value; Picky(String value) { this.value = value; } protected Picky(Parcel in) { value = in.readString(); } public static final Creator<Picky> CREATOR = new Creator<Picky>() { @Override public Picky createFromParcel(Parcel in) { return new Picky(in); } @Override public Picky[] newArray(int size) { return new Picky[size]; } }; @Override public boolean equals(Object o) { if(this == o) { return true; } if(o == null || getClass() != o.getClass()) { return false; } Picky picky = (Picky) o; return value.equals(picky.value); } @Override public int hashCode() { return value.hashCode(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(value); } } @Test public void setCallsEquals() { List<Object> history = HistoryBuilder.newBuilder() .addAll(Arrays.<Parcelable>asList(new Picky("Able"), new Picky("Baker"), new Picky("Charlie"), new Picky("Delta"))) .build(); Backstack flow = new Backstack(history); flow.setStateChanger(new FlowDispatcher(), Backstack.INITIALIZE); assertThat(history.size()).isEqualTo(4); flow.goTo(new Picky("Charlie")); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(new Picky("Charlie")); assertThat(lastStack.size()).isEqualTo(3); assertThat(lastDirection).isEqualTo(StateChange.BACKWARD); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(new Picky("Baker")); assertThat(lastDirection).isEqualTo(StateChange.BACKWARD); assertThat(flow.goBack()).isTrue(); assertThat(lastStack.get(lastStack.size() - 1)).isEqualTo(new Picky("Able")); assertThat(lastDirection).isEqualTo(StateChange.BACKWARD); assertThat(flow.goBack()).isFalse(); } }