/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ds/TraceableFifo.h" #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetProperty #include "js/RootingAPI.h" #include "jsapi-tests/tests.h" using namespace js; BEGIN_TEST(testTraceableFifoValueBasic) { JS::Rooted> fifo(cx, TraceableFifo(cx)); // Test empty state CHECK(fifo.empty()); CHECK(fifo.length() == 0); // Test pushBack with various JS::Value types CHECK(fifo.pushBack(JS::UndefinedValue())); CHECK(fifo.pushBack(JS::NullValue())); CHECK(fifo.pushBack(JS::Int32Value(42))); CHECK(fifo.pushBack(JS::BooleanValue(true))); CHECK(fifo.pushBack(JS::DoubleValue(3.14))); CHECK(!fifo.empty()); CHECK(fifo.length() == 5); // Test FIFO behavior - first in, first out CHECK(fifo.front().isUndefined()); fifo.popFront(); CHECK(fifo.front().isNull()); fifo.popFront(); CHECK(fifo.front().isInt32() && fifo.front().toInt32() == 42); fifo.popFront(); CHECK(fifo.front().isBoolean() && fifo.front().toBoolean() == true); fifo.popFront(); CHECK(fifo.front().isDouble() && fifo.front().toDouble() == 3.14); fifo.popFront(); CHECK(fifo.empty()); CHECK(fifo.length() == 0); return true; } END_TEST(testTraceableFifoValueBasic) BEGIN_TEST(testTraceableFifoValueGCSurvival) { JS::Rooted> fifo(cx, TraceableFifo(cx)); // Create objects and add them to fifo const size_t numObjects = 15; for (size_t i = 0; i < numObjects; ++i) { JS::RootedObject obj(cx, JS_NewPlainObject(cx)); CHECK(obj); // Add a property to make objects identifiable JS::RootedValue indexVal(cx, JS::NumberValue(static_cast(i))); CHECK(JS_DefineProperty(cx, obj, "testIndex", indexVal, 0)); JS::RootedValue objVal(cx, JS::ObjectValue(*obj)); CHECK(fifo.pushBack(objVal)); } CHECK(fifo.length() == numObjects); // Trigger multiple GC cycles to ensure objects are properly traced JS_GC(cx); JS_GC(cx); JS_GC(cx); // Verify all objects survived and have correct properties for (size_t i = 0; i < numObjects; ++i) { CHECK(!fifo.empty()); CHECK(fifo.front().isObject()); JS::RootedObject obj(cx, &fifo.front().toObject()); CHECK(obj); JS::RootedValue indexVal(cx); CHECK(JS_GetProperty(cx, obj, "testIndex", &indexVal)); CHECK(indexVal.isNumber() && indexVal.toNumber() == static_cast(i)); fifo.popFront(); } CHECK(fifo.empty()); return true; } END_TEST(testTraceableFifoValueGCSurvival) BEGIN_TEST(testTraceableFifoValueStrings) { JS::Rooted> fifo(cx, TraceableFifo(cx)); // Test with various string types const char* testStrings[] = {"hello", "world", "TraceableFifo", "JavaScript", "garbage collection", "SpiderMonkey", "test string with spaces"}; // Add strings to fifo for (const char* str : testStrings) { JS::RootedString jsStr(cx, JS_NewStringCopyZ(cx, str)); CHECK(jsStr); JS::RootedValue strVal(cx, JS::StringValue(jsStr)); CHECK(fifo.pushBack(strVal)); } CHECK(fifo.length() == std::size(testStrings)); // Trigger GC to ensure strings survive JS_GC(cx); // Verify strings in FIFO order for (const char* expected : testStrings) { CHECK(!fifo.empty()); CHECK(fifo.front().isString()); JS::RootedString str(cx, fifo.front().toString()); CHECK(str); bool match = false; CHECK(JS_StringEqualsAscii(cx, str, expected, strlen(expected), &match)); CHECK(match); fifo.popFront(); } CHECK(fifo.empty()); return true; } END_TEST(testTraceableFifoValueStrings) BEGIN_TEST(testTraceableFifoValueMixed) { JS::Rooted> fifo(cx, TraceableFifo(cx)); // Mix different value types CHECK(fifo.pushBack(JS::Int32Value(100))); JS::RootedString str(cx, JS_NewStringCopyZ(cx, "mixed")); CHECK(str); CHECK(fifo.pushBack(JS::StringValue(str))); JS::RootedObject obj(cx, JS_NewPlainObject(cx)); CHECK(obj); CHECK(fifo.pushBack(JS::ObjectValue(*obj))); CHECK(fifo.pushBack(JS::BooleanValue(false))); CHECK(fifo.pushBack(JS::UndefinedValue())); CHECK(fifo.length() == 5); // Force GC between operations JS_GC(cx); // Verify mixed types maintain order and survive GC CHECK(fifo.front().isInt32() && fifo.front().toInt32() == 100); fifo.popFront(); CHECK(fifo.front().isString()); JS::RootedString retrievedStr(cx, fifo.front().toString()); bool match = false; CHECK(JS_StringEqualsAscii(cx, retrievedStr, "mixed", 5, &match)); CHECK(match); fifo.popFront(); CHECK(fifo.front().isObject()); CHECK(&fifo.front().toObject() == obj); fifo.popFront(); CHECK(fifo.front().isBoolean() && !fifo.front().toBoolean()); fifo.popFront(); CHECK(fifo.front().isUndefined()); fifo.popFront(); CHECK(fifo.empty()); return true; } END_TEST(testTraceableFifoValueMixed) BEGIN_TEST(testTraceableFifoValueEmplaceBack) { JS::Rooted> fifo(cx, TraceableFifo(cx)); // Test emplaceBack with different value construction CHECK(fifo.emplaceBack(JS::UndefinedValue())); CHECK(fifo.emplaceBack(JS::Int32Value(999))); CHECK(fifo.emplaceBack(JS::DoubleValue(2.718))); CHECK(fifo.length() == 3); // Verify emplaced values CHECK(fifo.front().isUndefined()); fifo.popFront(); CHECK(fifo.front().isInt32() && fifo.front().toInt32() == 999); fifo.popFront(); CHECK(fifo.front().isDouble() && fifo.front().toDouble() == 2.718); fifo.popFront(); CHECK(fifo.empty()); return true; } END_TEST(testTraceableFifoValueEmplaceBack) BEGIN_TEST(testTraceableFifoValueClear) { JS::Rooted> fifo(cx, TraceableFifo(cx)); // Fill with many values for (int i = 0; i < 30; ++i) { if (i % 3 == 0) { CHECK(fifo.pushBack(JS::Int32Value(i))); } else if (i % 3 == 1) { JS::RootedObject obj(cx, JS_NewPlainObject(cx)); CHECK(obj); CHECK(fifo.pushBack(JS::ObjectValue(*obj))); } else { JS::RootedString str(cx, JS_NewStringCopyZ(cx, "test")); CHECK(str); CHECK(fifo.pushBack(JS::StringValue(str))); } } CHECK(fifo.length() == 30); CHECK(!fifo.empty()); // Clear the fifo fifo.clear(); CHECK(fifo.length() == 0); CHECK(fifo.empty()); // Verify we can still use it after clear CHECK(fifo.pushBack(JS::Int32Value(42))); CHECK(fifo.length() == 1); CHECK(fifo.front().isInt32() && fifo.front().toInt32() == 42); return true; } END_TEST(testTraceableFifoValueClear) BEGIN_TEST(testTraceableFifoValueLargeScale) { JS::Rooted> fifo(cx, TraceableFifo(cx)); // Large scale test with GC pressure const size_t largeCount = 100; for (size_t i = 0; i < largeCount; ++i) { // Create different types of values switch (i % 4) { case 0: { CHECK(fifo.pushBack(JS::Int32Value(static_cast(i)))); break; } case 1: { JS::RootedObject obj(cx, JS_NewPlainObject(cx)); CHECK(obj); CHECK(fifo.pushBack(JS::ObjectValue(*obj))); break; } case 2: { JS::RootedString str(cx, JS_NewStringCopyZ(cx, "large")); CHECK(str); CHECK(fifo.pushBack(JS::StringValue(str))); break; } case 3: { CHECK(fifo.pushBack(JS::DoubleValue(static_cast(i) + 0.5))); break; } } // Periodic GC to test tracing under pressure if (i % 25 == 0) { JS_GC(cx); } } CHECK(fifo.length() == largeCount); // Final GC sweep JS_GC(cx); JS_GC(cx); // Verify all values are intact for (size_t i = 0; i < largeCount; ++i) { CHECK(!fifo.empty()); switch (i % 4) { case 0: CHECK(fifo.front().isInt32()); CHECK(fifo.front().toInt32() == static_cast(i)); break; case 1: CHECK(fifo.front().isObject()); break; case 2: CHECK(fifo.front().isString()); break; case 3: CHECK(fifo.front().isDouble()); CHECK(fifo.front().toDouble() == static_cast(i) + 0.5); break; } fifo.popFront(); } CHECK(fifo.empty()); return true; } END_TEST(testTraceableFifoValueLargeScale)