[core] Fix offline status reporting regressions
The core of the change is ensuring that ensureResource doesn't release Zalgo: it should be consistently async, rather than async in the case that the resource doesn't exist in the database, but sync if it does. This ensures that status is reported in a more consistent sequence, regardless of the database state.
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/util/tile_cover.hpp>
|
||||
#include <mbgl/util/mapbox.hpp>
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
@@ -195,7 +196,8 @@ void OfflineDownload::activateDownload() {
|
||||
}
|
||||
|
||||
void OfflineDownload::deactivateDownload() {
|
||||
requests.clear();
|
||||
workRequests.clear();
|
||||
fileRequests.clear();
|
||||
}
|
||||
|
||||
void OfflineDownload::ensureTiles(SourceType type, uint16_t tileSize, const SourceInfo& info) {
|
||||
@@ -207,43 +209,48 @@ void OfflineDownload::ensureTiles(SourceType type, uint16_t tileSize, const Sour
|
||||
void OfflineDownload::ensureResource(const Resource& resource, std::function<void (Response)> callback) {
|
||||
status.requiredResourceCount++;
|
||||
|
||||
optional<std::pair<Response, uint64_t>> offlineResponse = offlineDatabase.getRegionResource(id, resource);
|
||||
if (offlineResponse) {
|
||||
if (callback) {
|
||||
callback(offlineResponse->first);
|
||||
}
|
||||
auto workRequestsIt = workRequests.insert(workRequests.begin(), nullptr);
|
||||
*workRequestsIt = util::RunLoop::Get()->invokeCancellable([=] () {
|
||||
workRequests.erase(workRequestsIt);
|
||||
|
||||
status.completedResourceCount++;
|
||||
status.completedResourceSize += offlineResponse->second;
|
||||
observer->statusChanged(status);
|
||||
optional<std::pair<Response, uint64_t>> offlineResponse = offlineDatabase.getRegionResource(id, resource);
|
||||
if (offlineResponse) {
|
||||
if (callback) {
|
||||
callback(offlineResponse->first);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
status.completedResourceCount++;
|
||||
status.completedResourceSize += offlineResponse->second;
|
||||
observer->statusChanged(status);
|
||||
|
||||
if (resource.kind == Resource::Kind::Tile
|
||||
&& util::mapbox::isMapboxURL(resource.url)
|
||||
&& offlineDatabase.offlineMapboxTileCountLimitExceeded()) {
|
||||
observer->mapboxTileCountLimitExceeded(offlineDatabase.getOfflineMapboxTileCountLimit());
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = requests.insert(requests.begin(), nullptr);
|
||||
*it = onlineFileSource.request(resource, [=] (Response onlineResponse) {
|
||||
if (onlineResponse.error) {
|
||||
observer->responseError(*onlineResponse.error);
|
||||
return;
|
||||
}
|
||||
|
||||
requests.erase(it);
|
||||
|
||||
if (callback) {
|
||||
callback(onlineResponse);
|
||||
if (resource.kind == Resource::Kind::Tile
|
||||
&& util::mapbox::isMapboxURL(resource.url)
|
||||
&& offlineDatabase.offlineMapboxTileCountLimitExceeded()) {
|
||||
observer->mapboxTileCountLimitExceeded(offlineDatabase.getOfflineMapboxTileCountLimit());
|
||||
return;
|
||||
}
|
||||
|
||||
status.completedResourceCount++;
|
||||
status.completedResourceSize += offlineDatabase.putRegionResource(id, resource, onlineResponse);
|
||||
auto fileRequestsIt = fileRequests.insert(fileRequests.begin(), nullptr);
|
||||
*fileRequestsIt = onlineFileSource.request(resource, [=] (Response onlineResponse) {
|
||||
if (onlineResponse.error) {
|
||||
observer->responseError(*onlineResponse.error);
|
||||
return;
|
||||
}
|
||||
|
||||
observer->statusChanged(status);
|
||||
fileRequests.erase(fileRequestsIt);
|
||||
|
||||
if (callback) {
|
||||
callback(onlineResponse);
|
||||
}
|
||||
|
||||
status.completedResourceCount++;
|
||||
status.completedResourceSize += offlineDatabase.putRegionResource(id, resource, onlineResponse);
|
||||
|
||||
observer->statusChanged(status);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace mbgl {
|
||||
|
||||
class OfflineDatabase;
|
||||
class FileSource;
|
||||
class WorkRequest;
|
||||
class FileRequest;
|
||||
class Resource;
|
||||
class Response;
|
||||
@@ -55,7 +56,8 @@ private:
|
||||
FileSource& onlineFileSource;
|
||||
OfflineRegionStatus status;
|
||||
std::unique_ptr<OfflineRegionObserver> observer;
|
||||
std::list<std::unique_ptr<FileRequest>> requests;
|
||||
std::list<std::unique_ptr<WorkRequest>> workRequests;
|
||||
std::list<std::unique_ptr<FileRequest>> fileRequests;
|
||||
std::set<std::string> requiredSourceURLs;
|
||||
};
|
||||
|
||||
|
||||
@@ -435,19 +435,8 @@ TEST(OfflineDownload, ReactivatePreviouslyCompletedDownload) {
|
||||
test.response("offline/0-0-0.vector.pbf"));
|
||||
|
||||
auto observer = std::make_unique<MockObserver>();
|
||||
bool completedOnce = false;
|
||||
|
||||
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
|
||||
if (!status.complete()) {
|
||||
return;
|
||||
} else if (!completedOnce) {
|
||||
completedOnce = true;
|
||||
download.setState(OfflineRegionDownloadState::Inactive);
|
||||
download.setState(OfflineRegionDownloadState::Active);
|
||||
} else {
|
||||
EXPECT_EQ(2, status.completedResourceCount);
|
||||
EXPECT_EQ(test.size, status.completedResourceSize);
|
||||
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
|
||||
if (status.complete()) {
|
||||
test.loop.stop();
|
||||
}
|
||||
};
|
||||
@@ -456,6 +445,43 @@ TEST(OfflineDownload, ReactivatePreviouslyCompletedDownload) {
|
||||
download.setState(OfflineRegionDownloadState::Active);
|
||||
|
||||
test.loop.run();
|
||||
|
||||
OfflineDownload redownload(
|
||||
region.getID(),
|
||||
OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/offline/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0),
|
||||
test.db, test.fileSource);
|
||||
|
||||
std::vector<OfflineRegionStatus> statusesAfterReactivate;
|
||||
|
||||
observer = std::make_unique<MockObserver>();
|
||||
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
|
||||
statusesAfterReactivate.push_back(status);
|
||||
if (status.complete()) {
|
||||
test.loop.stop();
|
||||
}
|
||||
};
|
||||
|
||||
redownload.setObserver(std::move(observer));
|
||||
redownload.setState(OfflineRegionDownloadState::Active);
|
||||
|
||||
test.loop.run();
|
||||
|
||||
ASSERT_EQ(3, statusesAfterReactivate.size());
|
||||
|
||||
EXPECT_EQ(OfflineRegionDownloadState::Active, statusesAfterReactivate[0].downloadState);
|
||||
EXPECT_FALSE(statusesAfterReactivate[0].requiredResourceCountIsPrecise);
|
||||
EXPECT_EQ(1, statusesAfterReactivate[0].requiredResourceCount);
|
||||
EXPECT_EQ(0, statusesAfterReactivate[0].completedResourceCount);
|
||||
|
||||
EXPECT_EQ(OfflineRegionDownloadState::Active, statusesAfterReactivate[1].downloadState);
|
||||
EXPECT_TRUE(statusesAfterReactivate[1].requiredResourceCountIsPrecise);
|
||||
EXPECT_EQ(2, statusesAfterReactivate[1].requiredResourceCount);
|
||||
EXPECT_EQ(1, statusesAfterReactivate[1].completedResourceCount);
|
||||
|
||||
EXPECT_EQ(OfflineRegionDownloadState::Active, statusesAfterReactivate[2].downloadState);
|
||||
EXPECT_TRUE(statusesAfterReactivate[2].requiredResourceCountIsPrecise);
|
||||
EXPECT_EQ(2, statusesAfterReactivate[2].requiredResourceCount);
|
||||
EXPECT_EQ(2, statusesAfterReactivate[2].completedResourceCount);
|
||||
}
|
||||
|
||||
TEST(OfflineDownload, Deactivate) {
|
||||
|
||||
Reference in New Issue
Block a user