FLEX-35031 -Since I'm planning to use ObjectUtil.isDynamicObject() in the fix, I wanted to unit test it. -Also, I introduced a new function in ObjectUtil to be used in the solution, getEnumerableProperties(), which returns all the dynamic properties of an object. In order to unit test this new method I needed a function with which to compare Arrays. So I added it to ArrayUtil, including the unit tests needed to validate it. -Also edited an asdoc entry and renamed a function parameter.
Project: http://git-wip-us.apache.org/repos/asf/flex-sdk/repo Commit: http://git-wip-us.apache.org/repos/asf/flex-sdk/commit/680b405d Tree: http://git-wip-us.apache.org/repos/asf/flex-sdk/tree/680b405d Diff: http://git-wip-us.apache.org/repos/asf/flex-sdk/diff/680b405d Branch: refs/heads/develop Commit: 680b405dfc4559ecf81239352b5bd9676d00adc8 Parents: e68c148 Author: Mihai Chira <[email protected]> Authored: Wed Feb 17 13:35:31 2016 +0100 Committer: Mihai Chira <[email protected]> Committed: Wed Feb 17 13:35:31 2016 +0100 ---------------------------------------------------------------------- .../framework/src/mx/utils/ArrayUtil.as | 92 ++++++ .../framework/src/mx/utils/ObjectUtil.as | 33 ++- .../framework/tests/mx/utils/ArrayUtil_Tests.as | 293 +++++++++++++++++++ .../tests/mx/utils/ObjectUtil_Tests.as | 167 +++++++++++ 4 files changed, 581 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/680b405d/frameworks/projects/framework/src/mx/utils/ArrayUtil.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/framework/src/mx/utils/ArrayUtil.as b/frameworks/projects/framework/src/mx/utils/ArrayUtil.as index c41cccf..85ec79c 100644 --- a/frameworks/projects/framework/src/mx/utils/ArrayUtil.as +++ b/frameworks/projects/framework/src/mx/utils/ArrayUtil.as @@ -107,6 +107,98 @@ public class ArrayUtil return -1; } + + /** + * Checks if the Array instances contain the same values + * against the same indexes. + * + * @param a The first Array instance. + * @param b The second Array instance. + * @return true if the two Arrays contain the same values + * (determined using the strict equality operator) associated + * with the same indexes. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function arraysMatch(a:Array, b:Array):Boolean + { + if(!a || !b) + return false; + + if(a.length != b.length) + return false; + + var indexesA:Array = ObjectUtil.getEnumerableProperties(a); + + for (var i:int = 0; i < indexesA.length; i++) + { + var index:String = indexesA[i]; + + if(!b.hasOwnProperty(index) || a[index] !== b[index]) + return false; + } + + return true; + } + + /** + * Checks if the Array instances contain the same values, + * even if in different orders. + * + * @param a The first Array instance. + * @param b The second Array instance. + * @return true if the two Arrays contain the same values. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function arrayValuesMatch(a:Array, b:Array):Boolean + { + if(!a || !b) + return false; + + var valuesOfA:Array = getArrayValues(a); + valuesOfA.sort(); + + var valuesOfB:Array = getArrayValues(b); + valuesOfB.sort(); + + return arraysMatch(valuesOfA, valuesOfB); + } + + /** + * Used to obtain the values in an Array, whether indexed + * or associative. + * + * @param value The Array instance. + * @return an indexed Array with the values found in <code>value</code>. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function getArrayValues(value:Array):Array + { + var result:Array = []; + + if(!value) + return result; + + var indexes:Array = ObjectUtil.getEnumerableProperties(value); + + for each(var index:String in indexes) + { + result.push(value[index]); + } + + return result; + } } } http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/680b405d/frameworks/projects/framework/src/mx/utils/ObjectUtil.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/framework/src/mx/utils/ObjectUtil.as b/frameworks/projects/framework/src/mx/utils/ObjectUtil.as index 6b1e79a..b1f23cc 100644 --- a/frameworks/projects/framework/src/mx/utils/ObjectUtil.as +++ b/frameworks/projects/framework/src/mx/utils/ObjectUtil.as @@ -1173,7 +1173,7 @@ public class ObjectUtil /** * Returns <code>true</code> if the object is an instance of a dynamic class. * - * @param obj The object. + * @param object The object. * * @return <code>true</code> if the object is an instance of a dynamic class. * @@ -1182,24 +1182,49 @@ public class ObjectUtil * @playerversion AIR 1.1 * @productversion Flex 3 */ - public static function isDynamicObject(obj:Object):Boolean + public static function isDynamicObject(object:Object):Boolean { try { // this test for checking whether an object is dynamic or not is // pretty hacky, but it assumes that no-one actually has a // property defined called "wootHackwoot" - obj["wootHackwoot"]; + object["wootHackwoot"]; } catch (e:Error) { - // our object isn't from a dynamic class + // our object isn't an instance of a dynamic class return false; } return true; } /** + * Returns all the properties defined dynamically on an object. + * + * @param object The object to inspect. + * + * @return an <code>Array</code> of the enumerable properties of the object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function getEnumerableProperties(object:Object):Array + { + var result:Array = []; + + if(!isDynamicObject(object)) + return result; + + for (var property:String in object) + result.push(property); + + return result; + } + + /** * Returns the value at the end of the property chain <code>path</code>. * If the value cannot be reached due to null links on the chain, * <code>undefined</code> is returned. http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/680b405d/frameworks/projects/framework/tests/mx/utils/ArrayUtil_Tests.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/framework/tests/mx/utils/ArrayUtil_Tests.as b/frameworks/projects/framework/tests/mx/utils/ArrayUtil_Tests.as new file mode 100644 index 0000000..d11f468 --- /dev/null +++ b/frameworks/projects/framework/tests/mx/utils/ArrayUtil_Tests.as @@ -0,0 +1,293 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You 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. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.utils +{ + import org.flexunit.asserts.assertEquals; + import org.flexunit.asserts.assertFalse; + import org.flexunit.asserts.assertNotNull; + import org.flexunit.asserts.assertTrue; + + public class ArrayUtil_Tests + { + //-------------------------------------------------------------------------- + // + // arraysMatch() + // + //-------------------------------------------------------------------------- + + [Test] + public function test_array_matches_with_itself():void + { + //given + var array:Array = [1, 2, 3]; + + //then + assertTrue(ArrayUtil.arraysMatch(array, array)); + } + + [Test] + public function test_empty_arrays_match():void + { + //then + assertTrue(ArrayUtil.arraysMatch([], [])); + } + + [Test] + public function test_arrays_with_same_values_in_different_indexes_dont_match():void + { + //then + assertFalse(ArrayUtil.arraysMatch(["name", "age"], ["age", "name"])); + } + + [Test] + public function test_arrays_match_when_properties_created_in_different_orders():void + { + //given + var arrayA:Array = []; + var arrayB:Array = []; + + //when + arrayA["name"] = "AName"; + arrayA["age"] = 22; + + arrayB["age"] = 22; + arrayB["name"] = "AName"; + + //then + assertTrue(ArrayUtil.arraysMatch(arrayA, arrayB)); + } + + [Test] + public function test_arrays_dont_match_when_indexes_match_but_values_different():void + { + //given + var arrayA:Array = []; + var arrayB:Array = []; + + //when + arrayA["name"] = "AName"; + arrayA["age"] = 444; + + arrayB["age"] = 22; + arrayB["name"] = "BName"; + + //then + assertFalse(ArrayUtil.arraysMatch(arrayA, arrayB)); + } + + [Test] + public function test_arrays_dont_match_when_they_have_same_number_of_different_indexes():void + { + //given + var arrayA:Array = []; + var arrayB:Array = []; + + //when + arrayA["name"] = "AName"; + arrayA["age"] = 444; + + arrayB["radius"] = "AName"; + arrayB[9] = 444; + + //then + assertFalse(ArrayUtil.arraysMatch(arrayA, arrayB)); + } + + [Test] + public function test_arrays_match_when_indexes_expressed_in_string_and_int():void + { + //given + var arrayA:Array = []; + var arrayB:Array = []; + + //when + arrayA[3] = "value"; + arrayA["4"] = "value"; + + arrayB["3"] = "value"; + arrayB[4] = "value"; + + //then + assertTrue(ArrayUtil.arraysMatch(arrayA, arrayB)); + } + + [Test] + public function test_arrays_dont_match_when_values_expressed_in_string_and_int():void + { + //given + var arrayA:Array = []; + var arrayB:Array = []; + + //when + arrayA[3] = 3; + arrayA[4] = 4; + + arrayB[3] = "3"; + arrayB[4] = "4"; + + //then + assertFalse(ArrayUtil.arraysMatch(arrayA, arrayB)); + } + + [Test] + public function test_array_and_null_dont_match():void + { + //then + assertFalse(ArrayUtil.arraysMatch([1, 2, 3], null)); + } + + [Test] + public function test_null_and_null_dont_match():void + { + //then + assertFalse(ArrayUtil.arraysMatch(null, null)); + } + + [Test] + public function test_arrays_with_null_in_different_positions_dont_match():void + { + //then + assertFalse(ArrayUtil.arraysMatch([null, 0], [0, null])); + } + + //-------------------------------------------------------------------------- + // + // arrayValuesMatch() + // + //-------------------------------------------------------------------------- + + [Test] + public function test_arrays_with_same_values_in_different_indexes_match_in_terms_of_values():void + { + //then + assertTrue(ArrayUtil.arrayValuesMatch(["name", "age"], ["age", "name"])); + } + + [Test] + public function test_array_values_dont_match_with_null():void + { + //then + assertFalse(ArrayUtil.arrayValuesMatch(["name", "age"], null)); + } + + [Test] + public function test_null_doesnt_match_values_with_array():void + { + //then + assertFalse(ArrayUtil.arrayValuesMatch(null, ["name", "age"])); + } + + [Test] + public function test_null_and_null_dont_have_matching_values():void + { + //then + assertFalse(ArrayUtil.arrayValuesMatch(null, null)); + } + + [Test] + public function test_array_values_match_although_they_have_different_indexes():void + { + //given + var arrayA:Array = []; + var arrayB:Array = []; + + //when + arrayA["name"] = "AName"; + arrayA["age"] = 444; + + arrayB["label"] = "AName"; + arrayB["spin"] = 444; + + //then + assertTrue(ArrayUtil.arrayValuesMatch(arrayA, arrayB)); + } + + [Test] + public function test_array_values_dont_match_although_they_have_same_indexes():void + { + //given + var arrayA:Array = []; + var arrayB:Array = []; + + //when + arrayA["name"] = "AName"; + arrayA["age"] = 444; + + arrayB["name"] = "BName"; + arrayB["age"] = 21; + + //then + assertFalse(ArrayUtil.arrayValuesMatch(arrayA, arrayB)); + } + + [Test] + //relevant because the array length is changed when a numeric index is used + public function test_array_values_match_even_when_one_of_their_indexes_is_numeric():void + { + //given + var arrayA:Array = []; + var arrayB:Array = []; + + //when + arrayA["name"] = "AName"; + arrayA["age"] = 444; + + arrayB["label"] = "AName"; + arrayB[9] = 444; + + //then + assertTrue(ArrayUtil.arrayValuesMatch(arrayA, arrayB)); + } + + + //-------------------------------------------------------------------------- + // + // getArrayValues() + // + //-------------------------------------------------------------------------- + [Test] + public function test_values_of_null_is_an_empty_array():void + { + //when + var values:Array = ArrayUtil.getArrayValues(null); + + //then + assertNotNull(values); + assertEquals(0, values.length); + } + + [Test] + public function test_values_of_array_whose_index_was_set_manually_to_a_number_include_only_that_value():void + { + //given + var array:Array = []; + array[2] = "hey"; + + //when + var values:Array = ArrayUtil.getArrayValues(array); + + //then + assertNotNull(values); + assertEquals(3, array.length); + assertEquals(1, values.length); + assertEquals("hey", values[0]); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/680b405d/frameworks/projects/framework/tests/mx/utils/ObjectUtil_Tests.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/framework/tests/mx/utils/ObjectUtil_Tests.as b/frameworks/projects/framework/tests/mx/utils/ObjectUtil_Tests.as new file mode 100644 index 0000000..a373267 --- /dev/null +++ b/frameworks/projects/framework/tests/mx/utils/ObjectUtil_Tests.as @@ -0,0 +1,167 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You 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. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.utils { + import flash.utils.Dictionary; + + import org.flexunit.asserts.assertEquals; + + import org.flexunit.asserts.assertFalse; + import org.flexunit.asserts.assertTrue; + + public class ObjectUtil_Tests + { + //-------------------------------------------------------------------------- + // + // isDynamicObject() + // + //-------------------------------------------------------------------------- + + [Test] + public function test_dynamic_class_instance_recognized_as_dynamic_object():void + { + //then + assertTrue(ObjectUtil.isDynamicObject(new DynamicVO("John"))); + } + + [Test] + public function test_anonymous_class_instance_recognized_as_dynamic_object():void + { + //then + assertTrue(ObjectUtil.isDynamicObject({name:"John"})); + } + + [Test] + public function test_array_instance_recognized_as_dynamic_object():void + { + //then + assertTrue(ObjectUtil.isDynamicObject([])); + } + + [Test] + public function test_dictionary_instance_recognized_as_dynamic_object():void + { + //then + assertTrue(ObjectUtil.isDynamicObject(new Dictionary())); + } + + [Test] + public function test_sealed_class_instance_recognized_as_non_dynamic_object():void + { + //then + assertFalse(ObjectUtil.isDynamicObject(new NonDynamicVO("John"))); + } + + [Test] + public function test_null_does_not_throw_fatal():void + { + //then + assertFalse(ObjectUtil.isDynamicObject(null)); + } + + //-------------------------------------------------------------------------- + // + // getEnumerableProperties() + // + //-------------------------------------------------------------------------- + + [Test] + public function test_enumerable_properties_of_anonymous_object():void + { + //given + var object:Object = {name:"John", age:32}; + + //when + var enumerableProperties:Array = ObjectUtil.getEnumerableProperties(object); + + //then + assertTrue(ArrayUtil.arrayValuesMatch(["age", "name"], enumerableProperties)); + } + + [Test] + public function test_enumerable_properties_of_null():void + { + //when + var enumerableProperties:Array = ObjectUtil.getEnumerableProperties(null); + + //then + assertEquals(0, enumerableProperties.length); + } + + [Test] + public function test_enumerable_properties_of_dictionary():void + { + //given + var object:Object = new Dictionary(false); + object["name"] = "John"; + object["age"] = 9; + + //when + var enumerableProperties:Array = ObjectUtil.getEnumerableProperties(object); + + //then + assertTrue(ArrayUtil.arrayValuesMatch(["age", "name"], enumerableProperties)); + } + + [Test] + public function test_enumerable_properties_of_dynamic_object():void + { + //given + var object:Object = new DynamicVO("John"); + object["age"] = 9; + + //when + var enumerableProperties:Array = ObjectUtil.getEnumerableProperties(object); + + //then + assertTrue(ArrayUtil.arrayValuesMatch(["age", "name"], enumerableProperties)); + } + + [Test] + public function test_enumerable_properties_of_sealed_class_instance():void + { + //given + var object:Object = new NonDynamicVO("John"); + + //when + var enumerableProperties:Array = ObjectUtil.getEnumerableProperties(object); + + //then + assertEquals(0, enumerableProperties.length); + } + } +} + +dynamic class DynamicVO +{ + public function DynamicVO(name:String) + { + this.name = name; + } +} + +class NonDynamicVO +{ + public var name:String; + + public function NonDynamicVO(name:String) + { + this.name = name; + } +} \ No newline at end of file
