tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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)