/* Ċ Ġ* ĠCopyright Ġ 2 0 2 1 Ġ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 . sqlite . inspection . test Ċ Ċ import Ġandroid . database . sqlite . SQLite Database Ċ import Ġandroid . os . System Clock . sleep Ċ import Ġandroidx . sqlite . inspection . Database Lock Registry Ċ import Ġandroidx . sqlite . inspection . Sqlite Inspector Protocol . Acquire Database Lock Command Ċ import Ġandroidx . sqlite . inspection . Sqlite Inspector Protocol . Command Ċ import Ġandroidx . sqlite . inspection . Sqlite Inspector Protocol . Error Content . ErrorCode . ERROR _ ISSUE _ WITH _ LOCK ING _ DATABASE Ċ import Ġandroidx . sqlite . inspection . Sqlite Inspector Protocol . Error Content . ErrorCode . ERROR _ NO _ OPEN _ DATABASE _ WITH _ REQUEST ED _ ID Ċ import Ġandroidx . sqlite . inspection . Sqlite Inspector Protocol . Release Database Lock Command Ċ import Ġandroidx . sqlite . inspection . Sqlite Inspector Protocol . Response . OneOf Case . AC Q UIRE _ DATABASE _ LOCK Ċ import Ġandroidx . sqlite . inspection . Sqlite Inspector Protocol . Response . OneOf Case . ERROR _ OCC UR RED Ċ import Ġandroidx . sqlite . inspection . Sqlite Inspector Protocol . Response . OneOf Case . RELEASE _ DATABASE _ LOCK Ċ import Ġandroidx . test . ext . junit . runners . Android JUnit 4 Ċ import Ġandroidx . test . filters . Medium Test Ċ import Ġandroidx . test . filters . Sdk Suppress Ċ import Ġandroidx . test . platform . app . Instrumentation Registry . get Instrumentation Ċ import Ġcom . google . common . truth . Truth . assertThat Ċ import Ġkotlinx . coroutines . as Coroutine Dispatcher Ċ import Ġkotlinx . coroutines . ensure Active Ċ import Ġkotlinx . coroutines . launch Ċ import Ġkotlinx . coroutines . run Blocking Ċ import Ġorg . junit . Rule Ċ import Ġorg . junit . Test Ċ import Ġorg . junit . rules . Temporary Folder Ċ import Ġorg . junit . runner . RunWith Ċ import Ġjava . util . concurrent . Callable Ċ import Ġjava . util . concurrent . Count DownLatch Ċ import Ġjava . util . concurrent . Executors . new Single Thread Executor Ċ import Ġjava . util . concurrent . TimeUnit . SECONDS Ċ import Ġjava . util . concurrent . atomic . Atomic Boolean Ċ import Ġjava . util . concurrent . atomic . Atomic Integer Ċ import Ġjava . util . regex . Pattern . CASE _ INS ENS ITIVE Ċ Ċ @ Medium Test Ċ @ RunWith ( Android JUnit 4 :: class ) Ċ @ Sdk Suppress ( min Sdk Version Ġ= Ġ 2 6 ) Ċ class ĠDatabase Lock ing Test Ġ{ ĊĠĠĠ Ġ@ get : Rule ĊĠĠĠ Ġval Ġtest Environment Ġ= ĠSql ite Inspector Test Environment () ĊĊĠĠĠ Ġ@ get : Rule ĊĠĠĠ Ġval Ġtemporary Folder Ġ= ĠTemporary Folder ( get Instrumentation (). context . cacheDir ) ĊĊĠĠĠ Ġprivate Ġval Ġdatabase Ġ= ĠDatabase (" db 1 ", ĠTable (" t 1 ", ĠlistOf ( Column (" c 1 ", Ġ" int ")) )) ĊĠĠĠ Ġprivate Ġval Ġtable Ġ= Ġdatabase . tables . single () ĊĠĠĠ Ġprivate Ġval Ġcolumn Ġ= Ġtable . columns . single () ĊĊĠĠĠ Ġprivate Ġval Ġvalue 1 Ġ= Ġ 1 3 3 7 ĊĠĠĠ Ġprivate Ġval Ġvalue 2 Ġ= Ġ 2 6 0 0 ĊĊĠĠĠ Ġ// Ġsim ulates Ġan Ġapplication Ġthread Ġ( i . e . Ġnot Ġ' Instr : Ġandroidx . test . runner . Android JUnit Runner ') ĊĠĠĠ Ġprivate Ġval Ġapplication Thread Ġ= Ġnew Single Thread Executor () ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ single Lock () Ġ= Ġtest _ lock ing ( sim ultaneous Locks Ġ= Ġ 1 ) ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ sim ultaneous Locks () Ġ= Ġtest _ lock ing ( sim ultaneous Locks Ġ= Ġ 3 ) ĊĊĠĠĠ Ġprivate Ġfun Ġtest _ lock ing ( sim ultaneous Locks : ĠInt ) Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġ// Ġcreate Ġa Ġnon - empty Ġdatabase ĊĠĠĠĠĠĠĠ Ġval Ġinstance Ġ= Ġdatabase . create Instance ( temporary Folder , Ġwrite A head Logging Enabled Ġ= Ġtrue ) ĊĠĠĠĠĠĠĠ Ġval Ġdatabase Id Ġ= Ġtest Environment . inspect Database ( instance ) ĊĠĠĠĠĠĠĠ Ġinsert Value ( instance , Ġvalue 1 ) ĊĠĠĠĠĠĠĠ ĠassertThat ( getValue Sum ( instance )). isEqualTo ( value 1 ) ĊĊĠĠĠĠĠĠĠ Ġ// Ġlock Ġthe Ġdatabase ĊĠĠĠĠĠĠĠ Ġval Ġlock Ids Ġ= Ġ( 1 .. sim ultaneous Locks ). map Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtest Environment . send Command ( acquire Lock Command ( database Id )). let Ġ{ Ġresponse Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . one Of Case ). isEqualTo ( AC Q UIRE _ DATABASE _ LOCK ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġresponse . acquire Database Lock . lock Id ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ ĠassertThat ( lock Ids . toSet (). single ()) Ġ// Ġexpecting Ġsimultaneous Ġlocks Ġto Ġhave Ġthe Ġsame Ġid ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. is GreaterThan ( 0 ) Ġ// Ġzero Ġcould Ġmean Ġunset ĊĊĠĠĠĠĠĠĠ Ġ// Ġtry Ġperforming Ġan Ġinsert Ġoperation Ġwhile Ġthe Ġdatabase Ġis Ġlocked ĊĠĠĠĠĠĠĠ Ġval Ġinsert Job Ġ= Ġlaunch ( application Thread . as Coroutine Dispatcher ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġinsert Value ( instance , Ġvalue 2 ) ĊĠĠĠĠĠĠĠ Ġ}. also Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġit . start () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġit . ensure Active () ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ ĠassertThat ( getValue Sum ( instance )). isEqualTo ( value 1 ) Ġ// Ġno Ġchange Ġbecause Ġdatabase Ġis Ġlocked ĊĊĠĠĠĠĠĠĠ Ġ// Ġrelease Ġthe Ġlock ĊĠĠĠĠĠĠĠ Ġlock Ids . forEach Ġ{ Ġlock Id Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠ Ġinsert Job . ensure Active () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtest Environment . send Command ( release Lock Command ( lock Id )). let Ġ{ Ġresponse Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . one Of Case ). isEqualTo ( RELEASE _ DATABASE _ LOCK ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġinsert Job . join () ĊĊĠĠĠĠĠĠĠ Ġ// Ġensure Ġthe Ġpreviously Ġblocked Ġinsert Ġoperation Ġsucceeded ĊĠĠĠĠĠĠĠ ĠassertThat ( getValue Sum ( instance )). isEqualTo ( value 1 Ġ+ Ġvalue 2 ) Ġ// Ġinsert Ġsucceeded ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ lock Ids Unique Per Db () Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġdb Ids Ġ= ĠlistOf (" db 1 ", Ġ" db 2 ", Ġ" db 3 "). map Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġdb Ġ= ĠDatabase ( it ). create Instance ( temporary Folder ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtest Environment . inspect Database ( db ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġval Ġlock Ids Ġ= Ġdb Ids . map Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtest Environment . send Command ( acquire Lock Command ( it )). acquire Database Lock . lock Id ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ ĠassertThat ( lock Ids . toSet ()). has Size ( db Ids . size ) ĊĠĠĠĠĠĠĠ ĠassertThat ( lock Ids . min OrNull () Ġ?: Ġ 0 ). is GreaterThan ( 0 ) ĊĊĠĠĠĠĠĠĠ Ġlock Ids . forEach Ġ{ Ġtest Environment . send Command ( release Lock Command ( it )) Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ timeout Error () Ġ= Ġwith Lock ing Timeout Override ( 5 0 ) Ġ{ ĊĠĠĠĠĠĠĠ Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġcreate Ġa Ġnon - empty Ġdatabase ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġinstance Ġ= Ġdatabase . create Instance ( temporary Folder , Ġwrite A head Logging Enabled Ġ= Ġtrue ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġdatabase Id Ġ= Ġtest Environment . inspect Database ( instance ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġinstance . beginTransaction () Ġ// Ġguarantees Ġa Ġtimeout ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġtry Ġto Ġlock Ġthe Ġdatabase Ġ( expect ing Ġa Ġfailure ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtest Environment . send Command ( acquire Lock Command ( database Id )). let Ġ{ Ġresponse Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġverify Ġexpected Ġerror Ġresponse ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . one Of Case ). isEqualTo ( ERROR _ OCC UR RED ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . error Occurred . content . message ). matches ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" .* try ing Ġto Ġlock Ġ.* database Ġ.* TimeoutException .* ". to Pattern () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . error Occurred . content . errorCode ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. isEqualTo ( ERROR _ ISSUE _ WITH _ LOCK ING _ DATABASE ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ database Already Closed () Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġ// Ġcreate Ġa Ġnon - empty Ġdatabase ĊĠĠĠĠĠĠĠ Ġval Ġinstance Ġ= Ġdatabase . create Instance ( temporary Folder , Ġwrite A head Logging Enabled Ġ= Ġtrue ) ĊĠĠĠĠĠĠĠ Ġval Ġdatabase Id Ġ= Ġtest Environment . inspect Database ( instance ) ĊĠĠĠĠĠĠĠ Ġinstance . close () ĊĊĠĠĠĠĠĠĠ Ġ// Ġtry Ġto Ġlock Ġthe Ġdatabase Ġ( expect ing Ġa Ġfailure ) ĊĠĠĠĠĠĠĠ Ġtest Environment . send Command ( acquire Lock Command ( database Id )). let Ġ{ Ġresponse Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġverify Ġexpected Ġerror Ġresponse ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . one Of Case ). isEqualTo ( ERROR _ OCC UR RED ) ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . error Occurred . content . message ). matches ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" .* database Ġ.* already Ġ.* closed .* ". to Pattern () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . error Occurred . content . errorCode ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. isEqualTo ( ERROR _ NO _ OPEN _ DATABASE _ WITH _ REQUEST ED _ ID ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ invalid Lock Id () Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġ// Ġtry Ġto Ġlock Ġthe Ġdatabase Ġ( expect ing Ġa Ġfailure ) ĊĠĠĠĠĠĠĠ Ġval Ġinvalid Lock Id Ġ= Ġ 2 2 5 5 ĊĠĠĠĠĠĠĠ Ġtest Environment . send Command ( release Lock Command ( invalid Lock Id )). let Ġ{ Ġresponse Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġverify Ġexpected Ġerror Ġresponse ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . one Of Case ). isEqualTo ( ERROR _ OCC UR RED ) ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . error Occurred . content . message ). matches ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" .* try ing Ġto Ġunlock Ġ.* database Ġ.* no Ġlock Ġwith Ġid .* Ġ$ invalid Lock Id .* " ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. to Pattern ( CASE _ INS ENS ITIVE ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . error Occurred . content . errorCode ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. isEqualTo ( ERROR _ ISSUE _ WITH _ LOCK ING _ DATABASE ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ keep Database Open While Locked () Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġ// Ġcreate Ġa Ġnon - empty Ġdatabase ĊĠĠĠĠĠĠĠ Ġval Ġinstance Ġ= Ġdatabase . create Instance ( temporary Folder , Ġwrite A head Logging Enabled Ġ= Ġtrue ) ĊĠĠĠĠĠĠĠ Ġval Ġdatabase Id Ġ= Ġtest Environment . inspect Database ( instance ) ĊĊĠĠĠĠĠĠĠ Ġ// Ġlock Ġthe Ġdatabase ĊĠĠĠĠĠĠĠ Ġval Ġlock Id Ġ= Ġtest Environment . send Command ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġacquire Lock Command ( database Id ) ĊĠĠĠĠĠĠĠ Ġ). acquire Database Lock . lock Id ĊĠĠĠĠĠĠĠ ĠassertThat ( lock Id ). is GreaterThan ( 0 ) ĊĊĠĠĠĠĠĠĠ Ġ// Ġclose Ġthe Ġdatabase Ġinstance Ġ( un successfully ) ĊĠĠĠĠĠĠĠ Ġinstance . close () ĊĠĠĠĠĠĠĠ ĠassertThat ( instance . isOpen ). isTrue () ĊĊĠĠĠĠĠĠĠ Ġ// Ġrelease Ġlock ĊĠĠĠĠĠĠĠ ĠassertThat ( test Environment . send Command ( release Lock Command ( lock Id )). one Of Case ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. isEqualTo ( RELEASE _ DATABASE _ LOCK ) ĊĊĠĠĠĠĠĠĠ Ġ// Ġsuccessful Ġclose Ġas Ġdb Ġclosed Ġnotification Ġreceived ĊĠĠĠĠĠĠĠ ĠassertThat ( instance . isOpen ). isFalse () ĊĠĠĠĠĠĠĠ Ġtest Environment . assert No Queued Events () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ parallel Requests _ first Times Out () Ġ= Ġwith Lock ing Timeout Override ( 5 0 0 ) Ġ{ ĊĠĠĠĠĠĠĠ Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġcreate Ġand Ġinspect Ġtwo Ġdatabases ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġ( db 1 , Ġdb 2 ) Ġ= ĠlistOf (" db 1 ", Ġ" db 2 "). map Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠDatabase ( it , Ġtable ). create Instance ( temporary Folder ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġ( id 1 , Ġid 2 ) Ġ= ĠlistOf ( db 1 , Ġdb 2 ). map Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġtest Environment . inspect Database ( it ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġlock Ġthe Ġfirst Ġdatabase Ġ( app Ġthread ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapplication Thread . submit Ġ{ Ġdb 1 . beginTransaction () Ġ}. get ( 2 , ĠSE CON DS ) ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġrequest Ġlock Ġon Ġboth Ġdatabases ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġl atch Ġ= ĠCount DownLatch ( 1 ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġ( lock Task 1 , Ġlock Task 2 ) Ġ= ĠlistOf ( id 1 , Ġid 2 ). map Ġ{ Ġdb Id Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnew Single Thread Executor (). submit ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠCallable Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( db Id Ġ== Ġid 1 ) Ġl atch . count Down () Ġelse Ġl atch . await () Ġ// Ġdb 1 Ġfirst ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġrun Blocking Ġ{ Ġtest Environment . send Command ( acquire Lock Command ( db Id )) Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġverify Ġdb 1 Ġtimeout , Ġdb 2 Ġsuccess ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġ( result 1 , Ġresult 2 ) Ġ= ĠlistOf ( lock Task 1 , Ġlock Task 2 ). map Ġ{ Ġit . get ( 2 , ĠSE CON DS ) Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( result 1 . one Of Case ). isEqualTo ( ERROR _ OCC UR RED ) ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( result 2 . one Of Case ). isEqualTo ( AC Q UIRE _ DATABASE _ LOCK ) ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġunlock Ġthe Ġfirst Ġdatabase Ġ( app Ġthread ) Ġand Ġretry Ġlocking ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapplication Thread . submit Ġ{ Ġdb 1 . end Transaction () Ġ}. get ( 2 , ĠSE CON DS ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtest Environment . send Command ( acquire Lock Command ( id 1 )). let Ġ{ Ġresponse Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( response . one Of Case ). isEqualTo ( AC Q UIRE _ DATABASE _ LOCK ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ app Resume s After Lock Released () Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġ// Ġcreate Ġdatabase ĊĠĠĠĠĠĠĠ Ġval Ġdb Ġ= Ġdatabase . create Instance ( temporary Folder ) ĊĠĠĠĠĠĠĠ Ġval Ġid Ġ= Ġtest Environment . inspect Database ( db ) ĊĊĠĠĠĠĠĠĠ Ġ// Ġstart Ġa Ġjob Ġinserting Ġvalues Ġat Ġapp Ġthread ĊĠĠĠĠĠĠĠ Ġval Ġinsert Count Ġ= ĠAtomicInteger ( 0 ) ĊĠĠĠĠĠĠĠ Ġvar Ġl atch Ġ= ĠCount DownLatch ( 2 ) Ġ// Ġallows Ġfor Ġa Ġfew Ġinsert Ġoperations Ġto Ġsucceed ĊĠĠĠĠĠĠĠ Ġval Ġinsert Task Ġ= Ġapplication Thread . submit Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġwhile Ġ( true ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdb . exec SQL (" insert Ġinto Ġ${ table . name } Ġvalues Ġ( 1 )") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġinsert Count . increment AndGet () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġl atch . count Down () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġsleep ( 1 0 ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ ĠassertThat ( latch . await ( 2 , ĠSE CON DS )). isTrue () ĊĊĠĠĠĠĠĠĠ Ġ// Ġlock Ġthe Ġdatabase ĊĠĠĠĠĠĠĠ Ġval Ġlock Id Ġ= Ġtest Environment . send Command ( acquire Lock Command ( id )). acquire Database Lock . lock Id ĊĠĠĠĠĠĠĠ ĠassertThat ( lock Id ). is GreaterThan ( 0 ) Ġ// Ġcheck Ġif Ġlocking Ġsucceeded ĊĊĠĠĠĠĠĠĠ Ġ// Ġcheck Ġif Ġthe Ġlock Ġis Ġblocking Ġthe Ġinsert Ġtask ĊĠĠĠĠĠĠĠ Ġval Ġcount When Locked Ġ= Ġinsert Count . get () ĊĠĠĠĠĠĠĠ Ġsleep ( 1 0 0 ) Ġ// Ġallows Ġfor Ġa Ġfew Ġinsert Ġoperations Ġto Ġhappen Ġ( if Ġthe Ġlock Ġdoesn 't Ġwork ) ĊĠĠĠĠĠĠĠ ĠassertThat ( insert Count . get ()). isEqualTo ( count When Locked ) ĊĊĠĠĠĠĠĠĠ Ġ// Ġrelease Ġthe Ġlock ĊĠĠĠĠĠĠĠ Ġtest Environment . send Command ( release Lock Command ( lock Id )). also Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( it . one Of Case ). isEqualTo ( RELEASE _ DATABASE _ LOCK ) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġ// Ġcheck Ġif Ġthe Ġinsert Ġtask Ġres umed Ġafter Ġunlock ing ĊĠĠĠĠĠĠĠ Ġl atch Ġ= ĠCount DownLatch ( 2 ) Ġ// Ġallows Ġfor Ġa Ġfew Ġinsert Ġoperations Ġto Ġhappen ĊĠĠĠĠĠĠĠ ĠassertThat ( latch . await ( 2 , ĠSE CON DS )). isTrue () ĊĠĠĠĠĠĠĠ ĠassertThat ( insert Count . get ()). is GreaterThan ( count When Locked ) ĊĠĠĠĠĠĠĠ Ġinsert Task . cancel ( true ) ĊĊĠĠĠĠĠĠĠ ĠUnit ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ end ToEnd _ inspector _ lock _ query _ unlock _ wal On () Ġ= ĊĠĠĠĠĠĠĠ Ġtest _ end ToEnd _ inspector _ lock _ query _ unlock ( write A head Logging Enabled Ġ= Ġtrue ) ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġtest _ end ToEnd _ inspector _ lock _ query _ unlock _ wal Off () Ġ= ĊĠĠĠĠĠĠĠ Ġtest _ end ToEnd _ inspector _ lock _ query _ unlock ( write A head Logging Enabled Ġ= Ġfalse ) ĊĊĠĠĠ Ġprivate Ġfun Ġtest _ end ToEnd _ inspector _ lock _ query _ unlock ( ĊĠĠĠĠĠĠĠ Ġwrite A head Logging Enabled : ĠBoolean ĊĠĠĠ Ġ) Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġ// Ġcreate Ġdatabase ĊĠĠĠĠĠĠĠ Ġval Ġdb Ġ= Ġdatabase . create Instance ( temporary Folder , Ġwrite A head Logging Enabled ) ĊĠĠĠĠĠĠĠ Ġinsert Value ( db , Ġvalue 1 ) ĊĠĠĠĠĠĠĠ ĠassertThat ( getValue Sum ( db )). isEqualTo ( value 1 ) ĊĊĠĠĠĠĠĠĠ Ġ// Ġinspect Ġdatabase ĊĠĠĠĠĠĠĠ Ġval Ġdb Id Ġ= Ġtest Environment . inspect Database ( db ) ĊĊĠĠĠĠĠĠĠ Ġ// Ġestablish Ġa Ġlock ĊĠĠĠĠĠĠĠ Ġval Ġlock Id Ġ= ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtest Environment . send Command ( acquire Lock Command ( db Id )). acquire Database Lock . lock Id ĊĠĠĠĠĠĠĠ ĠassertThat ( lock Id ). is GreaterThan ( 0 ) Ġ// Ġcheck Ġthat Ġlocking Ġsucceeded ĊĊĠĠĠĠĠĠĠ Ġ// Ġtry Ġto Ġinsert Ġa Ġvalue Ġon Ġapp Ġthread ĊĠĠĠĠĠĠĠ Ġval Ġapp Insert Done Ġ= ĠAtomic Boolean ( false ) ĊĠĠĠĠĠĠĠ Ġval Ġapp Insert Task Ġ= Ġapplication Thread . submit Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġinsert Value ( db , Ġvalue 2 ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapp Insert Done . set ( true ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ ĠassertThat ( app Insert Done . get ()). isFalse () ĊĊĠĠĠĠĠĠĠ Ġ// Ġquery Ġschema ĊĠĠĠĠĠĠĠ Ġtest Environment . send Command ( Message Factory . create Get Schema Command ( db Id )). let Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( it . getSchema . tables List . map Ġ{ Ġt Ġ-> Ġt . name Ġ}). isEqualTo ( listOf ( table . name )) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġ// Ġrepeat Ġlock Ġ( testing Ġsimultaneous Ġlocks ) ĊĠĠĠĠĠĠĠ Ġtest Environment . send Command ( acquire Lock Command ( db Id )). let Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( it . acquire Database Lock . lock Id ). isEqualTo ( lock Id ) Ġ// Ġthe Ġsame Ġlock Ġid Ġexpected ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġ// Ġquery Ġtable ĊĠĠĠĠĠĠĠ Ġtest Environment . issue Query ( db Id , Ġ" select Ġsum (${ column . name }) Ġfrom Ġ${ table . name } "). let Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( app Insert Done . get ()). isFalse () ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertThat ( it . rows List . single (). values List . single (). longValue . toInt ()). isEqualTo ( value 1 ) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġ// Ġrelease Ġall Ġlocks Ġand Ġverify Ġthe Ġapp Ġthread Ġinsert Ġoperation Ġsucceeds ĊĠĠĠĠĠĠĠ Ġtest Environment . send Command ( release Lock Command ( lock Id )) ĊĠĠĠĠĠĠĠ ĠassertThat ( app Insert Done . get ()). isFalse () ĊĠĠĠĠĠĠĠ Ġtest Environment . send Command ( release Lock Command ( lock Id )) ĊĠĠĠĠĠĠĠ Ġapp Insert Task . get ( 2 , ĠSE CON DS ) ĊĠĠĠĠĠĠĠ ĠassertThat ( app Insert Done . get ()). isTrue () ĊĠĠĠĠĠĠĠ ĠassertThat ( getValue Sum ( db )). isEqualTo ( value 1 Ġ+ Ġvalue 2 ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Suppress (" Same Parameter Value ") ĊĠĠĠ Ġprivate Ġfun Ġwith Lock ing Timeout Override ( override Ms : ĠInt , Ġblock : Ġ() Ġ-> ĠAny ) Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġcurrent Ġ= ĠDatabase Lock Registry . s Timeout Ms ĊĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠDatabase Lock Registry . s Timeout Ms Ġ= Ġoverride Ms ĊĠĠĠĠĠĠĠĠĠĠĠ Ġblock () ĊĠĠĠĠĠĠĠ Ġ} Ġfinally Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠDatabase Lock Registry . s Timeout Ms Ġ= Ġcurrent ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġacquire Lock Command ( database Id : ĠInt ) Ġ= ĠCommand . newBuilder (). set Acquire Database Lock ( ĊĠĠĠĠĠĠĠ ĠAc quire Database Lock Command . newBuilder (). set Database Id ( database Id ) ĊĠĠĠ Ġ). build () ĊĊĠĠĠ Ġprivate Ġfun Ġrelease Lock Command ( lock Id : ĠInt ) Ġ= ĠCommand . newBuilder (). set Release Database Lock ( ĊĠĠĠĠĠĠĠ ĠRelease Database Lock Command . newBuilder (). set Lock Id ( lock Id ) ĊĠĠĠ Ġ). build () ĊĊĠĠĠ Ġprivate Ġfun ĠgetValue Sum ( instance : ĠSQLite Database ): ĠInt Ġ= ĊĠĠĠĠĠĠĠ Ġinstance . compile Statement (" select Ġsum (${ column . name }) Ġfrom Ġ${ table . name }") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. simple Query For Long (). toInt () ĊĊĠĠĠ Ġprivate Ġfun Ġinsert Value ( instance : ĠSQLite Database , Ġvalue : ĠInt ) Ġ= ĊĠĠĠĠĠĠĠ Ġinstance . exec SQL (" insert Ġinto Ġ${ table . name } Ġvalues Ġ($ value )") Ċ }