[Layout] Layout rebuilds after resize/reposition - Fixed potential race condition #32

This commit is contained in:
Henry 2015-11-25 18:53:37 -08:00
parent 424953c894
commit db7224486c
2 changed files with 48 additions and 18 deletions

View File

@ -45,7 +45,8 @@ define(
* @param {Scope} $scope the controller's Angular scope
*/
function LayoutController($scope) {
var self = this;
var self = this,
callbackCount = 0;
// Update grid size when it changed
function updateGridSize(layoutGrid) {
@ -91,23 +92,26 @@ define(
e.preventDefault();
}
function getComposition(domainObject){
return domainObject.useCapability('composition');
}
function composeView (composition){
$scope.composition = composition;
return composition.map(function (object) {
return object.getId();
}) || [];
}
//Will fetch fully contextualized composed objects, and populate
// scope with them.
function refreshComposition(ids) {
return getComposition($scope.domainObject)
.then(composeView)
.then(function(ids){self.layoutPanels(ids);});
function refreshComposition() {
//Keep a track of how many composition callbacks have been made
var thisCount = ++callbackCount;
$scope.domainObject.useCapability('composition').then(function(composition){
var ids;
//Is this callback for the most recent composition
// request? If not, discard it. Prevents race condition
if (thisCount === callbackCount){
ids = composition.map(function (object) {
return object.getId();
}) || [];
$scope.composition = composition;
self.layoutPanels(ids);
}
});
}
// End drag; we don't want to put $scope into this

View File

@ -33,7 +33,8 @@ define(
testConfiguration,
controller,
mockCompositionCapability,
mockComposition;
mockComposition,
mockCompositionObjects;
function mockPromise(value){
return {
@ -67,6 +68,7 @@ define(
testModel = {};
mockComposition = ["a", "b", "c"];
mockCompositionObjects = mockComposition.map(mockDomainObject);
testConfiguration = {
panels: {
@ -77,7 +79,7 @@ define(
}
};
mockCompositionCapability = mockPromise(mockComposition.map(mockDomainObject));
mockCompositionCapability = mockPromise(mockCompositionObjects);
mockScope.domainObject = mockDomainObject("mockDomainObject");
mockScope.model = testModel;
@ -107,6 +109,30 @@ define(
);
});
it("Is robust to concurrent changes to composition", function () {
var secondMockComposition = ["a", "b", "c", "d"],
secondMockCompositionObjects = secondMockComposition.map(mockDomainObject),
firstCompositionCB,
secondCompositionCB;
spyOn(mockCompositionCapability, "then");
mockScope.$watchCollection.mostRecentCall.args[1]();
mockScope.$watchCollection.mostRecentCall.args[1]();
firstCompositionCB = mockCompositionCapability.then.calls[0].args[0];
secondCompositionCB = mockCompositionCapability.then.calls[1].args[0];
//Resolve promises in reverse order
secondCompositionCB(secondMockCompositionObjects);
firstCompositionCB(mockCompositionObjects);
//Expect the promise call that was initiated most recently to
// be the one used to populate scope, irrespective of order that
// it was eventually resolved
expect(mockScope.composition).toBe(secondMockCompositionObjects);
});
it("provides styles for frames, from configuration", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
expect(controller.getFrameStyle("a")).toEqual({