Global Metrics

path: .metrics.loc.ploc
old: 46.0
new: 195.0

path: .metrics.loc.lloc
old: 1.0
new: 80.0

path: .metrics.loc.sloc
old: 73.0
new: 304.0

path: .metrics.loc.cloc
old: 16.0
new: 55.0

path: .metrics.loc.blank
old: 11.0
new: 54.0

path: .metrics.nom.total
old: 1.0
new: 19.0

path: .metrics.nom.functions
old: 1.0
new: 19.0

path: .metrics.cyclomatic.sum
old: 14.0
new: 36.0

path: .metrics.cyclomatic.average
old: 1.0769230769230769
new: 1.565217391304348

path: .metrics.mi.mi_original
old: 62.08127803123237
new: 23.486761122965135

path: .metrics.mi.mi_visual_studio
old: 36.30484095393706
new: 13.73494802512581

path: .metrics.mi.mi_sei
old: 48.45613286110868
new: -7.53688940764081

path: .metrics.halstead.bugs
old: 0.14116263733518505
new: 1.7939110076783071

path: .metrics.halstead.effort
old: 8714.88233045279
new: 394805.5273364016

path: .metrics.halstead.N1
old: 91.0
new: 607.0

path: .metrics.halstead.level
old: 0.12093023255813952
new: 0.019815994338287332

path: .metrics.halstead.n1
old: 10.0
new: 27.0

path: .metrics.halstead.vocabulary
old: 62.0
new: 153.0

path: .metrics.halstead.N2
old: 86.0
new: 471.0

path: .metrics.halstead.estimated_program_length
old: 329.6421462922104
new: 1007.5192329194032

path: .metrics.halstead.n2
old: 52.0
new: 126.0

path: .metrics.halstead.time
old: 484.1601294695995
new: 21933.64040757787

path: .metrics.halstead.difficulty
old: 8.26923076923077
new: 50.464285714285715

path: .metrics.halstead.purity_ratio
old: 1.8623850073006236
new: 0.9346189544706894

path: .metrics.halstead.length
old: 177.0
new: 1078.0

path: .metrics.halstead.volume
old: 1053.8927469384769
new: 7823.464094422679

path: .metrics.nexits.average
old: 1.0
new: 0.21052631578947367

path: .metrics.nexits.sum
old: 1.0
new: 4.0

path: .metrics.nargs.sum
old: 3.0
new: 20.0

path: .metrics.nargs.average
old: 3.0
new: 1.0526315789473684

path: .metrics.cognitive.average
old: 0.0
new: 0.8947368421052632

path: .metrics.cognitive.sum
old: 0.0
new: 17.0

Spaces Data

Minimal test - lines (114, 159)

path: .spaces[2].metrics.nargs.sum
old: 0.0
new: 1.0

path: .spaces[2].metrics.nargs.average
old: null
new: 0.16666666666666666

path: .spaces[2].metrics.loc.ploc
old: 1.0
new: 37.0

path: .spaces[2].metrics.loc.blank
old: 0.0
new: 6.0

path: .spaces[2].metrics.loc.cloc
old: 0.0
new: 3.0

path: .spaces[2].metrics.loc.lloc
old: 0.0
new: 16.0

path: .spaces[2].metrics.loc.sloc
old: 1.0
new: 46.0

path: .spaces[2].metrics.cyclomatic.sum
old: 1.0
new: 7.0

path: .spaces[2].metrics.nexits.average
old: null
new: 0.16666666666666666

path: .spaces[2].metrics.nexits.sum
old: 0.0
new: 1.0

path: .spaces[2].metrics.halstead.n1
old: 0.0
new: 16.0

path: .spaces[2].metrics.halstead.vocabulary
old: 1.0
new: 57.0

path: .spaces[2].metrics.halstead.N1
old: 0.0
new: 111.0

path: .spaces[2].metrics.halstead.n2
old: 1.0
new: 41.0

path: .spaces[2].metrics.halstead.difficulty
old: 0.0
new: 15.414634146341465

