1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ppapi/shared_impl/unittest_utils.h" 6 7 #include <cmath> 8 9 #include "base/containers/hash_tables.h" 10 #include "base/logging.h" 11 #include "ppapi/shared_impl/array_var.h" 12 #include "ppapi/shared_impl/dictionary_var.h" 13 #include "ppapi/shared_impl/var.h" 14 #include "ppapi/shared_impl/var_tracker.h" 15 16 namespace ppapi { 17 18 namespace { 19 20 // When two vars x and y are found to be equal, an entry is inserted into 21 // |visited_map| with (x.value.as_id, y.value.as_id). This allows reference 22 // cycles to be avoided. It also allows us to associate nodes in |expected| with 23 // nodes in |actual| and check whether the graphs have equivalent topology. 24 bool Equals(const PP_Var& expected, 25 const PP_Var& actual, 26 base::hash_map<int64_t, int64_t>* visited_map) { 27 if (expected.type != actual.type) { 28 LOG(ERROR) << "expected type: " << expected.type << 29 " actual type: " << actual.type; 30 return false; 31 } 32 if (VarTracker::IsVarTypeRefcounted(expected.type)) { 33 base::hash_map<int64_t, int64_t>::iterator it = 34 visited_map->find(expected.value.as_id); 35 if (it != visited_map->end()) { 36 if (it->second != actual.value.as_id) { 37 LOG(ERROR) << "expected id: " << it->second << " actual id: " << 38 actual.value.as_id; 39 return false; 40 } else { 41 return true; 42 } 43 } else { 44 (*visited_map)[expected.value.as_id] = actual.value.as_id; 45 } 46 } 47 switch (expected.type) { 48 case PP_VARTYPE_UNDEFINED: 49 return true; 50 case PP_VARTYPE_NULL: 51 return true; 52 case PP_VARTYPE_BOOL: 53 if (expected.value.as_bool != actual.value.as_bool) { 54 LOG(ERROR) << "expected: " << expected.value.as_bool << " actual: " << 55 actual.value.as_bool; 56 return false; 57 } 58 return true; 59 case PP_VARTYPE_INT32: 60 if (expected.value.as_int != actual.value.as_int) { 61 LOG(ERROR) << "expected: " << expected.value.as_int << " actual: " << 62 actual.value.as_int; 63 return false; 64 } 65 return true; 66 case PP_VARTYPE_DOUBLE: 67 if (fabs(expected.value.as_double - actual.value.as_double) > 1.0e-4) { 68 LOG(ERROR) << "expected: " << expected.value.as_double << 69 " actual: " << actual.value.as_double; 70 return false; 71 } 72 return true; 73 case PP_VARTYPE_OBJECT: 74 if (expected.value.as_id != actual.value.as_id) { 75 LOG(ERROR) << "expected: " << expected.value.as_id << " actual: " << 76 actual.value.as_id; 77 return false; 78 } 79 return true; 80 case PP_VARTYPE_STRING: { 81 StringVar* expected_var = StringVar::FromPPVar(expected); 82 StringVar* actual_var = StringVar::FromPPVar(actual); 83 DCHECK(expected_var && actual_var); 84 if (expected_var->value() != actual_var->value()) { 85 LOG(ERROR) << "expected: " << expected_var->value() << " actual: " << 86 actual_var->value(); 87 return false; 88 } 89 return true; 90 } 91 case PP_VARTYPE_ARRAY_BUFFER: { 92 ArrayBufferVar* expected_var = ArrayBufferVar::FromPPVar(expected); 93 ArrayBufferVar* actual_var = ArrayBufferVar::FromPPVar(actual); 94 DCHECK(expected_var && actual_var); 95 if (expected_var->ByteLength() != actual_var->ByteLength()) { 96 LOG(ERROR) << "expected: " << expected_var->ByteLength() << 97 " actual: " << actual_var->ByteLength(); 98 return false; 99 } 100 if (memcmp(expected_var->Map(), actual_var->Map(), 101 expected_var->ByteLength()) != 0) { 102 LOG(ERROR) << "expected array buffer does not match actual."; 103 return false; 104 } 105 return true; 106 } 107 case PP_VARTYPE_ARRAY: { 108 ArrayVar* expected_var = ArrayVar::FromPPVar(expected); 109 ArrayVar* actual_var = ArrayVar::FromPPVar(actual); 110 DCHECK(expected_var && actual_var); 111 if (expected_var->elements().size() != actual_var->elements().size()) { 112 LOG(ERROR) << "expected: " << expected_var->elements().size() << 113 " actual: " << actual_var->elements().size(); 114 return false; 115 } 116 for (size_t i = 0; i < expected_var->elements().size(); ++i) { 117 if (!Equals(expected_var->elements()[i].get(), 118 actual_var->elements()[i].get(), 119 visited_map)) { 120 return false; 121 } 122 } 123 return true; 124 } 125 case PP_VARTYPE_DICTIONARY: { 126 DictionaryVar* expected_var = DictionaryVar::FromPPVar(expected); 127 DictionaryVar* actual_var = DictionaryVar::FromPPVar(actual); 128 DCHECK(expected_var && actual_var); 129 if (expected_var->key_value_map().size() != 130 actual_var->key_value_map().size()) { 131 LOG(ERROR) << "expected: " << expected_var->key_value_map().size() << 132 " actual: " << actual_var->key_value_map().size(); 133 return false; 134 } 135 DictionaryVar::KeyValueMap::const_iterator expected_iter = 136 expected_var->key_value_map().begin(); 137 DictionaryVar::KeyValueMap::const_iterator actual_iter = 138 actual_var->key_value_map().begin(); 139 for ( ; expected_iter != expected_var->key_value_map().end(); 140 ++expected_iter, ++actual_iter) { 141 if (expected_iter->first != actual_iter->first) { 142 LOG(ERROR) << "expected: " << expected_iter->first << 143 " actual: " << actual_iter->first; 144 return false; 145 } 146 if (!Equals(expected_iter->second.get(), 147 actual_iter->second.get(), 148 visited_map)) { 149 return false; 150 } 151 } 152 return true; 153 } 154 } 155 NOTREACHED(); 156 return false; 157 } 158 159 } // namespace 160 161 bool TestEqual(const PP_Var& expected, const PP_Var& actual) { 162 base::hash_map<int64_t, int64_t> visited_map; 163 return Equals(expected, actual, &visited_map); 164 } 165 166 } // namespace ppapi 167