package Ġio . quart ic . app . sync Ċ Ċ import Ġcom . n ha ar man . mockito _ kotlin .* Ċ import Ġio . quart ic . app . Build Config Ċ import Ġio . quart ic . app . api . Backend Api Ċ import Ġio . quart ic . app . state . Application State Ċ import Ġio . quart ic . tracker . api . Device Information Ċ import Ġio . quart ic . tracker . api . Sensor Value Ċ import Ġio . quart ic . tracker . api . Upload Request Ċ import Ġorg . junit . Test Ċ import Ġorg . mockito . Mockito . RET URNS _ DE EP _ ST UB S Ċ import Ġorg . mockito . stub bing . O ngo ing Stub bing Ċ import Ġretrofit 2 . Response . error Ċ import Ġretrofit 2 . adapter . rx java . HttpException Ċ import Ġrx . Observable . error Ċ import Ġrx . Observable . just Ċ Ċ class ĠBatch Uploader Should Ġ{ ĊĠĠĠ Ġprivate Ġval Ġstate Ġ= Ġmock < Application State >( RET URNS _ DE EP _ ST UB S ) ĊĠĠĠ Ġprivate Ġval Ġbackend Ġ= Ġmock < Backend Api >() ĊĠĠĠ Ġprivate Ġval Ġget Battery Level Ġ= Ġmock < () Ġ-> ĠInt > Ġ{} ĊĠĠĠ Ġprivate Ġval ĠgetCurrent Time Millis Ġ= Ġmock < () Ġ-> ĠLong > Ġ{} ĊĠĠĠ Ġprivate Ġval Ġget Device Information Ġ= Ġ{ ĠDevice Information (" device ", Ġ" manufacturer ", Ġ" model ") Ġ} ĊĠĠĠ Ġprivate Ġval Ġup loader Ġ= ĠBatch Uploader ( state , Ġbackend , Ġget Battery Level , ĠgetCurrent Time Millis , Ġget Device Information ) ĊĊĠĠĠ Ġprivate Ġval Ġparams A Ġ= ĠState ( listOf ( sensor Value ( 6 9 ), Ġsensor Value ( 7 0 )), Ġ 1 2 3 , Ġ 4 2 , Ġ 5 ) ĊĠĠĠ Ġprivate Ġval Ġparams B Ġ= ĠState ( listOf ( sensor Value ( 7 1 ), Ġsensor Value ( 7 2 )), Ġ 1 2 6 , Ġ 3 9 , Ġ 3 ) ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġdo _ nothing _ if _ user _ id _ not _ set () Ġ{ ĊĠĠĠĠĠĠĠ Ġmock State ( params A ) ĊĠĠĠĠĠĠĠ Ġwhenever ( state . userId ). thenReturn ( null ) ĊĠĠĠĠĠĠĠ Ġwhenever ( state . database . back log Size ). thenReturn ( 5 ) Ġ Ġ// ĠThis Ġwould Ġotherwise Ġallow Ġa Ġsync Ġto Ġoccur ĊĊĠĠĠĠĠĠĠ Ġup loader . upload () ĊĊĠĠĠĠĠĠĠ Ġverify ( state , Ġnever ()). last At tempted Sync Time Ġ= Ġany () ĊĠĠĠĠĠĠĠ Ġverify ( state , Ġnever ()). last Sync Time Ġ= Ġany () ĊĠĠĠĠĠĠĠ Ġverify ( backend , Ġnever ()). upload ( any ()) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġdo _ nothing _ except _ log _ sync _ times _ if _ back log _ is _ empty () Ġ{ ĊĠĠĠĠĠĠĠ Ġmock State () ĊĠĠĠĠĠĠĠ Ġwhenever ( state . userId ). thenReturn (" 1 2 3 4 ") ĠĠĠĠĠĠĠĠĠĠĠĠ Ġ// ĠThis Ġwould Ġotherwise Ġallow Ġa Ġsync Ġto Ġoccur ĊĠĠĠĠĠĠĠ Ġwhenever ( state . database . back log Size ). thenReturn ( 0 ) ĊĊĠĠĠĠĠĠĠ Ġup loader . upload () ĊĊĠĠĠĠĠĠĠ Ġverify ( state ). last At tempted Sync Time Ġ= ĠINITIAL _ TIMESTAMP ĊĠĠĠĠĠĠĠ Ġverify ( state ). last Sync Time Ġ= ĠFIN AL _ TIMESTAMP ĊĠĠĠĠĠĠĠ Ġverify ( backend , Ġnever ()). upload ( any ()) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġperform _ upload _ and _ delete _ entries _ and _ log _ success _ if _ user _ id _ set _ and _ non _ empty _ back log () Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Valid Auth () ĊĠĠĠĠĠĠĠ Ġmock Successful Upload () ĊĠĠĠĠĠĠĠ Ġmock State ( params A ) ĊĊĠĠĠĠĠĠĠ Ġup loader . upload () ĊĊĠĠĠĠĠĠĠ Ġverify ( state ). last At tempted Sync Time Ġ= ĠINITIAL _ TIMESTAMP ĊĠĠĠĠĠĠĠ Ġverify ( state ). last Sync Time Ġ= ĠFIN AL _ TIMESTAMP ĊĠĠĠĠĠĠĠ Ġverify ( backend ). upload ( params A . to Request ()) ĊĠĠĠĠĠĠĠ Ġverify ( state . database ). delete ( listOf ( 6 9 , Ġ 7 0 )) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġkeep _ upload ing _ until _ back log _ is _ empty () Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Valid Auth () ĊĠĠĠĠĠĠĠ Ġmock Successful Upload () ĊĠĠĠĠĠĠĠ Ġmock State ( params A , Ġparams B ) ĊĊĠĠĠĠĠĠĠ Ġup loader . upload () ĊĊĠĠĠĠĠĠĠ Ġverify ( backend ). upload ( params A . to Request ()) ĊĠĠĠĠĠĠĠ Ġverify ( backend ). upload ( params B . to Request ()) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġstop _ loop ing _ and _ not _ record _ success _ if _ back log _ fails () Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Valid Auth () ĊĠĠĠĠĠĠĠ Ġmock Un successful Upload ( 5 0 0 ) ĊĠĠĠĠĠĠĠ Ġmock State ( params A , Ġparams B ) ĊĊĠĠĠĠĠĠĠ Ġup loader . upload () ĊĊĠĠĠĠĠĠĠ Ġverify ( state , Ġnever ()). last Sync Time Ġ= Ġany () ĊĠĠĠĠĠĠĠ Ġverify ( backend , Ġtimes ( 1 )). upload ( any ()) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġnot _ delete _ entries _ if _ upload _ fails () Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Valid Auth () ĊĠĠĠĠĠĠĠ Ġmock Un successful Upload ( 5 0 0 ) ĊĠĠĠĠĠĠĠ Ġmock State ( params A ) ĊĊĠĠĠĠĠĠĠ Ġup loader . upload () ĊĊĠĠĠĠĠĠĠ Ġverify ( state . database , Ġnever ()). delete ( any ()) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġincrement _ failure _ count _ if _ auth _ error () Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Valid Auth () ĊĠĠĠĠĠĠĠ Ġmock Un successful Upload ( 4 0 1 ) ĊĠĠĠĠĠĠĠ Ġmock State ( params A ) ĊĠĠĠĠĠĠĠ Ġwhenever ( state . num Con secutive Sync Auth Failures ). thenReturn ( 1 ) ĊĊĠĠĠĠĠĠĠ Ġup loader . upload () ĊĊĠĠĠĠĠĠĠ Ġverify ( state ). num Con secutive Sync Auth Failures Ġ= Ġ 2 ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġnot _ increment _ failure _ count _ if _ other _ error () Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Valid Auth () ĊĠĠĠĠĠĠĠ Ġmock Un successful Upload ( 4 0 3 ) ĊĠĠĠĠĠĠĠ Ġmock State ( params A ) ĊĊĠĠĠĠĠĠĠ Ġup loader . upload () ĊĊĠĠĠĠĠĠĠ Ġverify ( state , Ġnever ()). num Con secutive Sync Auth Failures Ġ= Ġany () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġclear _ user _ id _ if _ failure _ count _ re aches _ limit () Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Valid Auth () ĊĠĠĠĠĠĠĠ Ġmock Un successful Upload ( 4 0 1 ) ĊĠĠĠĠĠĠĠ Ġmock State ( params A ) ĊĠĠĠĠĠĠĠ Ġwhenever ( state . num Con secutive Sync Auth Failures ). thenReturn ( Batch Uploader . MAX _ CON SEC UT IVE _ AUTH _ FAILURE S ) ĠĠĠ Ġ// ĠShould Ġbe ĠMAX Ġ- Ġ 1 , Ġbut Ġthis Ġmock Ġalways Ġreturns Ġthe Ġsame Ġvalue ĊĊĠĠĠĠĠĠĠ Ġup loader . upload () ĊĊĠĠĠĠĠĠĠ Ġverify ( state ). userId Ġ= Ġnull ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġreset _ failure _ count _ if _ upload _ successful () Ġ{ ĊĠĠĠĠĠĠĠ Ġmock Valid Auth () ĊĠĠĠĠĠĠĠ Ġmock Successful Upload () ĊĠĠĠĠĠĠĠ Ġmock State ( params A ) ĊĊĠĠĠĠĠĠĠ Ġup loader . upload () ĊĊĠĠĠĠĠĠĠ Ġverify ( state ). num Con secutive Sync Auth Failures Ġ= Ġ 0 ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġmock Valid Auth () Ġ{ ĊĠĠĠĠĠĠĠ Ġwhenever ( state . userId ). thenReturn (" 1 2 3 4 ") ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġmock Successful Upload () Ġ{ ĊĠĠĠĠĠĠĠ Ġwhenever ( backend . upload ( any ())). thenReturn ( just ( 1 )) ĠĠĠ Ġ// ĠReturned Ġint Ġisn 't Ġused Ġcurrently ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġmock Un successful Upload ( code : ĠInt ) Ġ{ ĊĠĠĠĠĠĠĠ Ġwhenever ( backend . upload ( any ())). thenReturn ( error ( HttpException ( error < String >( code , Ġmock ())) )) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġmock State ( var arg Ġstates : ĠState ) Ġ{ ĊĠĠĠĠĠĠĠ Ġfun Ġ< T > Ġstub Returns ( mock : ĠT , Ġvalues : ĠList < T >) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ(! values . isEmpty ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġvalues . fold ( wh enever ( mock ), ĠO ngo ing Stub bing < T >:: thenReturn ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġstub Returns ( state . database . sensor Values , Ġstates . map Ġ{ Ġit . values Ġ}) ĊĠĠĠĠĠĠĠ Ġstub Returns ( getCurrent Time Millis (), ĠlistOf ( INITIAL _ TIMESTAMP ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ+ Ġstates . map Ġ{ Ġit . timestamp Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ+ ĠlistOf ( FINAL _ TIMESTAMP ) ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġstub Returns ( get Battery Level (), Ġstates . map Ġ{ Ġit . battery Level Ġ}) ĊĠĠĠĠĠĠĠ Ġstub Returns ( state . database . back log Size , Ġstates ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. map Ġ{ Ġit . back log Size Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. flatMap Ġ{ ĠlistOf ( it , Ġit ) Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ+ ĠlistOf ( 0 ) ĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ// ĠLast Ġvalue Ġto Ġcause Ġtermination ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġdata Ġclass ĠState ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġvalues : ĠList < Sensor Value >, ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġtimestamp : ĠLong , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġback log Size : ĠInt , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġbattery Level : ĠInt ĊĠĠĠ Ġ) Ġ{ ĊĠĠĠĠĠĠĠ Ġfun Ġto Request () Ġ= ĠUpload Request ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġtimestamp Ġ= Ġtimestamp , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġapp Version Code Ġ= ĠBuild Config . VERSION _ CODE , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġapp Version Name Ġ= ĠBuild Config . VERSION _ NAME , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġbattery Level Ġ= Ġbattery Level , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġback log Size Ġ= Ġback log Size , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġvalues Ġ= Ġvalues , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdevice Information Ġ= ĠDevice Information ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdevice Ġ= Ġ" device ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmanufacturer Ġ= Ġ" manufacturer ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmodel Ġ= Ġ" model " ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġsensor Value ( my Id : ĠInt ) Ġ= Ġmock < Sensor Value > Ġ{ Ġon Ġ{ Ġid Ġ} Ġdo Return Ġmy Id Ġ} ĊĊĠĠĠ Ġcompanion Ġobject Ġ{ ĊĠĠĠĠĠĠĠ Ġval ĠINITIAL _ TIMESTAMP Ġ= Ġ 6 6 6 L ĊĠĠĠĠĠĠĠ Ġval ĠFIN AL _ TIMESTAMP Ġ= Ġ 7 7 7 L ĊĠĠĠ Ġ} Ċ }