package Ġcom . inst ac art . formula Ċ Ċ import Ġandroidx . fragment . app . Fragment Ċ import Ġandroidx . lifecycle . Lifecycle Ċ import Ġandroidx . test . core . app . Activity Scenario Ċ import Ġandroidx . test . ext . junit . rules . Activity Scenario Rule Ċ import Ġandroidx . test . ext . junit . runners . Android JUnit 4 Ċ import Ġcom . google . common . truth . Truth . assertThat Ċ import Ġcom . inst ac art . formula . android . Fragment Contract Ċ import Ġcom . inst ac art . formula . android . Fragment Flow State Ċ import Ġcom . inst ac art . formula . android . Fragment Key Ċ import Ġcom . inst ac art . formula . android . Back Callback Ċ import Ġcom . inst ac art . formula . test . Test Contract Ċ import Ġcom . inst ac art . formula . test . Test Contract With Id Ċ import Ġcom . inst ac art . formula . test . Test Fragment Activity Ċ import Ġcom . jak ew h art on . rx relay 3 . Publish Relay Ċ import Ġio . reactivex . rx java 3 . core . Observable Ċ import Ġorg . junit . Before Ċ import Ġorg . junit . Rule Ċ import Ġorg . junit . Test Ċ import Ġorg . junit . rules . Rule Chain Ċ import Ġorg . junit . runner . RunWith Ċ import Ġorg . ro bo lectric . annotation . Looper Mode Ċ Ċ @ RunWith ( Android JUnit 4 :: class ) Ċ class ĠFragment Flow Render View Test Ġ{ ĊĊĠĠĠ Ġclass ĠHead less Fragment Ġ: ĠFragment () ĊĊĠĠĠ Ġprivate Ġvar Ġlast State : ĠFragment Flow State ? Ġ= Ġnull ĊĠĠĠ Ġprivate Ġval Ġstate Change Relay Ġ= ĠPublish Relay . create < Pair < Fragment Contract <* >, ĠAny >>() ĊĠĠĠ Ġprivate Ġvar Ġon Pre Created : Ġ( Test Fragment Activity ) Ġ-> ĠUnit Ġ= Ġ{} ĊĠĠĠ Ġprivate Ġval Ġformula Rule Ġ= ĠTest Formula Rule ( ĊĠĠĠĠĠĠĠ Ġinit Formula Ġ= Ġ{ Ġapp Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠ ĠFormula Android . init ( app ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġactivity < Test Fragment Activity > Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġstore ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġconfigure Activity Ġ= Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġinitial Contract Ġ= ĠTest Contract () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġon Pre Created ( this ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġon Render Fragment State Ġ= Ġ{ Ġa , Ġstate Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlast State Ġ= Ġstate ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcontracts Ġ= Ġ Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġbind Ġ{ Ġkey : ĠTest Contract Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġstate Changes ( key ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġbind Ġ{ Ġkey : ĠTest Contract With Id Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġstate Changes ( key ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġclean Up Ġ= Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlast State Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġ}) ĊĊĠĠĠ Ġprivate Ġval Ġactivity Rule Ġ= ĠActivity Scenario Rule ( Test Fragment Activity :: class . java ) ĊĊĠĠĠ Ġ@ get : Rule Ġval Ġrule Ġ= ĠRule Chain . outer Rule ( formula Rule ). around ( activity Rule ) ĊĠĠĠ Ġlateinit Ġvar Ġscenario : ĠActivity Scenario < Test Fragment Activity > ĊĊĠĠĠ Ġ@ Before Ġfun Ġsetup () Ġ{ ĊĠĠĠĠĠĠĠ Ġscenario Ġ= Ġactivity Rule . scenario ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test Ġfun Ġ` add Ġfragment Ġlifecycle Ġevent `() Ġ{ ĊĠĠĠĠĠĠĠ ĠassertThat ( active Contracts ()). contains Exactly ( Test Contract ()). in Order () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test Ġfun Ġ` pop Ġback stack Ġlifecycle Ġevent `() Ġ{ ĊĠĠĠĠĠĠĠ Ġnavigate To Task Detail () ĊĠĠĠĠĠĠĠ Ġnavigate Back () ĊĊĠĠĠĠĠĠĠ ĠassertThat ( active Contracts ()). contains Exactly ( Test Contract ()). in Order () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test Ġfun Ġ` navig ating Ġforward Ġshould Ġhave Ġboth Ġkeys Ġin Ġback stack `() Ġ{ ĊĠĠĠĠĠĠĠ Ġnavigate To Task Detail () ĊĊĠĠĠĠĠĠĠ ĠassertThat ( active Contracts ()). contains Exactly ( ĊĠĠĠĠĠĠĠĠĠĠĠ ĠTest Contract (), ĊĠĠĠĠĠĠĠĠĠĠĠ ĠTest Contract With Id ( 1 ) ĊĠĠĠĠĠĠĠ Ġ). in Order () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test Ġfun Ġ` ignore Ġhead less Ġfragments `() Ġ{ ĊĠĠĠĠĠĠĠ Ġ// Ġadd Ġhead less Ġfragment ĊĠĠĠĠĠĠĠ Ġscenario . on Activity Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġit . support FragmentManager ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. beginTransaction () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. add ( Head less Fragment (), Ġ" headless ") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. commit Now () ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġassert Visible Contract ( Test Contract ()) ĊĠĠĠĠĠĠĠ ĠassertThat ( active Contracts ()). contains Exactly ( Test Contract ()). in Order () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test Ġfun Ġ` render Ġmodel Ġis Ġpassed Ġto Ġvisible Ġfragment `() Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġactivity Ġ= Ġactivity () ĊĠĠĠĠĠĠĠ Ġsend State Update ( Test Contract (), Ġ" update ") ĊĠĠĠĠĠĠĠ ĠassertThat ( activity . render Calls ). contains Exactly ( Test Contract () Ġto Ġ" update "). in Order () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test Ġfun Ġ` render Ġmodel Ġis Ġnot Ġpassed Ġto Ġnot Ġvisible Ġfragment `() Ġ{ ĊĠĠĠĠĠĠĠ Ġnavigate To Task Detail () ĊĊĠĠĠĠĠĠĠ Ġval Ġactivity Ġ= Ġactivity () ĊĠĠĠĠĠĠĠ Ġsend State Update ( Test Contract (), Ġ" update ") ĊĠĠĠĠĠĠĠ ĠassertThat ( activity . render Calls ). isEqualTo ( emptyList < Any >()) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test Ġfun Ġ` visible Ġfragments Ġare Ġupdated Ġwhen Ġnavig ating `() Ġ{ ĊĠĠĠĠĠĠĠ Ġnavigate To Task Detail () ĊĊĠĠĠĠĠĠĠ Ġval Ġcontract Ġ= ĠTest Contract With Id ( 1 ) ĊĊĠĠĠĠĠĠĠ Ġval Ġactivity Ġ= Ġactivity () ĊĠĠĠĠĠĠĠ Ġsend State Update ( contract , Ġ" update ") ĊĠĠĠĠĠĠĠ ĠassertThat ( activity . render Calls ). contains Exactly ( contract Ġto Ġ" update "). in Order () ĊĊĠĠĠĠĠĠĠ Ġnavigate Back () ĊĊĠĠĠĠĠĠĠ Ġsend State Update ( contract , Ġ" update - two ") ĊĠĠĠĠĠĠĠ ĠassertThat ( activity . render Calls ). contains Exactly ( contract Ġto Ġ" update "). in Order () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Looper Mode ( Looper Mode . Mode . LEG ACY ) ĊĠĠĠ Ġ@ Test Ġfun Ġ` deleg ates Ġback Ġpress Ġto Ġcurrent Ġrender Ġmodel `() Ġ{ ĊĠĠĠĠĠĠĠ Ġnavigate To Task Detail () ĊĊĠĠĠĠĠĠĠ Ġvar Ġback Pressed Ġ= Ġ 0 ĊĊĠĠĠĠĠĠĠ Ġval Ġcontract Ġ= ĠTest Contract With Id ( 1 ) ĊĠĠĠĠĠĠĠ Ġsend State Update ( contract , ĠBack Callback Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġback Pressed Ġ+= Ġ 1 ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtrue ĊĠĠĠĠĠĠĠ Ġ}) ĊĊĠĠĠĠĠĠĠ Ġnavigate Back () ĊĠĠĠĠĠĠĠ Ġnavigate Back () ĊĊĠĠĠĠĠĠĠ ĠassertThat ( back Pressed ). isEqualTo ( 2 ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test Ġfun Ġ` activity Ġrestart `() Ġ{ ĊĠĠĠĠĠĠĠ Ġnavigate To Task Detail () ĊĊĠĠĠĠĠĠĠ Ġval Ġprevious Ġ= Ġactivity () ĊĊĠĠĠĠĠĠĠ Ġscenario . re create () ĊĊĠĠĠĠĠĠĠ Ġ// ĠVerify Ġthat Ġactivity Ġhas Ġchanged ĊĠĠĠĠĠĠĠ Ġval Ġnew Ġ= Ġactivity () ĊĠĠĠĠĠĠĠ ĠassertThat ( previous ). isNot EqualTo ( new ) ĊĊĠĠĠĠĠĠĠ Ġassert Visible Contract ( Test Contract With Id ( 1 )) ĊĠĠĠĠĠĠĠ Ġ// ĠBoth Ġcontracts Ġshould Ġbe Ġactive . ĊĠĠĠĠĠĠĠ ĠassertThat ( active Contracts ()). contains Exactly ( Test Contract (), ĠTest Contract With Id ( 1 )). in Order () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test Ġfun Ġ` process Ġdeath Ġim itation `() Ġ{ ĊĠĠĠĠĠĠĠ Ġnavigate To Task Detail () ĊĊĠĠĠĠĠĠĠ Ġval Ġprevious Ġ= Ġactivity () ĊĠĠĠĠĠĠĠ Ġformula Rule . fake Process Death () ĊĊĠĠĠĠĠĠĠ Ġscenario . re create () ĊĊĠĠĠĠĠĠĠ Ġ// ĠVerify Ġthat Ġactivity Ġhas Ġchanged ĊĠĠĠĠĠĠĠ Ġval Ġnew Ġ= Ġactivity () ĊĠĠĠĠĠĠĠ ĠassertThat ( previous ). isNot EqualTo ( new ) ĊĊĠĠĠĠĠĠĠ Ġ// ĠWhen Ġactivity Ġis Ġre created , Ġit Ġfirst Ġtriggers Ġcurrent Ġfragment Ġand ĊĠĠĠĠĠĠĠ Ġ// Ġthen Ġloads Ġones Ġfrom Ġback stack ĊĠĠĠĠĠĠĠ Ġassert Visible Contract ( Test Contract With Id ( 1 )) ĊĠĠĠĠĠĠĠ ĠassertThat ( active Contracts ()). contains Exactly ( Test Contract With Id ( 1 ), ĠTest Contract ()). in Order () ĊĊĠĠĠĠĠĠĠ Ġnavigate Back () ĊĊĠĠĠĠĠĠĠ Ġassert Visible Contract ( Test Contract ()) ĊĠĠĠĠĠĠĠ ĠassertThat ( active Contracts ()). contains Exactly ( Test Contract ()). in Order () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test Ġfun Ġ` add Ġmultiple Ġfragments Ġwith Ġsame Ġfragment Ġkey `() Ġ{ ĊĠĠĠĠĠĠĠ Ġscenario . moveTo State ( Lifecycle . State . RE SU MED ) ĊĊĠĠĠĠĠĠĠ Ġnavigate To Task Detail ( id Ġ= Ġ 1 ) ĊĠĠĠĠĠĠĠ Ġnavigate To Task Detail ( id Ġ= Ġ 2 ) ĊĠĠĠĠĠĠĠ Ġnavigate To Task Detail ( id Ġ= Ġ 1 ) ĊĊĠĠĠĠĠĠĠ Ġassert Fragment View Is Created ( Test Contract With Id ( 1 )) ĊĠĠĠĠĠĠĠ ĠassertThat ( active Contracts ()). contains Exactly ( Test Contract (), ĠTest Contract With Id ( 1 ), ĠTest Contract With Id ( 2 ), ĠTest Contract With Id ( 1 )). in Order () ĊĊĠĠĠĠĠĠĠ Ġnavigate Back () ĊĠĠĠĠĠĠĠ Ġnavigate Back () ĊĊĠĠĠĠĠĠĠ Ġassert Fragment View Is Created ( Test Contract With Id ( 1 )) ĊĠĠĠĠĠĠĠ ĠassertThat ( active Contracts ()). contains Exactly ( Test Contract (), ĠTest Contract With Id ( 1 )). in Order () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġnavigate Back () Ġ{ ĊĠĠĠĠĠĠĠ Ġscenario . on Activity Ġ{ Ġit . on Back Pressed () Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġnavigate To Task Detail ( id : ĠInt Ġ= Ġ 1 , Ġallow State Loss : ĠBoolean Ġ= Ġfalse ) Ġ{ ĊĠĠĠĠĠĠĠ Ġscenario . on Activity Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġit . navigate To ( Test Contract With Id ( id ), Ġallow State Loss Ġ= Ġallow State Loss ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġassert Fragment View Is Created ( key : ĠFragment Key ) Ġ{ ĊĠĠĠĠĠĠĠ Ġscenario . on Activity Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġview Ġ= Ġit . support FragmentManager . find Fragment By Tag ( key . tag )?. view ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( view ). isNotNull () ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġactivity (): ĠTest Fragment Activity Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġscenario . activity () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġactive Contracts (): ĠList < Fragment Key > Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġscenario . get Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlast State !!. active Ids . map Ġ{ Ġit . key Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġassert Visible Contract ( contract : ĠFragment Contract <* >) Ġ{ ĊĠĠĠĠĠĠĠ Ġassert No Duplicates ( contract ) ĊĠĠĠĠĠĠĠ Ġ// ĠTODO : Ġwould Ġbe Ġbest Ġto Ġtest Ġvisible State () Ġhowever Ġ` Fragment Flow State . states ` Ġis Ġempty ĊĠĠĠĠĠĠĠ ĠassertThat ( scenario . get Ġ{ Ġlast State ?. visible Ids ?. last OrNull ()?. key Ġ}). isEqualTo ( contract ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġassert No Duplicates ( contract : ĠFragment Contract <* >) Ġ{ ĊĠĠĠĠĠĠĠ ĠassertThat ( last State ?. visible Ids ?. count Ġ{ Ġit . key Ġ== Ġcontract Ġ}). isEqualTo ( 1 ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġ< T Ġ: ĠAny > Ġsend State Update ( contract : ĠFragment Contract < T >, Ġupdate : ĠT ) Ġ{ ĊĠĠĠĠĠĠĠ Ġstate Change Relay . accept ( Pair ( contract , Ġupdate )) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġstate Changes ( contract : ĠFragment Contract < *> ): ĠObservable < Any > Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġstate Change Relay ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. filter Ġ{ Ġevent Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġevent . first Ġ== Ġcontract ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. map Ġ{ Ġit . second Ġ} ĊĠĠĠ Ġ} Ċ } Ċ