diff --git a/core/connection.js b/core/connection.js index 81489c570..9da7b0171 100644 --- a/core/connection.js +++ b/core/connection.js @@ -200,6 +200,8 @@ Blockly.Connection.prototype.checkConnection_ = function(target) { throw 'Source connection already connected.'; case Blockly.Connection.REASON_TARGET_NULL: throw 'Target connection is null.'; + case Blockly.Connection.REASON_CHECKS_FAILED: + throw 'Connection checks failed.'; default: throw 'Unknown connection failure: this should never happen!'; } @@ -293,8 +295,7 @@ Blockly.Connection.prototype.connect = function(otherConnection) { } // Establish the connections. - this.targetConnection = otherConnection; - otherConnection.targetConnection = this; + Blockly.Connection.connectReciprocally(this, otherConnection); // Demote the inferior block so that one is a child of the superior one. childBlock.setParent(parentBlock); @@ -319,6 +320,19 @@ Blockly.Connection.prototype.connect = function(otherConnection) { } }; +/** + * Update two connections to target each other. + * @param {Blockly.Connection} first The first connection to update. + * @param {Blockly.Connection} second The second conneciton to update. + */ +Blockly.Connection.connectReciprocally = function(first, second) { + if (!first || !second) { + throw 'Cannot connect null connections.'; + } + first.targetConnection = second; + second.targetConnection = first; +} + /** * Does the given block have one and only one connection point that will accept * an orphaned block? diff --git a/tests/jsunit/connection_db_test.js b/tests/jsunit/connection_db_test.js new file mode 100644 index 000000000..c7f897168 --- /dev/null +++ b/tests/jsunit/connection_db_test.js @@ -0,0 +1,95 @@ +/** + * @license + * Blockly Tests + * + * Copyright 2015 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'; + +function verify_DB_(msg, expected, db) { + var equal = (expected.length == db.length); + if (equal) { + for (var x = 0; x < expected.length; x++) { + if (expected[x] != db[x]) { + equal = false; + break; + } + } + } + if (equal) { + assertTrue(msg, true); + } else { + assertEquals(msg, expected, db); + } +} + +function test_DB_addConnection() { + var db = new Blockly.ConnectionDB(); + var o2 = {y_: 2, sourceBlock_: {}}; + db.addConnection_(o2); + verify_DB_('Adding connection #2', [o2], db); + + var o4 = {y_: 4, sourceBlock_: {}}; + db.addConnection_(o4); + verify_DB_('Adding connection #4', [o2, o4], db); + + var o1 = {y_: 1, sourceBlock_: {}}; + db.addConnection_(o1); + verify_DB_('Adding connection #1', [o1, o2, o4], db); + + var o3a = {y_: 3, sourceBlock_: {}}; + db.addConnection_(o3a); + verify_DB_('Adding connection #3a', [o1, o2, o3a, o4], db); + + var o3b = {y_: 3, sourceBlock_: {}}; + db.addConnection_(o3b); + verify_DB_('Adding connection #3b', [o1, o2, o3b, o3a, o4], db); +} + +function test_DB_removeConnection() { + var db = new Blockly.ConnectionDB(); + var o1 = {y_: 1, sourceBlock_: {}}; + var o2 = {y_: 2, sourceBlock_: {}}; + var o3a = {y_: 3, sourceBlock_: {}}; + var o3b = {y_: 3, sourceBlock_: {}}; + var o3c = {y_: 3, sourceBlock_: {}}; + var o4 = {y_: 4, sourceBlock_: {}}; + db.addConnection_(o1); + db.addConnection_(o2); + db.addConnection_(o3c); + db.addConnection_(o3b); + db.addConnection_(o3a); + db.addConnection_(o4); + verify_DB_('Adding connections 1-4', [o1, o2, o3a, o3b, o3c, o4], db); + + db.removeConnection_(o2); + verify_DB_('Removing connection #2', [o1, o3a, o3b, o3c, o4], db); + + db.removeConnection_(o4); + verify_DB_('Removing connection #4', [o1, o3a, o3b, o3c], db); + + db.removeConnection_(o1); + verify_DB_('Removing connection #1', [o3a, o3b, o3c], db); + + db.removeConnection_(o3a); + verify_DB_('Removing connection #3a', [o3b, o3c], db); + + db.removeConnection_(o3c); + verify_DB_('Removing connection #3c', [o3b], db); + + db.removeConnection_(o3b); + verify_DB_('Removing connection #3b', [], db); +} diff --git a/tests/jsunit/connection_test.js b/tests/jsunit/connection_test.js index c7f897168..b9bcc2b23 100644 --- a/tests/jsunit/connection_test.js +++ b/tests/jsunit/connection_test.js @@ -2,7 +2,7 @@ * @license * Blockly Tests * - * Copyright 2015 Google Inc. + * Copyright 2016 Google Inc. * https://developers.google.com/blockly/ * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,79 +17,217 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/** + * @fileoverview Tests for connection logic. + * @author fenichel@google.com (Rachel Fenichel) + */ 'use strict'; -function verify_DB_(msg, expected, db) { - var equal = (expected.length == db.length); - if (equal) { - for (var x = 0; x < expected.length; x++) { - if (expected[x] != db[x]) { - equal = false; - break; - } - } - } - if (equal) { - assertTrue(msg, true); - } else { - assertEquals(msg, expected, db); - } +var input; +var output; +var previous; +var next; + +var dummyWorkspace; + +function connectionTest_setUp() { + dummyWorkspace = {}; + input = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.INPUT_VALUE); + output = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE); + previous = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.PREVIOUS_STATEMENT); + next = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.NEXT_STATEMENT); } -function test_DB_addConnection() { - var db = new Blockly.ConnectionDB(); - var o2 = {y_: 2, sourceBlock_: {}}; - db.addConnection_(o2); - verify_DB_('Adding connection #2', [o2], db); - - var o4 = {y_: 4, sourceBlock_: {}}; - db.addConnection_(o4); - verify_DB_('Adding connection #4', [o2, o4], db); - - var o1 = {y_: 1, sourceBlock_: {}}; - db.addConnection_(o1); - verify_DB_('Adding connection #1', [o1, o2, o4], db); - - var o3a = {y_: 3, sourceBlock_: {}}; - db.addConnection_(o3a); - verify_DB_('Adding connection #3a', [o1, o2, o3a, o4], db); - - var o3b = {y_: 3, sourceBlock_: {}}; - db.addConnection_(o3b); - verify_DB_('Adding connection #3b', [o1, o2, o3b, o3a, o4], db); +function connectionTest_tearDown() { + input = null; + output = null; + previous = null; + next = null; + dummyWorkspace = null; } -function test_DB_removeConnection() { - var db = new Blockly.ConnectionDB(); - var o1 = {y_: 1, sourceBlock_: {}}; - var o2 = {y_: 2, sourceBlock_: {}}; - var o3a = {y_: 3, sourceBlock_: {}}; - var o3b = {y_: 3, sourceBlock_: {}}; - var o3c = {y_: 3, sourceBlock_: {}}; - var o4 = {y_: 4, sourceBlock_: {}}; - db.addConnection_(o1); - db.addConnection_(o2); - db.addConnection_(o3c); - db.addConnection_(o3b); - db.addConnection_(o3a); - db.addConnection_(o4); - verify_DB_('Adding connections 1-4', [o1, o2, o3a, o3b, o3c, o4], db); +/** + * These tests check that the reasons for failures to connect are consistent (internal view of + * error states). + */ +function testCanConnectWithReason_TargetNull() { + connectionTest_setUp(); - db.removeConnection_(o2); - verify_DB_('Removing connection #2', [o1, o3a, o3b, o3c, o4], db); + assertEquals(Blockly.Connection.REASON_TARGET_NULL, input.canConnectWithReason_(null)); - db.removeConnection_(o4); - verify_DB_('Removing connection #4', [o1, o3a, o3b, o3c], db); - - db.removeConnection_(o1); - verify_DB_('Removing connection #1', [o3a, o3b, o3c], db); - - db.removeConnection_(o3a); - verify_DB_('Removing connection #3a', [o3b, o3c], db); - - db.removeConnection_(o3c); - verify_DB_('Removing connection #3c', [o3b], db); - - db.removeConnection_(o3b); - verify_DB_('Removing connection #3b', [], db); + connectionTest_tearDown(); } + +function testCanConnectWithReason_Disconnect() { + connectionTest_setUp(); + + var tempConnection = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE); + Blockly.Connection.connectReciprocally(input, tempConnection); + assertEquals(Blockly.Connection.REASON_MUST_DISCONNECT, input.canConnectWithReason_(output)); + + connectionTest_tearDown(); +} + +function testCanConnectWithReason_DifferentWorkspaces() { + connectionTest_setUp(); + + input = new Blockly.Connection({workspace: {}}, Blockly.INPUT_VALUE); + output = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE); + + assertEquals(Blockly.Connection.REASON_DIFFERENT_WORKSPACES, input.canConnectWithReason_(output)); + + connectionTest_tearDown(); +} + + +function testCanConnectWithReason_Self() { + connectionTest_setUp(); + + var block = {type_: "test block"}; + input.sourceBlock_ = block; + assertEquals(Blockly.Connection.REASON_SELF_CONNECTION, input.canConnectWithReason_(input)); + + connectionTest_tearDown(); +} + +function testCanConnectWithReason_Type() { + connectionTest_setUp(); + + assertEquals(Blockly.Connection.REASON_WRONG_TYPE, input.canConnectWithReason_(previous)); + assertEquals(Blockly.Connection.REASON_WRONG_TYPE, input.canConnectWithReason_(next)); + + assertEquals(Blockly.Connection.REASON_WRONG_TYPE, output.canConnectWithReason_(previous)); + assertEquals(Blockly.Connection.REASON_WRONG_TYPE, output.canConnectWithReason_(next)); + + assertEquals(Blockly.Connection.REASON_WRONG_TYPE, previous.canConnectWithReason_(input)); + assertEquals(Blockly.Connection.REASON_WRONG_TYPE, previous.canConnectWithReason_(output)); + + assertEquals(Blockly.Connection.REASON_WRONG_TYPE, next.canConnectWithReason_(input)); + assertEquals(Blockly.Connection.REASON_WRONG_TYPE, next.canConnectWithReason_(output)); + + connectionTest_tearDown(); +} + +function testCanConnectWithReason_CanConnect() { + connectionTest_setUp(); + + assertEquals(Blockly.Connection.CAN_CONNECT, previous.canConnectWithReason_(next)); + assertEquals(Blockly.Connection.CAN_CONNECT, next.canConnectWithReason_(previous)); + assertEquals(Blockly.Connection.CAN_CONNECT, input.canConnectWithReason_(output)); + assertEquals(Blockly.Connection.CAN_CONNECT, output.canConnectWithReason_(input)); + + connectionTest_tearDown(); +} + +/** + * The next set of tests checks that exceptions are being thrown at the correct times (external + * view of errors). + */ +function testCheckConnection_Self() { + connectionTest_setUp(); + var block = {type_: "test block"}; + input.sourceBlock_ = block; + try { + input.checkConnection_(input); + fail(); + } catch (e) { + // expected + } + + connectionTest_tearDown(); +} + +function testCheckConnection_TypeInputPrev() { + connectionTest_setUp(); + try { + input.checkConnection_(previous); + fail(); + } catch (e) { + // expected + } + + connectionTest_tearDown(); +} + +function testCheckConnection_TypeInputNext() { + connectionTest_setUp(); + try { + input.checkConnection_(next); + fail(); + } catch (e) { + // expected + } + + connectionTest_tearDown(); +} + +function testCheckConnection_TypeOutputPrev() { + connectionTest_setUp(); + try { + output.checkConnection_(previous); + fail(); + } catch (e) { + // expected + } + + connectionTest_tearDown(); +} + +function testCheckConnection_TypePrevInput() { + connectionTest_setUp(); + try { + previous.checkConnection_(input); + fail(); + } catch (e) { + // expected + } + + connectionTest_tearDown(); +} + +function testCheckConnection_TypePrevOutput() { + connectionTest_setUp(); + try { + previous.checkConnection_(output); + fail(); + } catch (e) { + // expected + } + + connectionTest_tearDown(); +} + +function testCheckConnection_TypeNextInput() { + connectionTest_setUp(); + try { + next.checkConnection_(input); + fail(); + } catch (e) { + // expected + } + + connectionTest_tearDown(); +} + +function testCheckConnection_TypeNextOutput() { + connectionTest_setUp(); + try { + next.checkConnection_(output); + fail(); + } catch (e) { + // expected + } + + connectionTest_tearDown(); +} + +function testCheckConnection_Okay() { + connectionTest_setUp(); + previous.checkConnection_(next); + next.checkConnection_(previous); + input.checkConnection_(output); + output.checkConnection_(input); + + connectionTest_tearDown(); +} \ No newline at end of file diff --git a/tests/jsunit/index.html b/tests/jsunit/index.html index 9c3df0ea6..48f134e89 100644 --- a/tests/jsunit/index.html +++ b/tests/jsunit/index.html @@ -10,6 +10,7 @@ +