path: .spaces[2].metrics.halstead.effort
old: 0.0
new: 17083.25446099761

path: .spaces[2].metrics.halstead.level
old: null
new: 0.06487341772151899

path: .spaces[2].metrics.halstead.purity_ratio
old: null
new: 1.4929454325754812

path: .spaces[2].metrics.halstead.volume
old: 0.0
new: 1108.2491026913008

path: .spaces[2].metrics.halstead.estimated_program_length
old: null
new: 283.65963218934144

path: .spaces[2].metrics.halstead.time
old: 0.0
new: 949.069692277645

path: .spaces[2].metrics.halstead.N2
old: 1.0
new: 79.0

path: .spaces[2].metrics.halstead.length
old: 1.0
new: 190.0

path: .spaces[2].metrics.halstead.bugs
old: 0.0
new: 0.221101905390142

path: .spaces[2].metrics.mi.mi_visual_studio
old: null
new: 41.46854896156351

path: .spaces[2].metrics.mi.mi_original
old: null
new: 70.91121872427361

path: .spaces[2].metrics.mi.mi_sei
old: null
new: 46.58455146213936

path: .spaces[2].metrics.cognitive.average
old: null
new: 0.0

path: .spaces[2].metrics.nom.functions
old: 0.0
new: 6.0

path: .spaces[2].metrics.nom.total
old: 0.0
new: 6.0

Code

class ThreadMetrics : public ::testing::Test {
 public:
  explicit ThreadMetrics() = default;

 protected:
  virtual void SetUp() {
    // building the DocGroup structure
    RefPtr group =
        dom::BrowsingContextGroup::Create();
    mDocGroup = group->AddDocument("key"_ns, nullptr);
    mDocGroup2 = group->AddDocument("key2"_ns, nullptr);
    mCounter = mDocGroup->GetPerformanceCounter();
    mCounter2 = mDocGroup2->GetPerformanceCounter();
    mThreadMgr = do_GetService("@mozilla.org/thread-manager;1");
    mOther = DispatchCategory(TaskCategory::Other).GetValue();
    mDispatchCount = (uint32_t)TaskCategory::Other + 1;
  }

  virtual void TearDown() {
    // and remove the document from the doc group (actually, a nullptr)
    mDocGroup->RemoveDocument(nullptr);
    mDocGroup2->RemoveDocument(nullptr);
    mDocGroup = nullptr;
    mDocGroup2 = nullptr;
    ProcessAllEvents();
  }

  // this is used to get rid of transient events
  void initScheduler() { ProcessAllEvents(); }

  nsresult Dispatch(nsIRunnable* aRunnable) {
    ProcessAllEvents();
    return TimedRunnable::DispatchWithDocgroup(aRunnable, mDocGroup);
  }

  void ProcessAllEvents() { mThreadMgr->SpinEventLoopUntilEmpty(); }

  uint32_t mOther;
  bool mOldPref;
  RefPtr mDocGroup;
  RefPtr mDocGroup2;
  RefPtr mCounter;
  RefPtr mCounter2;
  nsCOMPtr mThreadMgr;
  uint32_t mDispatchCount;
};

Minimal test - lines (29, 40)

path: .spaces[0].metrics.loc.ploc
old: 4.0
new: 10.0

path: .spaces[0].metrics.loc.cloc
old: 1.0
new: 0.0

path: .spaces[0].metrics.loc.blank
old: 0.0
new: 2.0

path: .spaces[0].metrics.loc.sloc
old: 5.0
new: 12.0

path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 3.0

path: .spaces[0].metrics.nargs.average
old: null
new: 1.5

path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 4.0

path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 1.3333333333333333

path: .spaces[0].metrics.halstead.effort
old: 44.91767875292167
new: 3722.9895505855793

path: .spaces[0].metrics.halstead.volume
old: 22.458839376460833
new: 265.9278250418271

path: .spaces[0].metrics.halstead.purity_ratio
old: 1.5943609377704335
new: 1.483432758919099

