testTraceableFifoValue.cpp (8788B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 */ 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 #include "ds/TraceableFifo.h" 9 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetProperty 10 #include "js/RootingAPI.h" 11 12 #include "jsapi-tests/tests.h" 13 14 using namespace js; 15 16 BEGIN_TEST(testTraceableFifoValueBasic) { 17 JS::Rooted<TraceableFifo<JS::Value>> fifo(cx, TraceableFifo<JS::Value>(cx)); 18 19 // Test empty state 20 CHECK(fifo.empty()); 21 CHECK(fifo.length() == 0); 22 23 // Test pushBack with various JS::Value types 24 CHECK(fifo.pushBack(JS::UndefinedValue())); 25 CHECK(fifo.pushBack(JS::NullValue())); 26 CHECK(fifo.pushBack(JS::Int32Value(42))); 27 CHECK(fifo.pushBack(JS::BooleanValue(true))); 28 CHECK(fifo.pushBack(JS::DoubleValue(3.14))); 29 30 CHECK(!fifo.empty()); 31 CHECK(fifo.length() == 5); 32 33 // Test FIFO behavior - first in, first out 34 CHECK(fifo.front().isUndefined()); 35 fifo.popFront(); 36 37 CHECK(fifo.front().isNull()); 38 fifo.popFront(); 39 40 CHECK(fifo.front().isInt32() && fifo.front().toInt32() == 42); 41 fifo.popFront(); 42 43 CHECK(fifo.front().isBoolean() && fifo.front().toBoolean() == true); 44 fifo.popFront(); 45 46 CHECK(fifo.front().isDouble() && fifo.front().toDouble() == 3.14); 47 fifo.popFront(); 48 49 CHECK(fifo.empty()); 50 CHECK(fifo.length() == 0); 51 52 return true; 53 } 54 END_TEST(testTraceableFifoValueBasic) 55 56 BEGIN_TEST(testTraceableFifoValueGCSurvival) { 57 JS::Rooted<TraceableFifo<JS::Value>> fifo(cx, TraceableFifo<JS::Value>(cx)); 58 59 // Create objects and add them to fifo 60 const size_t numObjects = 15; 61 for (size_t i = 0; i < numObjects; ++i) { 62 JS::RootedObject obj(cx, JS_NewPlainObject(cx)); 63 CHECK(obj); 64 65 // Add a property to make objects identifiable 66 JS::RootedValue indexVal(cx, JS::NumberValue(static_cast<double>(i))); 67 CHECK(JS_DefineProperty(cx, obj, "testIndex", indexVal, 0)); 68 69 JS::RootedValue objVal(cx, JS::ObjectValue(*obj)); 70 CHECK(fifo.pushBack(objVal)); 71 } 72 73 CHECK(fifo.length() == numObjects); 74 75 // Trigger multiple GC cycles to ensure objects are properly traced 76 JS_GC(cx); 77 JS_GC(cx); 78 JS_GC(cx); 79 80 // Verify all objects survived and have correct properties 81 for (size_t i = 0; i < numObjects; ++i) { 82 CHECK(!fifo.empty()); 83 CHECK(fifo.front().isObject()); 84 85 JS::RootedObject obj(cx, &fifo.front().toObject()); 86 CHECK(obj); 87 88 JS::RootedValue indexVal(cx); 89 CHECK(JS_GetProperty(cx, obj, "testIndex", &indexVal)); 90 CHECK(indexVal.isNumber() && indexVal.toNumber() == static_cast<double>(i)); 91 92 fifo.popFront(); 93 } 94 95 CHECK(fifo.empty()); 96 97 return true; 98 } 99 END_TEST(testTraceableFifoValueGCSurvival) 100 101 BEGIN_TEST(testTraceableFifoValueStrings) { 102 JS::Rooted<TraceableFifo<JS::Value>> fifo(cx, TraceableFifo<JS::Value>(cx)); 103 104 // Test with various string types 105 const char* testStrings[] = {"hello", 106 "world", 107 "TraceableFifo", 108 "JavaScript", 109 "garbage collection", 110 "SpiderMonkey", 111 "test string with spaces"}; 112 113 // Add strings to fifo 114 for (const char* str : testStrings) { 115 JS::RootedString jsStr(cx, JS_NewStringCopyZ(cx, str)); 116 CHECK(jsStr); 117 JS::RootedValue strVal(cx, JS::StringValue(jsStr)); 118 CHECK(fifo.pushBack(strVal)); 119 } 120 121 CHECK(fifo.length() == std::size(testStrings)); 122 123 // Trigger GC to ensure strings survive 124 JS_GC(cx); 125 126 // Verify strings in FIFO order 127 for (const char* expected : testStrings) { 128 CHECK(!fifo.empty()); 129 CHECK(fifo.front().isString()); 130 131 JS::RootedString str(cx, fifo.front().toString()); 132 CHECK(str); 133 134 bool match = false; 135 CHECK(JS_StringEqualsAscii(cx, str, expected, strlen(expected), &match)); 136 CHECK(match); 137 138 fifo.popFront(); 139 } 140 141 CHECK(fifo.empty()); 142 143 return true; 144 } 145 END_TEST(testTraceableFifoValueStrings) 146 147 BEGIN_TEST(testTraceableFifoValueMixed) { 148 JS::Rooted<TraceableFifo<JS::Value>> fifo(cx, TraceableFifo<JS::Value>(cx)); 149 150 // Mix different value types 151 CHECK(fifo.pushBack(JS::Int32Value(100))); 152 153 JS::RootedString str(cx, JS_NewStringCopyZ(cx, "mixed")); 154 CHECK(str); 155 CHECK(fifo.pushBack(JS::StringValue(str))); 156 157 JS::RootedObject obj(cx, JS_NewPlainObject(cx)); 158 CHECK(obj); 159 CHECK(fifo.pushBack(JS::ObjectValue(*obj))); 160 161 CHECK(fifo.pushBack(JS::BooleanValue(false))); 162 CHECK(fifo.pushBack(JS::UndefinedValue())); 163 164 CHECK(fifo.length() == 5); 165 166 // Force GC between operations 167 JS_GC(cx); 168 169 // Verify mixed types maintain order and survive GC 170 CHECK(fifo.front().isInt32() && fifo.front().toInt32() == 100); 171 fifo.popFront(); 172 173 CHECK(fifo.front().isString()); 174 JS::RootedString retrievedStr(cx, fifo.front().toString()); 175 bool match = false; 176 CHECK(JS_StringEqualsAscii(cx, retrievedStr, "mixed", 5, &match)); 177 CHECK(match); 178 fifo.popFront(); 179 180 CHECK(fifo.front().isObject()); 181 CHECK(&fifo.front().toObject() == obj); 182 fifo.popFront(); 183 184 CHECK(fifo.front().isBoolean() && !fifo.front().toBoolean()); 185 fifo.popFront(); 186 187 CHECK(fifo.front().isUndefined()); 188 fifo.popFront(); 189 190 CHECK(fifo.empty()); 191 192 return true; 193 } 194 END_TEST(testTraceableFifoValueMixed) 195 196 BEGIN_TEST(testTraceableFifoValueEmplaceBack) { 197 JS::Rooted<TraceableFifo<JS::Value>> fifo(cx, TraceableFifo<JS::Value>(cx)); 198 199 // Test emplaceBack with different value construction 200 CHECK(fifo.emplaceBack(JS::UndefinedValue())); 201 CHECK(fifo.emplaceBack(JS::Int32Value(999))); 202 CHECK(fifo.emplaceBack(JS::DoubleValue(2.718))); 203 204 CHECK(fifo.length() == 3); 205 206 // Verify emplaced values 207 CHECK(fifo.front().isUndefined()); 208 fifo.popFront(); 209 210 CHECK(fifo.front().isInt32() && fifo.front().toInt32() == 999); 211 fifo.popFront(); 212 213 CHECK(fifo.front().isDouble() && fifo.front().toDouble() == 2.718); 214 fifo.popFront(); 215 216 CHECK(fifo.empty()); 217 218 return true; 219 } 220 END_TEST(testTraceableFifoValueEmplaceBack) 221 222 BEGIN_TEST(testTraceableFifoValueClear) { 223 JS::Rooted<TraceableFifo<JS::Value>> fifo(cx, TraceableFifo<JS::Value>(cx)); 224 225 // Fill with many values 226 for (int i = 0; i < 30; ++i) { 227 if (i % 3 == 0) { 228 CHECK(fifo.pushBack(JS::Int32Value(i))); 229 } else if (i % 3 == 1) { 230 JS::RootedObject obj(cx, JS_NewPlainObject(cx)); 231 CHECK(obj); 232 CHECK(fifo.pushBack(JS::ObjectValue(*obj))); 233 } else { 234 JS::RootedString str(cx, JS_NewStringCopyZ(cx, "test")); 235 CHECK(str); 236 CHECK(fifo.pushBack(JS::StringValue(str))); 237 } 238 } 239 240 CHECK(fifo.length() == 30); 241 CHECK(!fifo.empty()); 242 243 // Clear the fifo 244 fifo.clear(); 245 246 CHECK(fifo.length() == 0); 247 CHECK(fifo.empty()); 248 249 // Verify we can still use it after clear 250 CHECK(fifo.pushBack(JS::Int32Value(42))); 251 CHECK(fifo.length() == 1); 252 CHECK(fifo.front().isInt32() && fifo.front().toInt32() == 42); 253 254 return true; 255 } 256 END_TEST(testTraceableFifoValueClear) 257 258 BEGIN_TEST(testTraceableFifoValueLargeScale) { 259 JS::Rooted<TraceableFifo<JS::Value>> fifo(cx, TraceableFifo<JS::Value>(cx)); 260 261 // Large scale test with GC pressure 262 const size_t largeCount = 100; 263 264 for (size_t i = 0; i < largeCount; ++i) { 265 // Create different types of values 266 switch (i % 4) { 267 case 0: { 268 CHECK(fifo.pushBack(JS::Int32Value(static_cast<int32_t>(i)))); 269 break; 270 } 271 case 1: { 272 JS::RootedObject obj(cx, JS_NewPlainObject(cx)); 273 CHECK(obj); 274 CHECK(fifo.pushBack(JS::ObjectValue(*obj))); 275 break; 276 } 277 case 2: { 278 JS::RootedString str(cx, JS_NewStringCopyZ(cx, "large")); 279 CHECK(str); 280 CHECK(fifo.pushBack(JS::StringValue(str))); 281 break; 282 } 283 case 3: { 284 CHECK(fifo.pushBack(JS::DoubleValue(static_cast<double>(i) + 0.5))); 285 break; 286 } 287 } 288 289 // Periodic GC to test tracing under pressure 290 if (i % 25 == 0) { 291 JS_GC(cx); 292 } 293 } 294 295 CHECK(fifo.length() == largeCount); 296 297 // Final GC sweep 298 JS_GC(cx); 299 JS_GC(cx); 300 301 // Verify all values are intact 302 for (size_t i = 0; i < largeCount; ++i) { 303 CHECK(!fifo.empty()); 304 305 switch (i % 4) { 306 case 0: 307 CHECK(fifo.front().isInt32()); 308 CHECK(fifo.front().toInt32() == static_cast<int32_t>(i)); 309 break; 310 case 1: 311 CHECK(fifo.front().isObject()); 312 break; 313 case 2: 314 CHECK(fifo.front().isString()); 315 break; 316 case 3: 317 CHECK(fifo.front().isDouble()); 318 CHECK(fifo.front().toDouble() == static_cast<double>(i) + 0.5); 319 break; 320 } 321 322 fifo.popFront(); 323 } 324 325 CHECK(fifo.empty()); 326 327 return true; 328 } 329 END_TEST(testTraceableFifoValueLargeScale)