// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. // /// /// /// interface HTMLElement { dispose(); } module CorsicaTests { "use strict"; var mockControl = function () { this.element = document.createElement("div"); this.element.winControl = this; }; function createNestedTree(root:HTMLElement, levels:number, children:number, disposeImpl?) { if (!disposeImpl) { disposeImpl = function () { }; } WinJS.Utilities.markDisposable(root, disposeImpl.bind(root)); if (levels == 1) return; for (var i = 0; i < children; i++) { var child = document.createElement("div"); root.appendChild(child); createNestedTree(child, levels - 1, children, disposeImpl); } }; function markDisposableOrder(root, callback) { var n = root; while (n) { // Find first left most leaf on a path which have not yet been visited if (n.firstChild && !n.v) { n.v = true; // Mark node as visited n = n.firstChild; } else { if (callback) callback(n); //n.id = order++; // add id if (n == root) // If n is root node, traversal is complete so break loop break; // Else find next node if (n.nextSibling) n = n.nextSibling; else n = n.parentNode; } } } export class Dispose { testMarkDomElmntDisposable(complete) { var div = document.createElement("div"); WinJS.Utilities.markDisposable(div, function () { document.body.removeChild(div); complete(); }); document.body.appendChild(div); div.dispose(); } testMarkIframeDisposable(complete) { var iframe = document.createElement("iframe"); WinJS.Utilities.markDisposable(iframe, function () { document.body.removeChild(iframe); complete(); }); document.body.appendChild(iframe); iframe.dispose(); } testDisposeImplCalledBottomUp() { var root = document.createElement("div"); var callOrder = []; var startId = 0; var levels = 6; var children = 2; createNestedTree(root, levels, children, function () { callOrder.push(this.id); }); markDisposableOrder(root, function (e) { if (e.className.indexOf("win-disposable") >= 0) // mark if element is disposable e.id = startId++; }); root.dispose(); //verify call order var count = 0; for (var i in callOrder) { if (count != i) LiveUnit.Assert.areEqual(count, i, "dispose order is in correct for : " + root.innerHTML); else count++; } } testDisposeCalledOnTreeWithRandomDispElmnts() { var root = document.createElement("div"); var callOrder = []; var startId = 0; var levels = 4; var children = 2; createNestedTree(root, levels, children, function () { callOrder.push(this.id); }); // unmark few elements as non disposable (root.querySelectorAll("div")[0]).removeAttribute("class"); (root.querySelectorAll("div")[8]).removeAttribute("class"); (root.querySelectorAll("div")[11]).removeAttribute("class"); markDisposableOrder(root, function (e) { if (e.className.indexOf("win-disposable") >= 0) // mark if element is disposable e.id = startId++; }); root.dispose(); //verify call order var count = 0; for (var i in callOrder) { if (count != i) LiveUnit.Assert.areEqual(count, i, "dispose order is in correct for : " + root.innerHTML); else count++; } if (count != (Math.pow(children, levels) - 1 - 3)) LiveUnit.Assert.fail("Wrong number of dispose calls"); } testMarkDisposableFulfillsPattern() { var div = document.createElement("div"); var disposeImpl = function () { }; WinJS.Utilities.markDisposable(div, disposeImpl); LiveUnit.Assert.isTrue(div.classList.contains("win-disposable")); LiveUnit.Assert.isTrue(div.dispose); LiveUnit.Assert.isTrue(div.dispose !== disposeImpl); div.dispose(); div.dispose(); } testDefaultDisposeDoesNotDoubleDispose() { var div = document.createElement("div"); var disposed = false; WinJS.Utilities.markDisposable(div, function () { if (disposed) LiveUnit.Assert.fail("Default dispose should not call disposeImpl on subsequent dispose calls."); disposed = true; }); div.dispose(); div.dispose(); } testDisposeCallsEveryDisposeImpl() { var levels = 5; var children = 2; var total = Math.pow(children, levels) - 1; var calls = 0; var root = document.createElement("div"); createNestedTree(root, levels, children, function () { calls++; }); document.body.appendChild(root); WinJS.Utilities.disposeSubTree(root); LiveUnit.Assert.areEqual(total - 1, calls); // -1 because we are not calling on root node // call dispose again on root calls = 0; root.dispose(); LiveUnit.Assert.areEqual(1, calls); // called once on root // call again on root calls = 0; root.dispose(); LiveUnit.Assert.areEqual(0, calls); // called once on root document.body.removeChild(root); } testDisposeSubTreeDoesNotDisposeNestedDisposables() { var div = document.createElement("div"); var child = document.createElement("div"); var descendant = document.createElement("div"); WinJS.Utilities.markDisposable(child); child.dispose = function () { }; WinJS.Utilities.markDisposable(descendant, function () { LiveUnit.Assert.fail("Should not have been disposed!"); }); child.appendChild(descendant); div.appendChild(child); WinJS.Utilities.disposeSubTree(div); } testSubsequentDisposeCallsDontCrash() { var root = document.createElement("div"); createNestedTree(root, 5, 3); WinJS.Utilities.disposeSubTree(root); WinJS.Utilities.disposeSubTree(root); } testCallMarkDisposeMultipletimes() { var div = document.createElement("div"); var called = 0; WinJS.Utilities.markDisposable(div, function () { LiveUnit.Assert.fail("This should get overwritten by second call"); }); WinJS.Utilities.markDisposable(div, function () { called++; }); div.dispose(); div.dispose(); div.dispose(); div.dispose(); LiveUnit.Assert.areEqual(1, called); LiveUnit.Assert.areEqual("win-disposable", div.getAttribute("class")); //make sure only one "win-disposable" is added to class attribute } testMarkDisposableHandlesWinControlHostElements() { var control = new mockControl(); var disposed = false; var dupeCall = false; WinJS.Utilities.markDisposable(control.element, function () { dupeCall = disposed; disposed = true; }); LiveUnit.Assert.isTrue(control.dispose); LiveUnit.Assert.isFalse(control.element.dispose); control.dispose(); LiveUnit.Assert.isTrue(disposed); LiveUnit.Assert.isFalse(dupeCall); } } } LiveUnit.registerTestClass("CorsicaTests.Dispose");