path: .spaces[0].metrics.halstead.vocabulary
old: 7.0
new: 24.0

path: .spaces[0].metrics.halstead.n2
old: 3.0
new: 12.0

path: .spaces[0].metrics.halstead.level
old: 0.5
new: 0.07142857142857142

path: .spaces[0].metrics.halstead.N1
old: 5.0
new: 30.0

path: .spaces[0].metrics.halstead.bugs
old: 0.00421201861424495
new: 0.08007063821493973

path: .spaces[0].metrics.halstead.difficulty
old: 2.0
new: 14.0

path: .spaces[0].metrics.halstead.estimated_program_length
old: 12.754887502163468
new: 86.03910001730775

path: .spaces[0].metrics.halstead.length
old: 8.0
new: 58.0

path: .spaces[0].metrics.halstead.n1
old: 4.0
new: 12.0

path: .spaces[0].metrics.halstead.N2
old: 3.0
new: 28.0

path: .spaces[0].metrics.halstead.time
old: 2.495426597384537
new: 206.83275281030996

path: .spaces[0].metrics.nexits.average
old: null
new: 0.0

path: .spaces[0].metrics.cognitive.average
old: null
new: 0.0

path: .spaces[0].metrics.mi.mi_visual_studio
old: 75.15575883029284
new: 58.94253953114288

path: .spaces[0].metrics.mi.mi_original
old: 128.51634759980075
new: 100.79174259825432

path: .spaces[0].metrics.mi.mi_sei
old: 141.7463557556368
new: 70.11817465466349

path: .spaces[0].metrics.nom.functions
old: 0.0
new: 2.0

path: .spaces[0].metrics.nom.total
old: 0.0
new: 2.0

Code

struct RunnableDescriptor {
  MOZ_IMPLICIT RunnableDescriptor(nsIRunnable* aRunnable,
                                  DocGroup* aDocGroup = nullptr)
      : mRunnable(aRunnable), mDocGroup(aDocGroup) {}

  RunnableDescriptor(RunnableDescriptor&& aDescriptor)
      : mRunnable(std::move(aDescriptor.mRunnable)),
        mDocGroup(std::move(aDescriptor.mDocGroup)) {}

  nsCOMPtr mRunnable;
  RefPtr mDocGroup;
};

Minimal test - lines (222, 258)

path: .spaces[5].metrics.loc.ploc
old: 1.0
new: 22.0

path: .spaces[5].metrics.loc.lloc
old: 0.0
new: 12.0

path: .spaces[5].metrics.loc.cloc
old: 0.0
new: 7.0

path: .spaces[5].metrics.loc.blank
old: 0.0
new: 8.0

path: .spaces[5].metrics.loc.sloc
old: 1.0
new: 37.0

path: .spaces[5].metrics.nargs.sum
old: 0.0
new: 2.0

path: .spaces[5].metrics.nargs.average
old: null
new: 2.0

path: .spaces[5].metrics.cognitive.average
old: null
new: 3.0

path: .spaces[5].metrics.cognitive.sum
old: 0.0
new: 3.0

path: .spaces[5].metrics.cyclomatic.average
old: 1.0
new: 4.0

path: .spaces[5].metrics.cyclomatic.sum
old: 1.0
new: 4.0

path: .spaces[5].metrics.nexits.average
old: null
new: 0.0

path: .spaces[5].metrics.mi.mi_sei
old: null
new: 66.49157998104721

path: .spaces[5].metrics.mi.mi_visual_studio
old: null
new: 44.825856994170834

path: .spaces[5].metrics.mi.mi_original
old: null
new: 76.65221546003212

path: .spaces[5].metrics.halstead.purity_ratio
old: null
new: 1.8727861856939352

path: .spaces[5].metrics.halstead.level
old: null
new: 0.06630824372759857

path: .spaces[5].metrics.halstead.N2
old: 1.0
new: 62.0

path: .spaces[5].metrics.halstead.n2
old: 1.0
new: 37.0

path: .spaces[5].metrics.halstead.bugs
old: 0.0
new: 0.17923015541668944

