/** * @license * Blockly Tests * * Copyright 2017 Google Inc. * https://developers.google.com/blockly/ * * 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. */ 'use strict'; goog.require('goog.testing'); goog.require('goog.testing.MockControl'); var workspace; //var mockControl_; function procedureTest_setUp() { Blockly.Blocks[Blockly.PROCEDURES_CALL_BLOCK_TYPE] = { init: function() { this.procCode_ = ''; this.setPreviousStatement(true); this.setNextStatement(true); }, getProcCode: function() { return this.procCode_; } }; Blockly.Blocks['foo'] = { init: function() { this.jsonInit({ "message0": "foo", "previousStatement": null, "nextStatement": null }); this.setNextStatement(true); } }; Blockly.Blocks['loop'] = { init: function() { this.jsonInit({ message0: 'forever %1', "args0": [ { "type": "input_statement", "name": "SUBSTACK" } ]}); } }; workspace = new Blockly.Workspace(); //mockControl_ = new goog.testing.MockControl(); } function procedureTest_tearDown() { delete Blockly.Blocks[Blockly.PROCEDURES_CALL_BLOCK_TYPE]; delete Blockly.Blocks['foo']; delete Blockly.Blocks['loop']; //mockControl_.$tearDown(); workspace.dispose(); } function test_findCallers_simple_oneCaller() { var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<variables></variables>' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_1" x="301" y="516">' + '</block>' + '</xml>'; procedureTest_setUp(); try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); workspace.getBlockById('test_1').procCode_ = 'test_procedure'; var callers = Blockly.Procedures.getCallers('test_procedure', workspace, {id: ''}, false); assertEquals(1, callers.length); assertEquals('test_1', callers[0].id); } finally { procedureTest_tearDown(); } } function test_findCallers_noRecursion() { var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<variables></variables>' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_1" x="301" y="516">' + '</block>' + '</xml>'; procedureTest_setUp(); try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); workspace.getBlockById('test_1').procCode_ = 'test_procedure'; var rootBlock = workspace.getBlockById('test_1'); var callers = Blockly.Procedures.getCallers('test_procedure', workspace, rootBlock, false /* allowRecursion */); // There was a single call to this function, but it was in a stack that // should be ignored. assertEquals(0, callers.length); } finally { procedureTest_tearDown(); } } function test_findCallers_allowRecursion() { var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<variables></variables>' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_1" x="301" y="516">' + '</block>' + '</xml>'; procedureTest_setUp(); try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); workspace.getBlockById('test_1').procCode_ = 'test_procedure'; var rootBlock = workspace.getBlockById('test_1'); var callers = Blockly.Procedures.getCallers('test_procedure', workspace, rootBlock, true /* allowRecursion */); // There was a single call to this function, in the same stack as the // definition root. Recursion is allowed, so it should be found. assertEquals(1, callers.length); assertEquals('test_1', callers[0].id); } finally { procedureTest_tearDown(); } } function test_findCallers_simple_noCallers() { var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<variables></variables>' + '<block type="foo" id="test_1" x="301" y="516">' + '</block>' + '</xml>'; procedureTest_setUp(); try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); var callers = Blockly.Procedures.getCallers('test_procedure', workspace, {id: ''}, false); // There weren't even blocks of type procedures_callnoreturn. assertEquals(0, callers.length); } finally { procedureTest_tearDown(); } } function test_findCallers_wrongProcCode() { var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<variables></variables>' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_1" x="301" y="516">' + '</block>' + '</xml>'; procedureTest_setUp(); try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); workspace.getBlockById('test_1').procCode_ = 'wrong procedure'; var callers = Blockly.Procedures.getCallers('test_procedure', workspace, {id: ''}, false); // There was a procedure_callnoreturn call, but it had the wrong procCode. assertEquals(0, callers.length); } finally { procedureTest_tearDown(); } } function test_findCallers_onStatementInput() { var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<block type="loop" id="test_1">' + '<statement name="SUBSTACK">' + '<block type="foo" id="test_2">' + '<next>' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_3"></block>' + '</next></block>' + '</statement>' + '</block>' + '</xml>'; procedureTest_setUp(); try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); workspace.getBlockById('test_3').procCode_ = 'test_procedure'; var callers = Blockly.Procedures.getCallers('test_procedure', workspace, {id: ''}, false); // There should be one caller, connected to a stack on a statement input. assertEquals(1, callers.length); assertEquals('test_3', callers[0].id); } finally { procedureTest_tearDown(); } } function test_findCallers_multipleStacks() { var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<block type="foo" id="test_1"></block>' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_2"></block>'+ '<block type="foo" id="test_1"></block>' + '</xml>'; procedureTest_setUp(); try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); workspace.getBlockById('test_2').procCode_ = 'test_procedure'; var callers = Blockly.Procedures.getCallers('test_procedure', workspace, {id: ''}, false); // There should be one caller, but multiple stacks in the workspace. assertEquals(1, callers.length); assertEquals('test_2', callers[0].id); } finally { procedureTest_tearDown(); } } function test_findCallers_multipleCallers() { var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_1"></block>' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_2"></block>'+ '</xml>'; procedureTest_setUp(); try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); workspace.getBlockById('test_2').procCode_ = 'test_procedure'; workspace.getBlockById('test_1').procCode_ = 'test_procedure'; var callers = Blockly.Procedures.getCallers('test_procedure', workspace, {id: ''}, false); // There should be two callers, on two different stacks. assertEquals(2, callers.length); // Order doesn't matter. assertTrue(callers[0].id == 'test_1' || callers[0].id == 'test_2'); assertTrue(callers[1].id == 'test_1' || callers[1].id == 'test_2'); } finally { procedureTest_tearDown(); } } function test_deleteProcedure_noCallers() { // If there are no callers, the stack should be deleted. procedureTest_setUp(); var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_1" x="301" y="516"></block>' + '<block type="foo" id="test_2"></block>' + '<block type="foo" id="test_3"></block>' + '</block>' + '</xml>'; try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); workspace.getBlockById('test_1').procCode_ = 'test_procedure'; var rootBlock = workspace.getBlockById('test_1'); assertTrue(Blockly.Procedures.deleteProcedureDefCallback('test_procedure', rootBlock)); // The other two blocks should stick around. assertEquals(2, workspace.getTopBlocks().length); } finally { procedureTest_tearDown(); } } function test_deleteProcedure_recursiveCaller() { // If there is a caller but it's a part of stack starting with definitionRoot, // the stack should be deleted. procedureTest_setUp(); var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<block type="loop" id="test_1">' + '<statement name="SUBSTACK">' + '<block type="foo" id="test_2">' + '<next>' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_3"></block>' + '</next></block>' + '</statement>' + '</block>' + '</block>' + '</xml>'; try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); workspace.getBlockById('test_3').procCode_ = 'test_procedure'; var rootBlock = workspace.getBlockById('test_1'); assertTrue(Blockly.Procedures.deleteProcedureDefCallback('test_procedure', rootBlock)); assertEquals(0, workspace.getTopBlocks().length); } finally { procedureTest_tearDown(); } } function test_deleteProcedure_nonRecursiveCaller() { // If there is a caller and it's not part of the procedure definition, the // stack should not be deleted. procedureTest_setUp(); var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + '<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE + '" id="test_1" x="301" y="516"></block>' + '<block type="foo" id="test_2"></block>' + '<block type="foo" id="test_3"></block>' + '</xml>'; try { Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); workspace.getBlockById('test_1').procCode_ = 'test_procedure'; var rootBlock = workspace.getBlockById('test_2'); assertFalse(Blockly.Procedures.deleteProcedureDefCallback('test_procedure', rootBlock)); // All blocks should stay on the workspace. assertEquals(3, workspace.getTopBlocks().length); } finally { procedureTest_tearDown(); } }