Looks like some nice new capabilities for ArrayUtil and ObjectUtil. Did you rule out using describeType for isDynamicObject? I know it is slow, but it should always be right. What should isDynamicObject return for XML, Proxy and ObjectProxy?
Thanks, -Alex On 2/17/16, 8:00 AM, "mih...@apache.org" <mih...@apache.org> wrote: >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 <mih...@apache.org> >Authored: Wed Feb 17 13:35:31 2016 +0100 >Committer: Mihai Chira <mih...@apache.org> >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/p >rojects/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/p >rojects/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/p >rojects/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():v >oid >+ { >+ //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/p >rojects/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 >