path: .spaces[5].metrics.halstead.difficulty
old: 0.0
new: 15.08108108108108

path: .spaces[5].metrics.halstead.time
old: 0.0
new: 692.6693948663465

path: .spaces[5].metrics.halstead.N1
old: 0.0
new: 81.0

path: .spaces[5].metrics.halstead.length
old: 1.0
new: 143.0

path: .spaces[5].metrics.halstead.effort
old: 0.0
new: 12468.049107594235

path: .spaces[5].metrics.halstead.n1
old: 0.0
new: 18.0

path: .spaces[5].metrics.halstead.estimated_program_length
old: null
new: 267.8084245542328

path: .spaces[5].metrics.halstead.volume
old: 0.0
new: 826.7344390340264

path: .spaces[5].metrics.halstead.vocabulary
old: 1.0
new: 55.0

path: .spaces[5].metrics.nom.total
old: 0.0
new: 1.0

path: .spaces[5].metrics.nom.functions
old: 0.0
new: 1.0

Code

TEST_F(ThreadMetrics, CollectMultipleRecursiveMetrics) {
  nsresult rv;

  initScheduler();

  // Dispatching a runnable that will last for +75ms
  // and run another two recursively that last for 400ms each.
  RefPtr runnable = new TimedRunnable(25, 25);
  for (auto i : {1, 2}) {
    Unused << i;
    nsCOMPtr nested = new TimedRunnable(400, 0);
    runnable->AddNestedRunnable({nested});
  }

  rv = Dispatch(runnable);
  ASSERT_TRUE(NS_SUCCEEDED(rv));

  // Flush the queue
  ProcessAllEvents();

  // let's look at the counters
  ASSERT_EQ(mCounter->GetDispatchCounter()[mOther], 1u);

  // other counters should stay empty
  for (uint32_t i = 0; i < mDispatchCount; i++) {
    if (i != mOther) {
      ASSERT_EQ(mCounter->GetDispatchCounter()[i], 0u);
    }
  }

  // did we get incremented in the docgroup ?
  uint64_t duration = mCounter->GetExecutionDuration();
  ASSERT_GE(duration, runnable->TotalSlept());

  // let's make sure we don't count the time spent in recursive calls
  ASSERT_LT(duration, runnable->TotalSlept() + 200000u);
}

Minimal test - lines (161, 186)

path: .spaces[3].metrics.loc.blank
old: 0.0
new: 5.0

path: .spaces[3].metrics.loc.ploc
old: 1.0
new: 16.0

path: .spaces[3].metrics.loc.sloc
old: 1.0
new: 26.0

path: .spaces[3].metrics.loc.lloc
old: 0.0
new: 9.0

path: .spaces[3].metrics.loc.cloc
old: 0.0
new: 5.0

path: .spaces[3].metrics.mi.mi_original
old: null
new: 84.99972025325582

path: .spaces[3].metrics.mi.mi_visual_studio
old: null
new: 49.70743874459404

path: .spaces[3].metrics.mi.mi_sei
old: null
new: 78.64828687785112

path: .spaces[3].metrics.nom.total
old: 0.0
new: 1.0

path: .spaces[3].metrics.nom.functions
old: 0.0
new: 1.0

path: .spaces[3].metrics.nexits.average
old: null
new: 0.0

path: .spaces[3].metrics.halstead.n2
old: 1.0
new: 28.0

path: .spaces[3].metrics.halstead.difficulty
old: 0.0
new: 11.25

path: .spaces[3].metrics.halstead.estimated_program_length
old: null
new: 193.20929675174068

path: .spaces[3].metrics.halstead.bugs
old: 0.0
new: 0.1083499134576206

path: .spaces[3].metrics.halstead.vocabulary
old: 1.0
new: 43.0

path: .spaces[3].metrics.halstead.length
old: 1.0
new: 96.0

path: .spaces[3].metrics.halstead.effort
old: 0.0
new: 5860.365935078266

path: .spaces[3].metrics.halstead.purity_ratio
old: null
new: 2.0125968411639654

