scratch-blocks/tests/jsunit/procedure_test.js
2018-01-18 15:03:46 -05:00

341 lines
11 KiB
JavaScript

/**
* @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();
}
}