/* * Copyright 2022 Google LLC * * 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.google.android.libraries.mobiledatadownload.testing; import com.google.android.libraries.mobiledatadownload.downloader.DownloadRequest; import com.google.android.libraries.mobiledatadownload.downloader.FileDownloader; import com.google.common.base.Optional; import com.google.common.flogger.GoogleLogger; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import java.util.concurrent.CountDownLatch; /** * File Downloader that allows control over how long the download takes. * *
An optional delegate FileDownloader can be provided to perform downloading while maintaining * control over the download state. * *
The following "states" are defined and using the BlockingFileDownloader will allow the * download to be paused so assertions can be made: * *
The Finishing state is a special state only applicable when a delegate FileDownloader is * provided. The Finishing state can be considered most like the In Progress state, the main * distinction being that no action is being performed during the In Progress state, but the * delegate is running during the Finishing state. * *
Why not just run the delegate during In Progress? * *
Because the delgate could be performing actual work (i.e. network calls, I/O), there could be * some variability introduced that causes test assertions to become flaky. The In Progress is * reserved to effectively be a Frozen state that ensure no work is being done. This ensures that * test assertions remain consistent. * *
After creating an instance of BlockingFileDownloader, the following example shows how the * BlockingFileDownloader can control the state as well as when assertions can be made. * *
{@code * // Before the call to MDD's downloadFileGroup, the state is considered "Idle" and assertions * // can be made at this time. * * mdd.downloadFileGroup(...); * * // After calling downloadFileGroup, the state is in a transition period from "Idle" to * // "In Progress." assertions should not be made during this time. * * blockingFileDownloader.waitForDownloadStarted(); * * // The above method blocks until the "In Progress" state has been reached. Assertions can be made * // after this call for the "In Progress" state. If no assertions need to be made during this * // state, the above call can be skipped. * * blockingFileDownloader.finishDownloading(); * * // The above method moves the state from "In Progress" to "Finishing" (if a delegate is provided) * // or "Finished" (if no delegate is provided). This is another transition period, so assertions * // should not be made during this time. * * blockingFileDownloader.waitForDownloadCompleted(); * * // The above method ensures the state has changed from "In Progress"/"Finishing" to "Finished." * // After this point, assertions can be made safely again. * // * // Optionally, the future returned from mdd.downloadFileGroup can be awaited instead, since that * // also ensures the download has completed. * }*/ public final class BlockingFileDownloader implements FileDownloader { private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); private final ListeningExecutorService downloadExecutor; private final Optional
The in progress latch is triggered if it hasn't yet been triggered to prevent a deadlock * from occurring. */ public void finishDownloading() { if (downloadInProgressLatch.getCount() > 0) { downloadInProgressLatch.countDown(); } downloadFinishingLatch.countDown(); } /** * Resets the current state of the download lifecycle. * *
An existing download cycle is finished before resetting the state to prevent a deadlock from * occurring. */ public void resetState() { // Force a finish download if it was previously in progress to prevent deadlock. if (downloadFinishedLatch.getCount() > 0) { finishDownloading(); } downloadFinishedLatch.countDown(); logger.atInfo().log("Reset State back to: Idle"); downloadInProgressLatch = new CountDownLatch(1); downloadFinishingLatch = new CountDownLatch(1); downloadFinishedLatch = new CountDownLatch(1); delegateInProgressLatch = new CountDownLatch(1); } }