path: .spaces[3].metrics.halstead.time
old: 0.0
new: 325.5758852821259

path: .spaces[3].metrics.halstead.level
old: null
new: 0.08888888888888889

path: .spaces[3].metrics.halstead.volume
old: 0.0
new: 520.9214164514015

path: .spaces[3].metrics.halstead.N1
old: 0.0
new: 54.0

path: .spaces[3].metrics.halstead.N2
old: 1.0
new: 42.0

path: .spaces[3].metrics.halstead.n1
old: 0.0
new: 15.0

path: .spaces[3].metrics.nargs.average
old: null
new: 2.0

path: .spaces[3].metrics.nargs.sum
old: 0.0
new: 2.0

path: .spaces[3].metrics.cyclomatic.average
old: 1.0
new: 3.0

path: .spaces[3].metrics.cyclomatic.sum
old: 1.0
new: 3.0

path: .spaces[3].metrics.cognitive.sum
old: 0.0
new: 3.0

path: .spaces[3].metrics.cognitive.average
old: null
new: 3.0

Code

TEST_F(ThreadMetrics, CollectMetrics) {
  nsresult rv;
  initScheduler();

  // Dispatching a runnable that will last for +50ms
  nsCOMPtr runnable = new TimedRunnable(25, 25);
  rv = Dispatch(runnable);
  ASSERT_TRUE(NS_SUCCEEDED(rv));

  // Flush the queue
  ProcessAllEvents();

  // Let's look at the task category "other" counter
  ASSERT_EQ(mCounter->GetDispatchCounter()[mOther], 1u);

  // other counters should stay empty
  for (uint32_t i = 0; i < mDispatchCount; i++) {
    if (i != mOther) {
      ASSERT_EQ(mCounter->GetDispatchCounter()[i], 0u);
    }
  }

  // Did we get incremented in the docgroup ?
  uint64_t duration = mCounter->GetExecutionDuration();
  ASSERT_GE(duration, 50000u);
}

Minimal test - lines (260, 304)

path: .spaces[6].metrics.halstead.purity_ratio
old: null
new: 1.4716270698930516

path: .spaces[6].metrics.halstead.estimated_program_length
old: null
new: 263.42124551085624

path: .spaces[6].metrics.halstead.level
old: null
new: 0.060126582278481014

path: .spaces[6].metrics.halstead.n2
old: 1.0
new: 38.0

path: .spaces[6].metrics.halstead.time
old: 0.0
new: 951.8112768198084

path: .spaces[6].metrics.halstead.length
old: 1.0
new: 179.0

path: .spaces[6].metrics.halstead.bugs
old: 0.0
new: 0.2215274997758664

path: .spaces[6].metrics.halstead.volume
old: 0.0
new: 1030.1248628872609

path: .spaces[6].metrics.halstead.vocabulary
old: 1.0
new: 54.0

path: .spaces[6].metrics.halstead.difficulty
old: 0.0
new: 16.63157894736842

path: .spaces[6].metrics.halstead.N2
old: 1.0
new: 79.0

path: .spaces[6].metrics.halstead.n1
old: 0.0
new: 16.0

path: .spaces[6].metrics.halstead.effort
old: 0.0
new: 17132.60298275655

path: .spaces[6].metrics.halstead.N1
old: 0.0
new: 100.0

path: .spaces[6].metrics.mi.mi_original
old: null
new: 72.56740410582209

path: .spaces[6].metrics.mi.mi_sei
old: null
new: 65.1650461648317

path: .spaces[6].metrics.mi.mi_visual_studio
old: null
new: 42.43707842445736

path: .spaces[6].metrics.loc.ploc
old: 1.0
new: 24.0

path: .spaces[6].metrics.loc.blank
old: 0.0
new: 9.0

path: .spaces[6].metrics.loc.cloc
old: 0.0
new: 12.0

path: .spaces[6].metrics.loc.lloc
old: 0.0
new: 15.0

path: .spaces[6].metrics.loc.sloc
old: 1.0
new: 45.0

