mirror of
https://github.com/openseadragon/openseadragon.git
synced 2025-01-18 16:51:48 +03:00
Integration tests: bugfixing of manipulation of tiles that share data: when tiles are loaded, when tiles are processed, also await async data preparation befre finishing the invalidation event.
This commit is contained in:
parent
e403e29312
commit
20177116e7
@ -146,8 +146,7 @@ class HTMLDrawer extends OpenSeadragon.DrawerBase{
|
||||
* @returns {Element} the div to draw into
|
||||
*/
|
||||
_createDrawingElement(){
|
||||
let canvas = $.makeNeutralElement("div");
|
||||
return canvas;
|
||||
return $.makeNeutralElement("div");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -259,6 +258,10 @@ class HTMLDrawer extends OpenSeadragon.DrawerBase{
|
||||
// content during animation of the container size.
|
||||
|
||||
const dataObject = this.getDataToDraw(tile);
|
||||
if (!dataObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( dataObject.element.parentNode !== container ) {
|
||||
container.appendChild( dataObject.element );
|
||||
}
|
||||
|
@ -2367,6 +2367,14 @@ function OpenSeadragon( options ){
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes an AJAX request.
|
||||
* @param {String} url - the url to request
|
||||
* @param {Function} onSuccess
|
||||
* @param {Function} onError
|
||||
* @throws {Error}
|
||||
* @returns {XMLHttpRequest}
|
||||
* @deprecated deprecated way of calling this function
|
||||
*//**
|
||||
* Makes an AJAX request.
|
||||
* @param {Object} options
|
||||
* @param {String} options.url - the url to request
|
||||
@ -2379,14 +2387,6 @@ function OpenSeadragon( options ){
|
||||
* @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
|
||||
* @throws {Error}
|
||||
* @returns {XMLHttpRequest}
|
||||
*//**
|
||||
* Makes an AJAX request.
|
||||
* @param {String} url - the url to request
|
||||
* @param {Function} onSuccess
|
||||
* @param {Function} onError
|
||||
* @throws {Error}
|
||||
* @returns {XMLHttpRequest}
|
||||
* @deprecated deprecated way of calling this function
|
||||
*/
|
||||
makeAjaxRequest: function( url, onSuccess, onError ) {
|
||||
var withCredentials;
|
||||
|
14
src/tile.js
14
src/tile.js
@ -550,7 +550,10 @@ $.Tile.prototype = {
|
||||
|
||||
/**
|
||||
* Optimizazion: prepare target cache for subsequent use in rendering, and perform updateRenderTarget()
|
||||
* The main idea of this function is that it must be ASYNCHRONOUS since there might be additional processing
|
||||
* happening due to underlying drawer requirements.
|
||||
* @private
|
||||
* @return {OpenSeadragon.Promise<?>}
|
||||
*/
|
||||
updateRenderTargetWithDataTransform: function (drawerId, supportedFormats, usePrivateCache) {
|
||||
// Now, if working cache exists, we set main cache to the working cache --> prepare
|
||||
@ -570,17 +573,20 @@ $.Tile.prototype = {
|
||||
return cache.prepareForRendering(drawerId, supportedFormats, usePrivateCache, this.processing);
|
||||
}
|
||||
|
||||
return null;
|
||||
return $.Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves render target: changes might've been made to the rendering pipeline:
|
||||
* - working cache is set: make sure main cache will be replaced
|
||||
* - working cache is unset: make sure main cache either gets updated to original data or stays (based on this.__restore)
|
||||
*
|
||||
* The main idea of this function is that it is SYNCHRONOUS, e.g. can perform in-place cache swap to update
|
||||
* before any rendering occurs.
|
||||
* @private
|
||||
* @return
|
||||
*/
|
||||
updateRenderTarget: function () {
|
||||
updateRenderTarget: function (_allowTileNotLoaded = false) {
|
||||
// Check if we asked for restore, and make sure we set it to false since we update the whole cache state
|
||||
const requestedRestore = this.__restore;
|
||||
this.__restore = false;
|
||||
@ -593,7 +599,8 @@ $.Tile.prototype = {
|
||||
this.tiledImage._tileCache.consumeCache({
|
||||
tile: this,
|
||||
victimKey: this._wcKey,
|
||||
consumerKey: newCacheKey
|
||||
consumerKey: newCacheKey,
|
||||
tileAllowNotLoaded: _allowTileNotLoaded
|
||||
});
|
||||
this.cacheKey = newCacheKey;
|
||||
return;
|
||||
@ -831,7 +838,6 @@ $.Tile.prototype = {
|
||||
}
|
||||
// Working key is never updated, it will be invalidated (but do not dereference cache, just fix the pointers)
|
||||
this._caches[newKey] = cache;
|
||||
cache.AAA = true;
|
||||
delete this._caches[oldKey];
|
||||
},
|
||||
|
||||
|
@ -238,6 +238,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
// if not internal copy and we have no data, or we are ready to render, exit
|
||||
@ -273,8 +274,7 @@
|
||||
const selectedFormat = conversionPath[conversionPath.length - 1].target.value;
|
||||
return $.convertor.convert(this._tRef, this.data, this.type, selectedFormat).then(data => {
|
||||
internalCache.setDataAs(data, selectedFormat); // synchronous, SimpleCacheRecord
|
||||
fin();
|
||||
return this;
|
||||
return fin();
|
||||
});
|
||||
}
|
||||
|
||||
@ -372,11 +372,10 @@
|
||||
// make sure this gets destroyed even if loaded=false
|
||||
if (this.loaded) {
|
||||
this._destroySelfUnsafe(this._data, this._type);
|
||||
} else {
|
||||
} else if (this._promise) {
|
||||
const oldType = this._type;
|
||||
this._promise.then(x => this._destroySelfUnsafe(x, oldType));
|
||||
}
|
||||
this.loaded = false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -389,6 +388,7 @@
|
||||
if (!this._destroyed) {
|
||||
return;
|
||||
}
|
||||
this.loaded = false;
|
||||
this._tiles = null;
|
||||
this._data = null;
|
||||
this._type = null;
|
||||
@ -922,12 +922,14 @@
|
||||
* inheriting its tiles and key.
|
||||
* @param {String} options.consumerKey - The cache that consumes the victim. In fact, it gets destroyed and
|
||||
* replaced by victim, which inherits all its metadata.
|
||||
* @param {}
|
||||
* @param {Boolean} options.tileAllowNotLoaded - if true, tile that is not loaded is also processed,
|
||||
* this is internal parameter used in tile-loaded completion routine, as we need to prepare tile but
|
||||
* it is not yet loaded and cannot be marked as so (otherwise the system would think it is ready)
|
||||
*/
|
||||
consumeCache(options) {
|
||||
const victim = this._cachesLoaded[options.victimKey],
|
||||
tile = options.tile;
|
||||
if (!victim || (!tile.loaded && !tile.loading)) {
|
||||
if (!victim || (!options.tileAllowNotLoaded && !tile.loaded && !tile.loading)) {
|
||||
$.console.warn("Attempt to consume non-existent cache: this is probably a bug!");
|
||||
return;
|
||||
}
|
||||
@ -935,12 +937,14 @@
|
||||
let tiles = [...tile.getCache()._tiles];
|
||||
|
||||
if (consumer) {
|
||||
// We need to avoid costly conversions: replace consumer.
|
||||
// unloadCacheForTile() will modify the array, iterate over a copy
|
||||
const iterateTiles = [...consumer._tiles];
|
||||
// We need to avoid async execution here: replace consumer instead of overwriting the data.
|
||||
const iterateTiles = [...consumer._tiles]; // unloadCacheForTile() will modify the array, use a copy
|
||||
for (let tile of iterateTiles) {
|
||||
if (tile.loaded) {
|
||||
this.unloadCacheForTile(tile, options.consumerKey, true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// Just swap victim to become new consumer
|
||||
const resultCache = this.renameCache({
|
||||
@ -951,8 +955,10 @@
|
||||
if (resultCache) {
|
||||
// Only one cache got working item, other caches were idle: update cache: add the new cache
|
||||
// we can add since we removed above with unloadCacheForTile()
|
||||
// Loading tiles are also accepted, since they might be in the process of finishing. However,
|
||||
// note that they are not part of the unloading process above!
|
||||
for (let tile of tiles) {
|
||||
if (tile !== options.tile && tile.loaded) {
|
||||
if (tile !== options.tile && (tile.loaded || tile.loading)) {
|
||||
tile.addCache(options.consumerKey, resultCache.data, resultCache.type, true, false);
|
||||
}
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
requestInvalidate: function (viewportOnly, tStamp, restoreTiles = true) {
|
||||
tStamp = tStamp || $.now();
|
||||
const tiles = viewportOnly ? this._lastDrawn.map(x => x.tile) : this._tileCache.getLoadedTilesFor(this);
|
||||
this.viewer.world.requestTileInvalidateEvent(tiles, tStamp, restoreTiles);
|
||||
return this.viewer.world.requestTileInvalidateEvent(tiles, tStamp, restoreTiles);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -2106,7 +2106,12 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
_setTileLoaded: function(tile, data, cutoff, tileRequest, dataType) {
|
||||
tile.tiledImage = this; //unloaded with tile.unload(), so we need to set it back
|
||||
// does nothing if tile.cacheKey already present
|
||||
tile.addCache(tile.cacheKey, data, dataType, false, false);
|
||||
|
||||
let tileCacheCreated = false;
|
||||
tile.addCache(tile.cacheKey, () => {
|
||||
tileCacheCreated = true;
|
||||
return data;
|
||||
}, dataType, false, false);
|
||||
|
||||
let resolver = null,
|
||||
increment = 0,
|
||||
@ -2125,7 +2130,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
tile.hasTransparency = tile.hasTransparency || _this.source.hasTransparency(
|
||||
undefined, tile.getUrl(), tile.ajaxHeaders, tile.postData
|
||||
);
|
||||
tile.updateRenderTarget();
|
||||
tile.updateRenderTarget(true);
|
||||
//make sure cache data is ready for drawing, if not, request the desired format
|
||||
const cache = tile.getCache(tile.cacheKey),
|
||||
requiredTypes = _this._drawer.getSupportedDataFormats();
|
||||
@ -2162,6 +2167,16 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
|
||||
const fallbackCompletion = getCompletionCallback();
|
||||
|
||||
if (!tileCacheCreated) {
|
||||
// Tile-loaded not called on each tile, but only on tiles with new data! Verify we share the main cache
|
||||
|
||||
// We could attempt to initialize the tile here (e.g. find another tile that has same key and if
|
||||
// we find it in different main cache, we try to share it with current tile, but this process
|
||||
// is also happening within tile cache logics (see last part of consumeCache(..)).
|
||||
fallbackCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when a tile has just been loaded in memory. That means that the
|
||||
* image has been downloaded and can be modified before being drawn to the canvas.
|
||||
|
@ -769,18 +769,21 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
* @function
|
||||
* @param {Boolean} [restoreTiles=true] if true, tile processing starts from the tile original data
|
||||
* @fires OpenSeadragon.Viewer.event:tile-invalidated
|
||||
* @return {OpenSeadragon.Promise<?>}
|
||||
*/
|
||||
requestInvalidate: function (restoreTiles = true) {
|
||||
if ( !THIS[ this.hash ] ) {
|
||||
//this viewer has already been destroyed: returning immediately
|
||||
return;
|
||||
return $.Promise.resolve();
|
||||
}
|
||||
|
||||
const tStamp = $.now();
|
||||
this.world.requestInvalidate(tStamp, restoreTiles);
|
||||
if (this.navigator) {
|
||||
this.navigator.world.requestInvalidate(tStamp, restoreTiles);
|
||||
const worldPromise = this.world.requestInvalidate(tStamp, restoreTiles);
|
||||
if (!this.navigator) {
|
||||
return worldPromise;
|
||||
}
|
||||
const navigatorPromise = this.navigator.world.requestInvalidate(tStamp, restoreTiles);
|
||||
return $.Promise.all([worldPromise, navigatorPromise]);
|
||||
},
|
||||
|
||||
|
||||
|
66
src/world.js
66
src/world.js
@ -242,16 +242,16 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
||||
* @param {Boolean} [restoreTiles=true] if true, tile processing starts from the tile original data
|
||||
* @function
|
||||
* @fires OpenSeadragon.Viewer.event:tile-invalidated
|
||||
* @return {OpenSeadragon.Promise<?>}
|
||||
*/
|
||||
requestInvalidate: function (tStamp, restoreTiles = true) {
|
||||
$.__updated = tStamp = tStamp || $.now();
|
||||
for ( let i = 0; i < this._items.length; i++ ) {
|
||||
this._items[i].requestInvalidate(true, tStamp, restoreTiles);
|
||||
}
|
||||
|
||||
const promises = this._items.map(item => item.requestInvalidate(true, tStamp, restoreTiles));
|
||||
|
||||
const tiles = this.viewer.tileCache.getLoadedTilesFor(true);
|
||||
// Delay processing of all tiles of all items to a later stage by increasing tstamp
|
||||
this.requestTileInvalidateEvent(tiles, tStamp, restoreTiles);
|
||||
promises.push(this.requestTileInvalidateEvent(tiles, tStamp, restoreTiles));
|
||||
return $.Promise.all(promises);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -263,42 +263,20 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
||||
* changes are added to the cycle, else they await next iteration
|
||||
* @param {Boolean} [restoreTiles=true] if true, tile processing starts from the tile original data
|
||||
* @fires OpenSeadragon.Viewer.event:tile-invalidated
|
||||
* @return {OpenSeadragon.Promise<?>}
|
||||
*/
|
||||
requestTileInvalidateEvent: function(tileList, tStamp, restoreTiles = true) {
|
||||
if (tileList.length < 1) {
|
||||
return;
|
||||
return $.Promise.resolve();
|
||||
}
|
||||
|
||||
if (this._queuedInvalidateTiles.length) {
|
||||
this._queuedInvalidateTiles.push(tileList);
|
||||
return;
|
||||
return $.Promise.resolve();
|
||||
}
|
||||
|
||||
// this.viewer.viewer is defined in navigator, ensure we call event on the parent viewer
|
||||
const eventTarget = this.viewer.viewer || this.viewer;
|
||||
const finish = () => {
|
||||
for (let tile of tileList) {
|
||||
// pass update stamp on the new cache object to avoid needless updates
|
||||
const newCache = tile.getCache();
|
||||
if (newCache) {
|
||||
|
||||
newCache._updateStamp = tStamp;
|
||||
for (let t of newCache._tiles) {
|
||||
// Mark all as processing
|
||||
t.processing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this._queuedInvalidateTiles.length) {
|
||||
// Make space for other logics execution before we continue in processing
|
||||
let list = this._queuedInvalidateTiles.splice(0, 1)[0];
|
||||
this.requestTileInvalidateEvent(list, tStamp, restoreTiles);
|
||||
} else {
|
||||
this.draw();
|
||||
}
|
||||
};
|
||||
|
||||
const supportedFormats = eventTarget.drawer.getSupportedDataFormats();
|
||||
const keepInternalCacheCopy = eventTarget.drawer.options.usePrivateCache;
|
||||
const drawerId = eventTarget.drawer.getId();
|
||||
@ -320,7 +298,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
||||
return true;
|
||||
});
|
||||
|
||||
$.Promise.all(tileList.map(tile => {
|
||||
const jobList = tileList.map(tile => {
|
||||
if (restoreTiles) {
|
||||
tile.restore();
|
||||
}
|
||||
@ -328,9 +306,31 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
||||
tile: tile,
|
||||
tiledImage: tile.tiledImage,
|
||||
}).then(() => {
|
||||
tile.updateRenderTargetWithDataTransform(drawerId, supportedFormats, keepInternalCacheCopy);
|
||||
// asynchronous finisher
|
||||
return tile.updateRenderTargetWithDataTransform(drawerId, supportedFormats, keepInternalCacheCopy);
|
||||
}).catch(e => {
|
||||
$.console.error("Update routine error:", e);
|
||||
});
|
||||
});
|
||||
|
||||
return $.Promise.all(jobList).then(() => {
|
||||
for (let tile of tileList) {
|
||||
// pass update stamp on the new cache object to avoid needless updates
|
||||
const newCache = tile.getCache();
|
||||
if (newCache) {
|
||||
newCache._updateStamp = tStamp;
|
||||
tile.processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._queuedInvalidateTiles.length) {
|
||||
// Make space for other logics execution before we continue in processing
|
||||
let list = this._queuedInvalidateTiles.splice(0, 1)[0];
|
||||
this.requestTileInvalidateEvent(list, tStamp, restoreTiles);
|
||||
} else {
|
||||
this.draw();
|
||||
}
|
||||
});
|
||||
})).catch(finish).then(finish);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -240,5 +240,29 @@
|
||||
};
|
||||
|
||||
OpenSeadragon.console = testConsole;
|
||||
|
||||
OpenSeadragon.getBuiltInDrawersForTest = function() {
|
||||
const drawers = [];
|
||||
for (let property in OpenSeadragon) {
|
||||
const drawer = OpenSeadragon[ property ],
|
||||
proto = drawer.prototype;
|
||||
if( proto &&
|
||||
proto instanceof OpenSeadragon.DrawerBase &&
|
||||
$.isFunction( proto.getType )){
|
||||
drawers.push(proto.getType.call( drawer ));
|
||||
}
|
||||
}
|
||||
return drawers;
|
||||
};
|
||||
|
||||
OpenSeadragon.Viewer.prototype.waitForFinishedJobsForTest = function () {
|
||||
let finish;
|
||||
let int = setInterval(() => {
|
||||
if (this.imageLoader.jobsInProgress < 1) {
|
||||
finish();
|
||||
}
|
||||
}, 50);
|
||||
return new OpenSeadragon.Promise((resolve) => finish = resolve);
|
||||
};
|
||||
} )();
|
||||
|
||||
|
259
test/modules/data-manipulation.js
Normal file
259
test/modules/data-manipulation.js
Normal file
@ -0,0 +1,259 @@
|
||||
/* global QUnit, testLog */
|
||||
|
||||
(function() {
|
||||
|
||||
let viewer;
|
||||
QUnit.module(`Data Manipulation Across Drawers`, {
|
||||
beforeEach: function () {
|
||||
$('<div id="example"></div>').appendTo("#qunit-fixture");
|
||||
testLog.reset();
|
||||
},
|
||||
afterEach: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
OpenSeadragon.getBuiltInDrawersForTest().forEach(testDrawer);
|
||||
// If yuu want to debug a specific drawer, use instead:
|
||||
// ['webgl'].forEach(testDrawer);
|
||||
|
||||
function getPluginCode(overlayColor = "rgba(0,0,255,0.5)") {
|
||||
return async function(e) {
|
||||
const tile = e.tile;
|
||||
const ctx = await tile.getData('context2d'), canvas = ctx.canvas;
|
||||
ctx.fillStyle = overlayColor;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
await tile.setData(ctx, 'context2d');
|
||||
};
|
||||
}
|
||||
|
||||
function getResetTileDataCode() {
|
||||
return async function(e) {
|
||||
const tile = e.tile;
|
||||
tile.restore();
|
||||
};
|
||||
}
|
||||
|
||||
function getTileDescription(t) {
|
||||
return `${t.level}/${t.x}-${t.y}`;
|
||||
}
|
||||
|
||||
|
||||
function testDrawer(type) {
|
||||
|
||||
function whiteViewport() {
|
||||
viewer = OpenSeadragon({
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
maxImageCacheCount: 200,
|
||||
springStiffness: 100,
|
||||
drawer: type
|
||||
});
|
||||
|
||||
viewer.open({
|
||||
width: 24,
|
||||
height: 24,
|
||||
tileSize: 24,
|
||||
minLevel: 1,
|
||||
|
||||
// This is a crucial test feature: all tiles share the same URL, so there are plenty collisions
|
||||
getTileUrl: (x, y, l) => "",
|
||||
getTilePostData: () => "",
|
||||
downloadTileStart: (context) => {
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
canvas.width = context.tile.size.x;
|
||||
canvas.height = context.tile.size.y;
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.fillRect(0, 0, context.tile.size.x, context.tile.size.y);
|
||||
|
||||
context.finish(ctx, null, "context2d");
|
||||
}
|
||||
});
|
||||
}
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
|
||||
// we test middle of the canvas, so that we can test both tiles or the output canvas of canvas drawer :)
|
||||
async function readTileData() {
|
||||
if (type === "canvas") {
|
||||
//test with the underlying canvas instead
|
||||
const canvas = viewer.drawer.canvas;
|
||||
return viewer.drawer.canvas.getContext("2d").getImageData(canvas.width/2, canvas.height/2, 1, 1);
|
||||
}
|
||||
|
||||
await sleep(200);
|
||||
|
||||
//else incompatible drawer for data getting
|
||||
const tile = viewer.world.getItemAt(0).getTilesToDraw()[0];
|
||||
const cache = tile.tile.getCache();
|
||||
if (!cache || !cache.loaded) return null;
|
||||
|
||||
const ctx = await cache.getDataAs("context2d");
|
||||
if (!ctx) return null;
|
||||
return ctx.getImageData(ctx.canvas.width/2, ctx.canvas.height/2, 1, 1)
|
||||
}
|
||||
|
||||
QUnit.test(type + ' drawer: basic scenario.', function(assert) {
|
||||
whiteViewport();
|
||||
const done = assert.async();
|
||||
const fnA = getPluginCode("rgba(0,0,255,1)");
|
||||
const fnB = getPluginCode("rgba(255,0,0,1)");
|
||||
|
||||
const crashTest = () => assert.ok(false, "Tile Invalidated event should not be called");
|
||||
|
||||
viewer.addHandler('tile-loaded', fnA);
|
||||
viewer.addHandler('tile-invalidated', crashTest);
|
||||
viewer.addHandler('tile-loaded', fnB);
|
||||
viewer.addHandler('tile-invalidated', crashTest);
|
||||
|
||||
viewer.addHandler('open', async () => {
|
||||
await viewer.waitForFinishedJobsForTest();
|
||||
let data = await readTileData();
|
||||
assert.equal(data.data[0], 255);
|
||||
assert.equal(data.data[1], 0);
|
||||
assert.equal(data.data[2], 0);
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
// Thorough testing of the cache state
|
||||
for (let tile of viewer.tileCache._tilesLoaded) {
|
||||
|
||||
const caches = Object.entries(tile._caches);
|
||||
assert.equal(caches.length, 2, `Tile ${getTileDescription(tile)} has only two caches - main & original`);
|
||||
for (let [key, value] of caches) {
|
||||
assert.ok(value.loaded, `Attached cache '${key}' is ready.`);
|
||||
assert.notOk(value._destroyed, `Attached cache '${key}' is not destroyed.`);
|
||||
assert.ok(value._tiles.includes(tile), `Attached cache '${key}' reference is bidirectional.`);
|
||||
}
|
||||
assert.notOk(tile.getCache(tile._wcKey), "Tile cache working key is unset");
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test(type + ' drawer: basic scenario with priorities + events addition.', function(assert) {
|
||||
whiteViewport();
|
||||
const done = assert.async();
|
||||
// FNA gets applied last since it has low priority
|
||||
const fnA = getPluginCode("rgba(0,0,255,1)");
|
||||
const fnB = getPluginCode("rgba(255,0,0,1)");
|
||||
|
||||
viewer.addHandler('tile-loaded', fnA);
|
||||
viewer.addHandler('tile-invalidated', fnA);
|
||||
viewer.addHandler('tile-loaded', fnB, null, 1);
|
||||
viewer.addHandler('tile-invalidated', fnB, null, 1);
|
||||
// const promise = viewer.requestInvalidate();
|
||||
|
||||
viewer.addHandler('open', async () => {
|
||||
await viewer.waitForFinishedJobsForTest();
|
||||
|
||||
let data = await readTileData();
|
||||
assert.equal(data.data[0], 0);
|
||||
assert.equal(data.data[1], 0);
|
||||
assert.equal(data.data[2], 255);
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
// Test swap
|
||||
viewer.addHandler('tile-loaded', fnB);
|
||||
viewer.addHandler('tile-invalidated', fnB);
|
||||
await viewer.requestInvalidate();
|
||||
|
||||
data = await readTileData();
|
||||
// suddenly B is applied since it was added with same priority but later
|
||||
assert.equal(data.data[0], 255);
|
||||
assert.equal(data.data[1], 0);
|
||||
assert.equal(data.data[2], 0);
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
// Now B gets applied last! Red
|
||||
viewer.addHandler('tile-loaded', fnB, null, -1);
|
||||
viewer.addHandler('tile-invalidated', fnB, null, -1);
|
||||
await viewer.requestInvalidate();
|
||||
// no change
|
||||
data = await readTileData();
|
||||
assert.equal(data.data[0], 255);
|
||||
assert.equal(data.data[1], 0);
|
||||
assert.equal(data.data[2], 0);
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
// Thorough testing of the cache state
|
||||
for (let tile of viewer.tileCache._tilesLoaded) {
|
||||
|
||||
const caches = Object.entries(tile._caches);
|
||||
assert.equal(caches.length, 2, `Tile ${getTileDescription(tile)} has only two caches - main & original`);
|
||||
for (let [key, value] of caches) {
|
||||
assert.ok(value.loaded, `Attached cache '${key}' is ready.`);
|
||||
assert.notOk(value._destroyed, `Attached cache '${key}' is not destroyed.`);
|
||||
assert.ok(value._tiles.includes(tile), `Attached cache '${key}' reference is bidirectional.`);
|
||||
}
|
||||
assert.notOk(tile.getCache(tile._wcKey), "Tile cache working key is unset");
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test(type + ' drawer: one calls tile restore.', function(assert) {
|
||||
whiteViewport();
|
||||
|
||||
const done = assert.async();
|
||||
const fnA = getPluginCode("rgba(0,255,0,1)");
|
||||
const fnB = getResetTileDataCode();
|
||||
|
||||
viewer.addHandler('tile-loaded', fnA);
|
||||
viewer.addHandler('tile-invalidated', fnA);
|
||||
viewer.addHandler('tile-loaded', fnB, null, 1);
|
||||
viewer.addHandler('tile-invalidated', fnB, null, 1);
|
||||
// const promise = viewer.requestInvalidate();
|
||||
|
||||
viewer.addHandler('open', async () => {
|
||||
await viewer.waitForFinishedJobsForTest();
|
||||
|
||||
let data = await readTileData();
|
||||
assert.equal(data.data[0], 0);
|
||||
assert.equal(data.data[1], 255);
|
||||
assert.equal(data.data[2], 0);
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
// Test swap - suddenly B applied since it was added later
|
||||
viewer.addHandler('tile-loaded', fnB);
|
||||
viewer.addHandler('tile-invalidated', fnB);
|
||||
await viewer.requestInvalidate();
|
||||
data = await readTileData();
|
||||
assert.equal(data.data[0], 255);
|
||||
assert.equal(data.data[1], 255);
|
||||
assert.equal(data.data[2], 255);
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
viewer.addHandler('tile-loaded', fnB, null, -1);
|
||||
viewer.addHandler('tile-invalidated', fnB, null, -1);
|
||||
await viewer.requestInvalidate();
|
||||
data = await readTileData();
|
||||
//Erased!
|
||||
assert.equal(data.data[0], 255);
|
||||
assert.equal(data.data[1], 255);
|
||||
assert.equal(data.data[2], 255);
|
||||
assert.equal(data.data[3], 255);
|
||||
|
||||
// Thorough testing of the cache state
|
||||
for (let tile of viewer.tileCache._tilesLoaded) {
|
||||
|
||||
const caches = Object.entries(tile._caches);
|
||||
assert.equal(caches.length, 1, `Tile ${getTileDescription(tile)} has only single, original cache`);
|
||||
for (let [key, value] of caches) {
|
||||
assert.ok(value.loaded, `Attached cache '${key}' is ready.`);
|
||||
assert.notOk(value._destroyed, `Attached cache '${key}' is not destroyed.`);
|
||||
assert.ok(value._tiles.includes(tile), `Attached cache '${key}' reference is bidirectional.`);
|
||||
}
|
||||
assert.notOk(tile.getCache(tile._wcKey), "Tile cache working key is unset");
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
}());
|
@ -2,8 +2,7 @@
|
||||
|
||||
(function() {
|
||||
var viewer;
|
||||
const drawerTypes = ['webgl','canvas','html'];
|
||||
drawerTypes.forEach(runDrawerTests);
|
||||
OpenSeadragon.getBuiltInDrawersForTest().forEach(runDrawerTests);
|
||||
|
||||
function runDrawerTests(drawerType){
|
||||
|
||||
|
@ -113,7 +113,6 @@
|
||||
});
|
||||
|
||||
// ----------
|
||||
// TODO: this used to be async
|
||||
QUnit.test('basics', function(assert) {
|
||||
const done = assert.async();
|
||||
const fakeViewer = MockSeadragon.getViewer(
|
||||
|
@ -66,6 +66,7 @@
|
||||
<script src="/test/modules/ajax-post-data.js"></script>
|
||||
<script src="/test/modules/imageloader.js"></script>
|
||||
<script src="/test/modules/tilesource-dynamic-url.js"></script>
|
||||
<script src="/test/modules/data-manipulation.js"></script>
|
||||
<!--The navigator tests are the slowest (for now; hopefully they can be sped up)
|
||||
so we put them last. -->
|
||||
<script src="/test/modules/navigator.js"></script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user