# 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;
    };