path: .spaces[6].metrics.nargs.sum
old: 0.0
new: 2.0

path: .spaces[6].metrics.nargs.average
old: null
new: 2.0

path: .spaces[6].metrics.cognitive.average
old: null
new: 3.0

path: .spaces[6].metrics.cognitive.sum
old: 0.0
new: 3.0

path: .spaces[6].metrics.nexits.average
old: null
new: 0.0

path: .spaces[6].metrics.nom.total
old: 0.0
new: 1.0

path: .spaces[6].metrics.nom.functions
old: 0.0
new: 1.0

path: .spaces[6].metrics.cyclomatic.average
old: 1.0
new: 3.0

path: .spaces[6].metrics.cyclomatic.sum
old: 1.0
new: 3.0

Code

TEST_F(ThreadMetrics, CollectMultipleRecursiveMetricsWithTwoDocgroups) {
  nsresult rv;

  initScheduler();

  // Dispatching a runnable that will last for +75ms
  // and run another two recursively that last for 400ms each.  The
  // first nested runnable will have a docgroup, but the second will
  // not, to test that the time for first nested event is accounted
  // correctly.
  RefPtr runnable = new TimedRunnable(25, 25);
  RefPtr nested1 = new TimedRunnable(400, 0);
  runnable->AddNestedRunnable({nested1, mDocGroup2});
  nsCOMPtr nested2 = new TimedRunnable(400, 0);
  runnable->AddNestedRunnable({nested2});

  rv = Dispatch(runnable);
  ASSERT_TRUE(NS_SUCCEEDED(rv));

  // Flush the queue
  ProcessAllEvents();

  // let's look at the counters
  ASSERT_EQ(mCounter->GetDispatchCounter()[mOther], 1u);

  // other counters should stay empty
  for (uint32_t i = 0; i < mDispatchCount; i++) {
    if (i != mOther) {
      ASSERT_EQ(mCounter->GetDispatchCounter()[i], 0u);
    }
  }

  uint64_t duration = mCounter2->GetExecutionDuration();
  // Make sure this we incremented the timings for the first nested
  // runnable correctly.
  ASSERT_GE(duration, nested1->TotalSlept());
  ASSERT_LT(duration, nested1->TotalSlept() + 20000u);

  // And now for the outer runnable.
  duration = mCounter->GetExecutionDuration();
  ASSERT_GE(duration, runnable->TotalSlept());

  // let's make sure we don't count the time spent in recursive calls
  ASSERT_LT(duration, runnable->TotalSlept() + 200000u);
}

Minimal test - lines (188, 220)

path: .spaces[4].metrics.nom.functions
old: 0.0
new: 1.0

path: .spaces[4].metrics.nom.total
old: 0.0
new: 1.0

path: .spaces[4].metrics.halstead.vocabulary
old: 13.0
new: 50.0

path: .spaces[4].metrics.halstead.estimated_program_length
old: 36.529325012980806
new: 236.9737366025115

path: .spaces[4].metrics.halstead.length
old: 21.0
new: 130.0

path: .spaces[4].metrics.halstead.n1
old: 4.0
new: 16.0

path: .spaces[4].metrics.halstead.N1
old: 12.0
new: 73.0

path: .spaces[4].metrics.halstead.purity_ratio
old: 1.7394916672848002
new: 1.8228748969423965

path: .spaces[4].metrics.halstead.time
old: 8.634359342329214
new: 546.6794034801401

path: .spaces[4].metrics.halstead.volume
old: 77.70923408096293
new: 733.7013046707142

path: .spaces[4].metrics.halstead.n2
old: 9.0
new: 34.0

path: .spaces[4].metrics.halstead.difficulty
old: 2.0
new: 13.411764705882351

path: .spaces[4].metrics.halstead.N2
old: 9.0
new: 57.0

path: .spaces[4].metrics.halstead.bugs
old: 0.00963563842219266
new: 0.15306723008731535

path: .spaces[4].metrics.halstead.level
old: 0.5
new: 0.07456140350877193

