/*
* Copyright (C) 2022 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 com.android.server.healthconnect;
import static android.Manifest.permission.MIGRATE_HEALTH_CONNECT_DATA;
import static android.health.connect.HealthConnectException.ERROR_UNSUPPORTED_OPERATION;
import static android.health.connect.HealthConnectManager.DATA_DOWNLOAD_STARTED;
import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD;
import static com.android.server.healthconnect.backuprestore.BackupRestore.DATA_DOWNLOAD_STATE_KEY;
import static com.android.server.healthconnect.backuprestore.BackupRestore.DATA_RESTORE_STATE_KEY;
import static com.android.server.healthconnect.backuprestore.BackupRestore.INTERNAL_RESTORE_STATE_STAGING_DONE;
import static com.android.server.healthconnect.backuprestore.BackupRestore.INTERNAL_RESTORE_STATE_STAGING_IN_PROGRESS;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.health.connect.MedicalIdFilter;
import android.health.connect.aidl.HealthConnectExceptionParcel;
import android.health.connect.aidl.IDataStagingFinishedCallback;
import android.health.connect.aidl.IHealthConnectService;
import android.health.connect.aidl.IMigrationCallback;
import android.health.connect.aidl.IReadMedicalResourcesResponseCallback;
import android.health.connect.aidl.MedicalIdFiltersParcel;
import android.health.connect.exportimport.ScheduledExportSettings;
import android.health.connect.migration.MigrationEntityParcel;
import android.health.connect.migration.MigrationException;
import android.health.connect.restore.StageRemoteDataRequest;
import android.healthconnect.cts.utils.AssumptionCheckerRule;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.LocalManagerRegistry;
import com.android.server.appop.AppOpsManagerLocal;
import com.android.server.healthconnect.migration.MigrationCleaner;
import com.android.server.healthconnect.migration.MigrationStateManager;
import com.android.server.healthconnect.migration.MigrationTestUtils;
import com.android.server.healthconnect.migration.MigrationUiStateManager;
import com.android.server.healthconnect.permission.FirstGrantTimeManager;
import com.android.server.healthconnect.permission.HealthConnectPermissionHelper;
import com.android.server.healthconnect.storage.TransactionManager;
import com.android.server.healthconnect.storage.datatypehelpers.PreferenceHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.quality.Strictness;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeoutException;
/** Unit test class for {@link HealthConnectServiceImpl} */
@RunWith(AndroidJUnit4.class)
public class HealthConnectServiceImplTest {
/**
* Health connect service APIs that blocks calls when data sync (ex: backup and restore, data
* migration) is in progress.
*
*
Before adding a method name to this list, make sure the method implementation contains
* the blocking part (i.e: {@link HealthConnectServiceImpl#throwExceptionIfDataSyncInProgress}
* for asynchronous APIs and {@link
* HealthConnectServiceImpl#throwIllegalStateExceptionIfDataSyncInProgress} for synchronous
* APIs).
*
*
Also, consider adding the method to {@link
* android.healthconnect.cts.HealthConnectManagerTest#testDataApis_migrationInProgress_apisBlocked}
* cts test.
*/
public static final Set BLOCK_CALLS_DURING_DATA_SYNC_LIST =
Set.of(
"grantHealthPermission",
"revokeHealthPermission",
"revokeAllHealthPermissions",
"getGrantedHealthPermissions",
"getHealthPermissionsFlags",
"setHealthPermissionsUserFixedFlagValue",
"getHistoricalAccessStartDateInMilliseconds",
"insertRecords",
"aggregateRecords",
"readRecords",
"updateRecords",
"getChangeLogToken",
"getChangeLogs",
"deleteUsingFilters",
"deleteUsingFiltersForSelf",
"getCurrentPriority",
"updatePriority",
"setRecordRetentionPeriodInDays",
"getRecordRetentionPeriodInDays",
"getContributorApplicationsInfo",
"queryAllRecordTypesInfo",
"queryAccessLogs",
"getActivityDates",
"configureScheduledExport",
"getScheduledExportStatus",
"getScheduledExportPeriodInDays",
"getImportStatus",
"runImport",
"readMedicalResources");
/** Health connect service APIs that do not block calls when data sync is in progress. */
public static final Set DO_NOT_BLOCK_CALLS_DURING_DATA_SYNC_LIST =
Set.of(
"startMigration",
"finishMigration",
"writeMigrationData",
"stageAllHealthConnectRemoteData",
"getAllDataForBackup",
"getAllBackupFileNames",
"deleteAllStagedRemoteData",
"setLowerRateLimitsForTesting",
"updateDataDownloadState",
"getHealthConnectDataState",
"getHealthConnectMigrationUiState",
"insertMinDataMigrationSdkExtensionVersion",
"asBinder",
"queryDocumentProviders");
private static final String TEST_URI = "content://com.android.server.healthconnect/testuri";
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public final ExtendedMockitoRule mExtendedMockitoRule =
new ExtendedMockitoRule.Builder(this)
.mockStatic(Environment.class)
.mockStatic(PreferenceHelper.class)
.mockStatic(LocalManagerRegistry.class)
.mockStatic(UserHandle.class)
.mockStatic(TransactionManager.class)
.setStrictness(Strictness.LENIENT)
.build();
@Mock private TransactionManager mTransactionManager;
@Mock private HealthConnectDeviceConfigManager mDeviceConfigManager;
@Mock private HealthConnectPermissionHelper mHealthConnectPermissionHelper;
@Mock private MigrationCleaner mMigrationCleaner;
@Mock private FirstGrantTimeManager mFirstGrantTimeManager;
@Mock private MigrationStateManager mMigrationStateManager;
@Mock private MigrationUiStateManager mMigrationUiStateManager;
@Mock private Context mServiceContext;
@Mock private PreferenceHelper mPreferenceHelper;
@Mock private AppOpsManagerLocal mAppOpsManagerLocal;
@Mock private PackageManager mPackageManager;
@Mock IMigrationCallback mCallback;
@Captor ArgumentCaptor mErrorCaptor;
private Context mContext;
private HealthConnectServiceImpl mHealthConnectService;
private UserHandle mUserHandle;
private File mMockDataDirectory;
private ThreadPoolExecutor mInternalTaskScheduler;
@Rule
public AssumptionCheckerRule mSupportedHardwareRule =
new AssumptionCheckerRule(
android.healthconnect.cts.utils.TestUtils::isHardwareSupported,
"Tests should run on supported hardware only.");
@Before
public void setUp() throws Exception {
when(UserHandle.of(anyInt())).thenCallRealMethod();
mUserHandle = UserHandle.of(UserHandle.myUserId());
when(mServiceContext.getPackageManager()).thenReturn(mPackageManager);
when(mServiceContext.getUser()).thenReturn(mUserHandle);
mInternalTaskScheduler = HealthConnectThreadScheduler.sInternalBackgroundExecutor;
mContext =
new HealthConnectUserContext(
InstrumentationRegistry.getInstrumentation().getContext(), mUserHandle);
mMockDataDirectory = mContext.getDir("mock_data", Context.MODE_PRIVATE);
when(Environment.getDataDirectory()).thenReturn(mMockDataDirectory);
when(PreferenceHelper.getInstance()).thenReturn(mPreferenceHelper);
when(LocalManagerRegistry.getManager(AppOpsManagerLocal.class))
.thenReturn(mAppOpsManagerLocal);
when(TransactionManager.getInitialisedInstance()).thenReturn(mTransactionManager);
mHealthConnectService =
new HealthConnectServiceImpl(
mTransactionManager,
mDeviceConfigManager,
mHealthConnectPermissionHelper,
mMigrationCleaner,
mFirstGrantTimeManager,
mMigrationStateManager,
mMigrationUiStateManager,
mServiceContext);
}
@After
public void tearDown() throws TimeoutException {
TestUtils.waitForAllScheduledTasksToComplete();
deleteDir(mMockDataDirectory);
clearInvocations(mPreferenceHelper);
}
@Test
public void testInstantiated_attachesMigrationCleanerToMigrationStateManager() {
verify(mMigrationCleaner).attachTo(mMigrationStateManager);
}
@Test
public void testStageRemoteData_withValidInput_allFilesStaged() throws Exception {
File dataDir = mContext.getDataDir();
File testRestoreFile1 = createAndGetNonEmptyFile(dataDir, "testRestoreFile1");
File testRestoreFile2 = createAndGetNonEmptyFile(dataDir, "testRestoreFile2");
assertThat(testRestoreFile1.exists()).isTrue();
assertThat(testRestoreFile2.exists()).isTrue();
Map pfdsByFileName = new ArrayMap<>();
pfdsByFileName.put(
testRestoreFile1.getName(),
ParcelFileDescriptor.open(testRestoreFile1, ParcelFileDescriptor.MODE_READ_ONLY));
pfdsByFileName.put(
testRestoreFile2.getName(),
ParcelFileDescriptor.open(testRestoreFile2, ParcelFileDescriptor.MODE_READ_ONLY));
final IDataStagingFinishedCallback callback = mock(IDataStagingFinishedCallback.class);
mHealthConnectService.stageAllHealthConnectRemoteData(
new StageRemoteDataRequest(pfdsByFileName), mUserHandle, callback);
verify(callback, timeout(5000).times(1)).onResult();
var stagedFileNames = mHealthConnectService.getStagedRemoteFileNames(mUserHandle);
assertThat(stagedFileNames.size()).isEqualTo(2);
assertThat(stagedFileNames.contains(testRestoreFile1.getName())).isTrue();
assertThat(stagedFileNames.contains(testRestoreFile2.getName())).isTrue();
}
@Test
public void testStageRemoteData_withNotReadMode_onlyValidFilesStaged() throws Exception {
File dataDir = mContext.getDataDir();
File testRestoreFile1 = createAndGetNonEmptyFile(dataDir, "testRestoreFile1");
File testRestoreFile2 = createAndGetNonEmptyFile(dataDir, "testRestoreFile2");
assertThat(testRestoreFile1.exists()).isTrue();
assertThat(testRestoreFile2.exists()).isTrue();
Map pfdsByFileName = new ArrayMap<>();
pfdsByFileName.put(
testRestoreFile1.getName(),
ParcelFileDescriptor.open(testRestoreFile1, ParcelFileDescriptor.MODE_WRITE_ONLY));
pfdsByFileName.put(
testRestoreFile2.getName(),
ParcelFileDescriptor.open(testRestoreFile2, ParcelFileDescriptor.MODE_READ_ONLY));
final IDataStagingFinishedCallback callback = mock(IDataStagingFinishedCallback.class);
mHealthConnectService.stageAllHealthConnectRemoteData(
new StageRemoteDataRequest(pfdsByFileName), mUserHandle, callback);
verify(callback, timeout(5000).times(1)).onError(any());
var stagedFileNames = mHealthConnectService.getStagedRemoteFileNames(mUserHandle);
assertThat(stagedFileNames.size()).isEqualTo(1);
assertThat(stagedFileNames.contains(testRestoreFile2.getName())).isTrue();
}
// Imitates the state when we are not actively staging but the disk reflects that.
// Which means we were interrupted, and therefore we should stage.
@Test
public void testStageRemoteData_whenStagingProgress_doesStage() throws Exception {
File dataDir = mContext.getDataDir();
File testRestoreFile1 = createAndGetNonEmptyFile(dataDir, "testRestoreFile1");
File testRestoreFile2 = createAndGetNonEmptyFile(dataDir, "testRestoreFile2");
assertThat(testRestoreFile1.exists()).isTrue();
assertThat(testRestoreFile2.exists()).isTrue();
Map pfdsByFileName = new ArrayMap<>();
pfdsByFileName.put(
testRestoreFile1.getName(),
ParcelFileDescriptor.open(testRestoreFile1, ParcelFileDescriptor.MODE_READ_ONLY));
pfdsByFileName.put(
testRestoreFile2.getName(),
ParcelFileDescriptor.open(testRestoreFile2, ParcelFileDescriptor.MODE_READ_ONLY));
when(mPreferenceHelper.getPreference(eq(DATA_RESTORE_STATE_KEY)))
.thenReturn(String.valueOf(INTERNAL_RESTORE_STATE_STAGING_IN_PROGRESS));
final IDataStagingFinishedCallback callback = mock(IDataStagingFinishedCallback.class);
mHealthConnectService.stageAllHealthConnectRemoteData(
new StageRemoteDataRequest(pfdsByFileName), mUserHandle, callback);
verify(callback, timeout(5000)).onResult();
var stagedFileNames = mHealthConnectService.getStagedRemoteFileNames(mUserHandle);
assertThat(stagedFileNames.size()).isEqualTo(2);
assertThat(stagedFileNames.contains(testRestoreFile1.getName())).isTrue();
assertThat(stagedFileNames.contains(testRestoreFile2.getName())).isTrue();
}
@Test
public void testStageRemoteData_whenStagingDone_doesNotStage() throws Exception {
File dataDir = mContext.getDataDir();
File testRestoreFile1 = createAndGetNonEmptyFile(dataDir, "testRestoreFile1");
File testRestoreFile2 = createAndGetNonEmptyFile(dataDir, "testRestoreFile2");
assertThat(testRestoreFile1.exists()).isTrue();
assertThat(testRestoreFile2.exists()).isTrue();
Map pfdsByFileName = new ArrayMap<>();
pfdsByFileName.put(
testRestoreFile1.getName(),
ParcelFileDescriptor.open(testRestoreFile1, ParcelFileDescriptor.MODE_READ_ONLY));
pfdsByFileName.put(
testRestoreFile2.getName(),
ParcelFileDescriptor.open(testRestoreFile2, ParcelFileDescriptor.MODE_READ_ONLY));
when(mPreferenceHelper.getPreference(eq(DATA_RESTORE_STATE_KEY)))
.thenReturn(String.valueOf(INTERNAL_RESTORE_STATE_STAGING_DONE));
final IDataStagingFinishedCallback callback = mock(IDataStagingFinishedCallback.class);
mHealthConnectService.stageAllHealthConnectRemoteData(
new StageRemoteDataRequest(pfdsByFileName), mUserHandle, callback);
verify(callback, timeout(5000)).onResult();
var stagedFileNames = mHealthConnectService.getStagedRemoteFileNames(mUserHandle);
assertThat(stagedFileNames.size()).isEqualTo(0);
}
@Test
public void testUpdateDataDownloadState_settingValidState_setsState() {
mHealthConnectService.updateDataDownloadState(DATA_DOWNLOAD_STARTED);
verify(mPreferenceHelper, times(1))
.insertOrReplacePreference(
eq(DATA_DOWNLOAD_STATE_KEY), eq(String.valueOf(DATA_DOWNLOAD_STARTED)));
}
@Test
public void testStartMigration_noShowMigrationInfoIntentAvailable_returnsError()
throws InterruptedException, RemoteException {
setUpPassingPermissionCheckFor(MIGRATE_HEALTH_CONNECT_DATA);
mHealthConnectService.startMigration(MigrationTestUtils.MOCK_CONFIGURED_PACKAGE, mCallback);
Thread.sleep(500);
verifyZeroInteractions(mMigrationStateManager);
verify(mCallback).onError(any(MigrationException.class));
}
@Test
public void testStartMigration_showMigrationInfoIntentAvailable()
throws MigrationStateManager.IllegalMigrationStateException,
InterruptedException,
RemoteException {
setUpPassingPermissionCheckFor(MIGRATE_HEALTH_CONNECT_DATA);
MigrationTestUtils.setResolveActivityResult(new ResolveInfo(), mPackageManager);
mHealthConnectService.startMigration(MigrationTestUtils.MOCK_CONFIGURED_PACKAGE, mCallback);
Thread.sleep(500);
verify(mMigrationStateManager).startMigration(mServiceContext);
}
@Test
public void testFinishMigration_noShowMigrationInfoIntentAvailable_returnsError()
throws InterruptedException, RemoteException {
setUpPassingPermissionCheckFor(MIGRATE_HEALTH_CONNECT_DATA);
mHealthConnectService.finishMigration(
MigrationTestUtils.MOCK_CONFIGURED_PACKAGE, mCallback);
Thread.sleep(500);
verifyZeroInteractions(mMigrationStateManager);
verify(mCallback).onError(any(MigrationException.class));
}
@Test
public void testFinishMigration_showMigrationInfoIntentAvailable()
throws MigrationStateManager.IllegalMigrationStateException,
InterruptedException,
RemoteException {
setUpPassingPermissionCheckFor(MIGRATE_HEALTH_CONNECT_DATA);
MigrationTestUtils.setResolveActivityResult(new ResolveInfo(), mPackageManager);
mHealthConnectService.finishMigration(
MigrationTestUtils.MOCK_CONFIGURED_PACKAGE, mCallback);
Thread.sleep(500);
verify(mMigrationStateManager).finishMigration(mServiceContext);
}
@Test
public void testWriteMigration_noShowMigrationInfoIntentAvailable_returnsError()
throws InterruptedException, RemoteException {
setUpPassingPermissionCheckFor(MIGRATE_HEALTH_CONNECT_DATA);
mHealthConnectService.writeMigrationData(
MigrationTestUtils.MOCK_CONFIGURED_PACKAGE,
mock(MigrationEntityParcel.class),
mCallback);
Thread.sleep(500);
verifyZeroInteractions(mMigrationStateManager);
verify(mCallback).onError(any(MigrationException.class));
}
@Test
public void testWriteMigration_showMigrationInfoIntentAvailable()
throws MigrationStateManager.IllegalMigrationStateException,
InterruptedException,
RemoteException {
setUpPassingPermissionCheckFor(MIGRATE_HEALTH_CONNECT_DATA);
MigrationTestUtils.setResolveActivityResult(new ResolveInfo(), mPackageManager);
mHealthConnectService.writeMigrationData(
MigrationTestUtils.MOCK_CONFIGURED_PACKAGE,
mock(MigrationEntityParcel.class),
mCallback);
Thread.sleep(500);
verify(mMigrationStateManager).validateWriteMigrationData();
verify(mCallback).onSuccess();
}
@Test
public void testInsertMinSdkExtVersion_noShowMigrationInfoIntentAvailable_returnsError()
throws InterruptedException, RemoteException {
setUpPassingPermissionCheckFor(MIGRATE_HEALTH_CONNECT_DATA);
mHealthConnectService.insertMinDataMigrationSdkExtensionVersion(
MigrationTestUtils.MOCK_CONFIGURED_PACKAGE, 0, mCallback);
Thread.sleep(500);
verifyZeroInteractions(mMigrationStateManager);
verify(mCallback).onError(any(MigrationException.class));
}
@Test
public void testInsertMinSdkExtVersion_showMigrationInfoIntentAvailable()
throws MigrationStateManager.IllegalMigrationStateException,
InterruptedException,
RemoteException {
setUpPassingPermissionCheckFor(MIGRATE_HEALTH_CONNECT_DATA);
MigrationTestUtils.setResolveActivityResult(new ResolveInfo(), mPackageManager);
mHealthConnectService.insertMinDataMigrationSdkExtensionVersion(
MigrationTestUtils.MOCK_CONFIGURED_PACKAGE, 0, mCallback);
Thread.sleep(500);
verify(mMigrationStateManager).validateSetMinSdkVersion();
verify(mCallback).onSuccess();
}
@Test
public void testConfigureScheduledExport_schedulesAnInternalTask() throws Exception {
long taskCount = mInternalTaskScheduler.getCompletedTaskCount();
mHealthConnectService.configureScheduledExport(
ScheduledExportSettings.withUri(Uri.parse(TEST_URI)), mUserHandle);
Thread.sleep(500);
assertThat(mInternalTaskScheduler.getCompletedTaskCount()).isEqualTo(taskCount + 1);
}
/**
* Tests that new HealthConnect APIs block API calls during data sync using {@link
* HealthConnectServiceImpl.BlockCallsDuringDataSync} annotation.
*
* If the API doesn't need to block API calls during data sync(ex: backup and restore, data
* migration), add it to the allowedApisList list yo pass this test.
*/
@Test
public void testHealthConnectServiceApis_blocksCallsDuringDataSync() {
// These APIs are not expected to block API calls during data sync.
Method[] allMethods = IHealthConnectService.class.getMethods();
for (Method m : allMethods) {
assertWithMessage(
"Method '%s' does not belong to either"
+ " BLOCK_CALLS_DURING_DATA_SYNC_LIST or"
+ " DO_NOT_BLOCK_CALLS_DURING_DATA_SYNC_LIST. Make sure the method"
+ " implementation includes a section blocking calls during data"
+ " sync, then add the method to BLOCK_CALLS_DURING_DATA_SYNC_LIST"
+ " (check the Javadoc for this constant for more details). If the"
+ " method must allow calls during data sync, add it to"
+ " DO_NOT_BLOCK_CALLS_DURING_DATA_SYNC_LIST.",
m.getName())
.that(
DO_NOT_BLOCK_CALLS_DURING_DATA_SYNC_LIST.contains(m.getName())
|| BLOCK_CALLS_DURING_DATA_SYNC_LIST.contains(m.getName()))
.isTrue();
assertWithMessage(
"Method '%s' can not belong to both BLOCK_CALLS_DURING_DATA_SYNC_LIST"
+ " and DO_NOT_BLOCK_CALLS_DURING_DATA_SYNC_LIST.",
m.getName())
.that(
DO_NOT_BLOCK_CALLS_DURING_DATA_SYNC_LIST.contains(m.getName())
&& BLOCK_CALLS_DURING_DATA_SYNC_LIST.contains(m.getName()))
.isFalse();
}
}
@Test
@DisableFlags(FLAG_PERSONAL_HEALTH_RECORD)
public void testReadMedicalResources_byIds_flagOff_throws() throws Exception {
IReadMedicalResourcesResponseCallback callback =
mock(IReadMedicalResourcesResponseCallback.class);
mHealthConnectService.readMedicalResources(
mContext.getAttributionSource(),
new MedicalIdFiltersParcel(List.of(MedicalIdFilter.fromId("id"))),
callback);
verify(callback, timeout(5000).times(1)).onError(mErrorCaptor.capture());
assertThat(mErrorCaptor.getValue().getHealthConnectException().getErrorCode())
.isEqualTo(ERROR_UNSUPPORTED_OPERATION);
}
private void setUpPassingPermissionCheckFor(String permission) {
doNothing()
.when(mServiceContext)
.enforcePermission(eq(permission), anyInt(), anyInt(), anyString());
}
private static File createAndGetNonEmptyFile(File dir, String fileName) throws IOException {
File file = new File(dir, fileName);
FileWriter fileWriter = new FileWriter(file);
fileWriter.write("Contents of file " + fileName);
fileWriter.close();
return file;
}
private static void deleteDir(File dir) {
File[] files = dir.listFiles();
if (files != null) {
for (var file : files) {
if (file.isDirectory()) {
deleteDir(file);
} else {
assertThat(file.delete()).isTrue();
}
}
}
assertWithMessage(
"Directory "
+ dir.getAbsolutePath()
+ " is not empty, Files present = "
+ Arrays.toString(dir.list()))
.that(dir.delete())
.isTrue();
}
}