diff --git a/claimManagement/src/main/java/org/openimis/imisclaims/EnquireActivity.java b/claimManagement/src/main/java/org/openimis/imisclaims/EnquireActivity.java index d5284fa9..f9e48969 100644 --- a/claimManagement/src/main/java/org/openimis/imisclaims/EnquireActivity.java +++ b/claimManagement/src/main/java/org/openimis/imisclaims/EnquireActivity.java @@ -202,11 +202,13 @@ protected AlertDialog ShowDialog(final TextView tv, String msg) { @SuppressLint({"WrongConstant", "Range"}) @Nullable protected Insuree getDataFromDb(String chfid) { - try { - SQLiteDatabase db = openOrCreateDatabase(SQLHandler.DB_NAME_DATA, SQLiteDatabase.OPEN_READONLY, null); - String[] columns = {"CHFID", "Photo", "InsureeName", "DOB", "Gender", "ProductCode", "ProductName", "ExpiryDate", "Status", "DedType", "Ded1", "Ded2", "Ceiling1", "Ceiling2"}; - String[] selectionArgs = {chfid}; - Cursor c = db.query("tblPolicyInquiry", columns, "Trim(CHFID)=?", selectionArgs, null, null, null); + String[] columns = {"CHFID", "Photo", "InsureeName", "DOB", "Gender", "ProductCode", "ProductName", "ExpiryDate", "Status", "DedType", "Ded1", "Ded2", "Ceiling1", "Ceiling2"}; + String[] selectionArgs = {chfid}; + // try-with-resources so the database connection and cursor are always + // closed, including on the exception path; otherwise the connection to + // the shared DB file leaks (and, under tests, causes SQLITE_BUSY). + try (SQLiteDatabase db = openOrCreateDatabase(SQLHandler.DB_NAME_DATA, SQLiteDatabase.OPEN_READONLY, null); + Cursor c = db.query("tblPolicyInquiry", columns, "Trim(CHFID)=?", selectionArgs, null, null, null)) { String name = null; Date dateOfBirth = null; String gender = null; @@ -254,8 +256,6 @@ protected Insuree getDataFromDb(String chfid) { /* validityTo = */ null )); } - c.close(); - db.close(); return new Insuree( /* chfId = */ chfid, /* name = */ Objects.requireNonNull(name), diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/AddItemsTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/AddItemsTest.java index 7f951c9b..0fdd3299 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/AddItemsTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/AddItemsTest.java @@ -37,6 +37,10 @@ public void setup() { ClaimActivity.lvItemList = new java.util.ArrayList<>(); activity = Robolectric.buildActivity(AddItems.class).create().get(); + // Release the real test databases opened in onCreate before swapping in + // the mock, otherwise the leaked connections to the shared DB files make + // later tests in the suite fail with SQLITE_BUSY. + activity.sqlHandler.closeDatabases(); activity.sqlHandler = mockSqlHandler; etItems = activity.findViewById(R.id.etItems); diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/AddServicesTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/AddServicesTest.java index 075d2972..17f38821 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/AddServicesTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/AddServicesTest.java @@ -36,6 +36,10 @@ public void setup() { ClaimActivity.lvServiceList = new java.util.ArrayList<>(); activity = Robolectric.buildActivity(AddServices.class).create().get(); + // Release the real test databases opened in onCreate before swapping in + // the mock, otherwise the leaked connections to the shared DB files make + // later tests in the suite fail with SQLITE_BUSY. + activity.sqlHandler.closeDatabases(); activity.sqlHandler = mockSqlHandler; etServices = activity.findViewById(R.id.etService); diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java index 968aa6f9..a8f74456 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java @@ -37,6 +37,12 @@ public void setup() { .resume() .get(); + // onCreate() already opened the real (shared, on-disk) test databases. + // Close them before replacing the handler with a mock, otherwise the + // open connections leak across the suite and later tests fail with + // SQLITE_BUSY ("Failed to change locale ... database is locked"). + activity.sqlHandler.closeDatabases(); + // Inject mocks activity.sqlHandler = mock(SQLHandler.class); activity.global = mock(Global.class); @@ -199,7 +205,12 @@ public void isValidData_NoItemsAndServices_ReturnsTrue() { @Test public void isValidData_ChfIdMaxLength_ReturnsFalse(){ - activity.etInsureeNumber.setText("12345678901234567890"); + // The CHFID field caps input at 12 characters via an android:maxLength + // filter in the layout, so a longer string is truncated before it ever + // reaches validation. Lower the configured maximum length instead, to + // actually exercise the "exceeds maximum length" validation branch. + activity.chfIdMaxLength = 5; + activity.etInsureeNumber.setText("123456789012"); boolean result = activity.isValidData(); assertFalse(result); } diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/EnquireActivityTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/EnquireActivityTest.java index f5a0cec5..43fc77b9 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/EnquireActivityTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/EnquireActivityTest.java @@ -8,6 +8,7 @@ import android.view.View; import android.widget.ListAdapter; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,6 +47,15 @@ public void setup() { activity.etCHFID.setText("CHF123"); } + @After + public void tearDown() { + // Close the real databases opened in onCreate so the connections to the + // shared test DB files do not leak and cause SQLITE_BUSY in later tests. + if (activity != null && activity.sqlHandler != null) { + activity.sqlHandler.closeDatabases(); + } + } + /* ===================================================== buildEnquireValue() ===================================================== */ diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/ImisActivityTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/ImisActivityTest.java index 8892ba71..355e382f 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/ImisActivityTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/ImisActivityTest.java @@ -16,7 +16,16 @@ private String savedLanguage() { .create() .get(); - return activity.global.getSavedLanguage(); + try { + return activity.global.getSavedLanguage(); + } finally { + // Close the real databases opened in onCreate so the connections to + // the shared test DB files do not leak and cause SQLITE_BUSY in + // later tests of the suite. + if (activity.sqlHandler != null) { + activity.sqlHandler.closeDatabases(); + } + } } @Test diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/MapItemsTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/MapItemsTest.java index f89ad643..9421c142 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/MapItemsTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/MapItemsTest.java @@ -33,6 +33,10 @@ public void setup() { mockCursor = mock(Cursor.class); activity = Robolectric.buildActivity(MapItems.class).create().get(); + // Release the real test databases opened in onCreate before swapping in + // the mock, otherwise the leaked connections to the shared DB files make + // later tests in the suite fail with SQLITE_BUSY. + activity.sqlHandler.closeDatabases(); activity.sqlHandler = mockSqlHandler; } diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/MapServicesTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/MapServicesTest.java index 99a1e27c..d873aafe 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/MapServicesTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/MapServicesTest.java @@ -30,6 +30,10 @@ public void setup() { mockCursor = mock(Cursor.class); activity = Robolectric.buildActivity(MapServices.class).create().get(); + // Release the real test databases opened in onCreate before swapping in + // the mock, otherwise the leaked connections to the shared DB files make + // later tests in the suite fail with SQLITE_BUSY. + activity.sqlHandler.closeDatabases(); activity.sqlHandler = mockSqlHandler; } diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/SearchClaimsActivityTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/SearchClaimsActivityTest.java index dbd68f8d..b9dfae14 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/SearchClaimsActivityTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/SearchClaimsActivityTest.java @@ -7,6 +7,7 @@ import android.widget.EditText; import android.widget.Spinner; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,6 +40,15 @@ public void setup() { dateProcessedFrom = activity.findViewById(R.id.date_processed_from); } + @After + public void tearDown() { + // Close the real databases opened in onCreate so the connections to the + // shared test DB files do not leak and cause SQLITE_BUSY in later tests. + if (activity != null && activity.sqlHandler != null) { + activity.sqlHandler.closeDatabases(); + } + } + @Test public void getStatus_returnsCorrectStatusForAllPositions() { assertNull(activity.getStatus(spinner)); // Position 0 - no selection diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java index a550d6d8..e7846494 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java @@ -130,6 +130,10 @@ public void processClaimResponse_WithRejectedClaim_UpdatesStatusToRejected() thr new PostNewClaims.Result("claim123", PostNewClaims.Result.Status.REJECTED, "Claim rejected"); when(sqlHandler.getClaimUUIDForCode("claim123")).thenReturn("uuid123"); + // setUp() stubs getString(anyInt()) to return "Error message" for every + // resource id. The rejected branch uses the R.string.ClaimRejected + // resource (not the Result message), so stub it explicitly here. + when(resources.getString(R.string.ClaimRejected)).thenReturn("Claim rejected"); JSONArray result = synchronizeService.processClaimResponse(Arrays.asList(rejectedResult));