path: .spaces[4].metrics.halstead.effort
old: 155.41846816192586
new: 9840.22926264252

path: .spaces[4].metrics.nargs.sum
old: 0.0
new: 2.0

path: .spaces[4].metrics.nargs.average
old: null
new: 2.0

path: .spaces[4].metrics.cyclomatic.average
old: 1.0
new: 3.0

path: .spaces[4].metrics.mi.mi_original
old: 116.15079030142728
new: 79.35644708382942

path: .spaces[4].metrics.mi.mi_sei
old: 92.17477674940372
new: 71.81619163441891

path: .spaces[4].metrics.mi.mi_visual_studio
old: 67.9244387727645
new: 46.40727899639147

path: .spaces[4].metrics.loc.blank
old: 0.0
new: 7.0

path: .spaces[4].metrics.loc.lloc
old: 0.0
new: 11.0

path: .spaces[4].metrics.loc.ploc
old: 7.0
new: 19.0

path: .spaces[4].metrics.loc.cloc
old: 0.0
new: 7.0

path: .spaces[4].metrics.loc.sloc
old: 7.0
new: 33.0

path: .spaces[4].metrics.nexits.average
old: null
new: 0.0

path: .spaces[4].metrics.cognitive.average
old: null
new: 3.0

path: .spaces[4].metrics.cognitive.sum
old: 0.0
new: 3.0

Code

TEST_F(ThreadMetrics, CollectRecursiveMetrics) {
  nsresult rv;

  initScheduler();

  // Dispatching a runnable that will last for +50ms
  // and run another one recursively that lasts for 400ms
  RefPtr runnable = new TimedRunnable(25, 25);
  nsCOMPtr nested = new TimedRunnable(400, 0);
  runnable->AddNestedRunnable({nested});
  rv = Dispatch(runnable);
  ASSERT_TRUE(NS_SUCCEEDED(rv));

  // Flush the queue
  ProcessAllEvents();

  // let's look at the counters
  ASSERT_EQ(mCounter->GetDispatchCounter()[mOther], 1u);

  // other counters should stay empty
  for (uint32_t i = 0; i < mDispatchCount; i++) {
    if (i != mOther) {
      ASSERT_EQ(mCounter->GetDispatchCounter()[i], 0u);
    }
  }

  // did we get incremented in the docgroup ?
  uint64_t duration = mCounter->GetExecutionDuration();
  ASSERT_GE(duration, runnable->TotalSlept());

  // let's make sure we don't count the time spent in recursive calls
  ASSERT_LT(duration, runnable->TotalSlept() + 200000u);
}

Minimal test - lines (45, 106)

path: .spaces[1].metrics.loc.sloc
old: 1.0
new: 62.0

path: .spaces[1].metrics.loc.ploc
old: 1.0
new: 50.0

path: .spaces[1].metrics.loc.cloc
old: 0.0
new: 5.0

path: .spaces[1].metrics.loc.lloc
old: 0.0
new: 17.0

path: .spaces[1].metrics.loc.blank
old: 0.0
new: 7.0

path: .spaces[1].metrics.nargs.average
old: null
new: 1.1428571428571428

path: .spaces[1].metrics.nargs.sum
old: 0.0
new: 8.0

path: .spaces[1].metrics.mi.mi_sei
old: null
new: 37.96077545834554

path: .spaces[1].metrics.mi.mi_visual_studio
old: null
new: 36.98701826564402

path: .spaces[1].metrics.mi.mi_original
old: null
new: 63.24780123425127

path: .spaces[1].metrics.cyclomatic.average
old: 1.0
new: 1.375

path: .spaces[1].metrics.cyclomatic.sum
old: 1.0
new: 11.0

path: .spaces[1].metrics.cognitive.average
old: null
new: 0.7142857142857143

path: .spaces[1].metrics.cognitive.sum
old: 0.0
new: 5.0

path: .spaces[1].metrics.nom.functions
old: 0.0
new: 7.0

path: .spaces[1].metrics.nom.total
old: 0.0
new: 7.0

path: .spaces[1].metrics.halstead.n2
old: 1.0
new: 52.0

path: .spaces[1].metrics.halstead.volume
old: 0.0
new: 1599.4694434415578

path: .spaces[1].metrics.halstead.effort
old: 0.0
new: 39494.59164190309

path: .spaces[1].metrics.halstead.estimated_program_length
old: null
new: 406.4619653606445

path: .spaces[1].metrics.halstead.level
old: null
new: 0.04049844236760125

path: .spaces[1].metrics.halstead.bugs
old: 0.0
new: 0.3865780257375092

path: .spaces[1].metrics.halstead.N2
old: 1.0
new: 107.0

path: .spaces[1].metrics.halstead.vocabulary
old: 1.0
new: 76.0

path: .spaces[1].metrics.halstead.difficulty
old: 0.0
new: 24.692307692307693

path: .spaces[1].metrics.halstead.N1
old: 0.0
new: 149.0

path: .spaces[1].metrics.halstead.n1
old: 0.0
new: 24.0

path: .spaces[1].metrics.halstead.length
old: 1.0
new: 256.0

path: .spaces[1].metrics.halstead.purity_ratio
old: null
new: 1.5877420521900176

path: .spaces[1].metrics.halstead.time
old: 0.0
new: 2194.1439801057268

path: .spaces[1].metrics.nexits.sum
old: 0.0
new: 3.0

path: .spaces[1].metrics.nexits.average
old: null
new: 0.42857142857142855

Code

class TimedRunnable final : public Runnable {
 public:
  explicit TimedRunnable(uint32_t aExecutionTime1, uint32_t aExecutionTime2)
      : Runnable("TimedRunnable"),
        mExecutionTime1(aExecutionTime1),
        mExecutionTime2(aExecutionTime2) {}

  NS_IMETHODIMP Run() {
    Sleep(mExecutionTime1);
    for (uint32_t index = 0; index < mNestedRunnables.Length(); ++index) {
      if (index != 0) {
        Sleep(mExecutionTime1);
      }
      (void)DispatchNestedRunnable(mNestedRunnables[index].mRunnable,
                                   mNestedRunnables[index].mDocGroup);
    }
    Sleep(mExecutionTime2);
    return NS_OK;
  }

  void AddNestedRunnable(RunnableDescriptor aDescriptor) {
    mNestedRunnables.AppendElement(std::move(aDescriptor));
  }

  void Sleep(uint32_t aMilliseconds) {
    TimeStamp start = TimeStamp::Now();
    PR_Sleep(PR_MillisecondsToInterval(aMilliseconds + 5));
    TimeStamp stop = TimeStamp::Now();
    mTotalSlept += (stop - start).ToMicroseconds();
  }

  // Total sleep time, in microseconds.
  uint64_t TotalSlept() const { return mTotalSlept; }

  static void DispatchNestedRunnable(nsIRunnable* aRunnable,
                                     DocGroup* aDocGroup) {
    // Dispatch another runnable so nsThread::ProcessNextEvent is called
    // recursively
    nsCOMPtr thread = do_GetMainThread();
    if (aDocGroup) {
      (void)DispatchWithDocgroup(aRunnable, aDocGroup);
    } else {
      thread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
    }
    (void)NS_ProcessNextEvent(thread, false);
  }

  static nsresult DispatchWithDocgroup(nsIRunnable* aRunnable,
                                       DocGroup* aDocGroup) {
    nsCOMPtr runnable = aRunnable;
    runnable = new SchedulerGroup::Runnable(runnable.forget(), aDocGroup);
    return aDocGroup->Dispatch(TaskCategory::Other, runnable.forget());
  }

 private:
  uint32_t mExecutionTime1;
  uint32_t mExecutionTime2;
  // When we sleep, the actual time we sleep might not match how long
  // we asked to sleep for.  Record how much we actually slept.
  uint64_t mTotalSlept = 0;
  nsTArray mNestedRunnables;
};