text:
using content::NavigationSimulator;
using page_load_metrics::mojom::UserInteractionLatencies;
using page_load_metrics::mojom::UserInteractionLatency;
using page_load_metrics::mojom::UserInteractionType;

class AMPPageLoadMetricsObserverTest
    : public page_load_metrics::PageLoadMetricsObserverTestHarness,
      public testing::WithParamInterface<bool> {
 public:
  AMPPageLoadMetricsObserverTest() {}

  AMPPageLoadMetricsObserverTest(const AMPPageLoadMetricsObserverTest&) =
      delete;
  AMPPageLoadMetricsObserverTest& operator=(
      const AMPPageLoadMetricsObserverTest&) = delete;

  void SetUp() override {
    PageLoadMetricsObserverTestHarness::SetUp();
    ResetTest();
  }

  void ResetTest() {
    page_load_metrics::InitPageLoadTimingForTest(&timing_);
    // Reset to the default testing state. Does not reset histogram state.
    timing_.navigation_start = base::Time::FromDoubleT(1);
    timing_.response_start = base::Seconds(2);
    timing_.parse_timing->parse_start = base::Seconds(3);
    timing_.paint_timing->first_contentful_paint = base::Seconds(4);
    timing_.paint_timing->first_image_paint = base::Seconds(5);
    timing_.document_timing->load_event_start = base::Seconds(7);
    PopulateRequiredTimingFields(&timing_);
  }

  void RunTest(const GURL& url) {
    NavigateAndCommit(url);
    tester()->SimulateTimingUpdate(timing_);

    // Navigate again to force OnComplete, which happens when a new navigation
    // occurs.
    NavigateAndCommit(GURL("http://otherurl.com"));
  }

  void ValidateHistogramsFor(const std::string& histogram,
                             const char* view_type,
                             const absl::optional<base::TimeDelta>& event,
                             bool expect_histograms) {
    const size_t kTypeOffset = strlen("PageLoad.Clients.AMP.");
    std::string view_type_histogram = histogram;
    view_type_histogram.insert(kTypeOffset, view_type);
    tester()->histogram_tester().ExpectTotalCount(histogram,
                                                  expect_histograms ? 1 : 0);
    tester()->histogram_tester().ExpectTotalCount(view_type_histogram,
                                                  expect_histograms ? 1 : 0);
    if (!expect_histograms)
      return;
    tester()->histogram_tester().ExpectUniqueSample(
        histogram,
        static_cast<base::HistogramBase::Sample>(
            event.value().InMilliseconds()),
        1);
    tester()->histogram_tester().ExpectUniqueSample(
        view_type_histogram,
        static_cast<base::HistogramBase::Sample>(
            event.value().InMilliseconds()),
        1);
  }

  ukm::mojom::UkmEntryPtr GetAmpPageLoadUkmEntry(const GURL& url) {
    ukm::mojom::UkmEntryPtr entry;
    for (auto& it : tester()->test_ukm_recorder().GetMergedEntriesByName(
             ukm::builders::AmpPageLoad::kEntryName)) {
      const ukm::UkmSource* source =
          tester()->test_ukm_recorder().GetSourceForSourceId(it.first);
      if (source->url() == url) {
        entry = std::move(it.second);
      }
    }
    return entry;
  }

 protected:
  bool WithFencedFrames() { return GetParam(); }

  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
    tracker->AddObserver(base::WrapUnique(new AMPPageLoadMetricsObserver()));
  }

  content::RenderFrameHost* AppendChildFrame(content::RenderFrameHost* parent,
                                             const char* frame_name) {
    if (WithFencedFrames()) {
      return content::RenderFrameHostTester::For(parent)->AppendFencedFrame();
    } else {
      return content::RenderFrameHostTester::For(parent)->AppendChild(
          frame_name);
    }
  }

  content::RenderFrameHost* AppendChildFrameAndNavigateAndCommit(
      content::RenderFrameHost* parent,
      const char* frame_name,
      const GURL& url) {
    content::RenderFrameHost* subframe = AppendChildFrame(parent, frame_name);
    std::unique_ptr<NavigationSimulator> simulator =
        NavigationSimulator::CreateRendererInitiated(url, subframe);
    simulator->Commit();
    return simulator->GetFinalRenderFrameHost();
  }

  page_load_metrics::mojom::PageLoadTiming timing_;
};

INSTANTIATE_TEST_SUITE_P(All, AMPPageLoadMetricsObserverTest, testing::Bool());

TEST_P(AMPPageLoadMetricsObserverTest, AMPCachePage) {
  RunTest(GURL("https://cdn.ampproject.org/page"));
  EXPECT_TRUE(tester()
                  ->test_ukm_recorder()
                  .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                  .empty());
}

TEST_P(AMPPageLoadMetricsObserverTest, GoogleSearchAMPCachePage) {
  RunTest(GURL("https://www.google.com/amp/page"));
  EXPECT_TRUE(tester()
                  ->test_ukm_recorder()
                  .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                  .empty());
}

TEST_P(AMPPageLoadMetricsObserverTest, GoogleSearchAMPCachePageBaseURL) {
  RunTest(GURL("https://www.google.com/amp/"));
  EXPECT_TRUE(tester()
                  ->test_ukm_recorder()
                  .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                  .empty());
}

TEST_P(AMPPageLoadMetricsObserverTest, GoogleNewsAMPCachePage) {
  RunTest(GURL("https://news.google.com/news/amp?page"));
  EXPECT_TRUE(tester()
                  ->test_ukm_recorder()
                  .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                  .empty());
}

TEST_P(AMPPageLoadMetricsObserverTest, GoogleNewsAMPCachePageBaseURL) {
  RunTest(GURL("https://news.google.com/news/amp"));
  EXPECT_TRUE(tester()
                  ->test_ukm_recorder()
                  .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                  .empty());
}

TEST_P(AMPPageLoadMetricsObserverTest, NonAMPPage) {
  RunTest(GURL("https://www.google.com/not-amp/page"));
  EXPECT_TRUE(tester()
                  ->test_ukm_recorder()
                  .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                  .empty());
}

TEST_P(AMPPageLoadMetricsObserverTest, GoogleSearchAMPViewerSameDocument) {
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://www.google.com/search"), main_rfh())
      ->Commit();

  NavigationSimulator::CreateRendererInitiated(
      GURL("https://www.google.com/amp/page"), main_rfh())
      ->CommitSameDocument();

  // Verify that subframe metrics aren't recorded without an AMP subframe.
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming."
      "MainFrameToSubFrameNavigationDelta.Subframe",
      0);

  EXPECT_TRUE(tester()
                  ->test_ukm_recorder()
                  .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                  .empty());
}

TEST_P(AMPPageLoadMetricsObserverTest, SubFrameInputBeforeNavigation) {
  GURL main_frame_url("https://ampviewer.com/");
  GURL amp_url("https://ampviewer.com/page");

  // This emulates the AMP subframe non-prerender flow: first we perform a
  // same-document navigation in the main frame to the AMP viewer URL, then we
  // create and navigate the subframe to an AMP cache URL.
  NavigationSimulator::CreateRendererInitiated(main_frame_url, main_rfh())
      ->Commit();

  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
      ->CommitSameDocument();

  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      1);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming."
      "MainFrameToSubFrameNavigationDelta.Subframe",
      0);

  ukm::mojom::UkmEntryPtr main_frame_entry =
      GetAmpPageLoadUkmEntry(main_frame_url);
  ukm::mojom::UkmEntryPtr sub_frame_entry = GetAmpPageLoadUkmEntry(amp_url);
  ASSERT_NE(nullptr, main_frame_entry.get());
  ASSERT_NE(nullptr, sub_frame_entry.get());

  // We expect a source with a negative NavigationDelta metric, since the main
  // frame navigation occurred before the AMP subframe navigation.
  const int64_t* nav_delta_metric =
      tester()->test_ukm_recorder().GetEntryMetric(
          sub_frame_entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
  EXPECT_NE(nullptr, nav_delta_metric);
  EXPECT_GE(*nav_delta_metric, 0ll);

  const int64_t* amp_subframe_metric =
      tester()->test_ukm_recorder().GetEntryMetric(main_frame_entry.get(),
                                                   "SubFrameAmpPageLoad");
  EXPECT_NE(nullptr, amp_subframe_metric);
  EXPECT_GE(*amp_subframe_metric, 1ll);
  EXPECT_EQ(nullptr, tester()->test_ukm_recorder().GetEntryMetric(
                         main_frame_entry.get(), "MainFrameAmpPageLoad"));
}

TEST_P(AMPPageLoadMetricsObserverTest, SubFrameNavigationBeforeInput) {
  GURL amp_url("https://ampviewer.com/page");

  // This emulates the AMP subframe prerender flow: first we create and navigate
  // the subframe to an AMP cache URL, then we perform a same-document
  // navigation in the main frame to the AMP viewer URL.
  NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
                                               main_rfh())
      ->Commit();

  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
      ->CommitSameDocument();

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
      1);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming."
      "MainFrameToSubFrameNavigationDelta.Subframe",
      0);

  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
  ASSERT_NE(nullptr, entry.get());

  // We expect a source with a positive NavigationDelta metric, since the main
  // frame navigation occurred after the AMP subframe navigation.
  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
  const int64_t* nav_delta_metric =
      tester()->test_ukm_recorder().GetEntryMetric(
          entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
  EXPECT_NE(nullptr, nav_delta_metric);
  EXPECT_LE(*nav_delta_metric, 0ll);
}

TEST_P(AMPPageLoadMetricsObserverTest, SubFrameMetrics) {
  GURL amp_url("https://ampviewer.com/page");

  NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
                                               main_rfh())
      ->Commit();

  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
      ->CommitSameDocument();

  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  page_load_metrics::mojom::PageLoadTiming subframe_timing;
  page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
  subframe_timing.navigation_start = base::Time::FromDoubleT(2);
  subframe_timing.paint_timing->first_paint = base::Milliseconds(4);
  subframe_timing.paint_timing->first_contentful_paint = base::Milliseconds(5);
  subframe_timing.paint_timing->largest_contentful_paint
      ->largest_image_paint_size = 1;
  subframe_timing.paint_timing->largest_contentful_paint->largest_image_paint =
      base::Milliseconds(10);
  subframe_timing.paint_timing->experimental_largest_contentful_paint
      ->largest_text_paint_size = 3;
  subframe_timing.paint_timing->experimental_largest_contentful_paint
      ->largest_text_paint = base::Milliseconds(8);
  subframe_timing.interactive_timing->first_input_timestamp =
      base::Milliseconds(20);
  subframe_timing.interactive_timing->first_input_delay = base::Milliseconds(3);
  PopulateRequiredTimingFields(&subframe_timing);

  tester()->SimulateTimingUpdate(subframe_timing, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.PaintTiming.InputToFirstContentfulPaint.Subframe",
      1);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.PaintTiming.InputToLargestContentfulPaint.Subframe",
      1);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.InteractiveTiming.FirstInputDelay4.Subframe", 1);

  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
  ASSERT_NE(nullptr, entry.get());
  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(), "SubFrame.InteractiveTiming.FirstInputDelay4", 3);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(), "SubFrame.PaintTiming.NavigationToFirstPaint", 4);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(), "SubFrame.PaintTiming.NavigationToFirstContentfulPaint", 5);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(), "SubFrame.PaintTiming.NavigationToLargestContentfulPaint2",
      10);
}

TEST_P(AMPPageLoadMetricsObserverTest, SubFrameMetrics_LayoutInstability) {
  GURL amp_url("https://ampviewer.com/page");

  NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
                                               main_rfh())
      ->Commit();

  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
      ->CommitSameDocument();

  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  page_load_metrics::mojom::FrameRenderDataUpdate render_data(1.0, 0.5, 0, 0, 0,
                                                              0, {});
  tester()->SimulateRenderDataUpdate(render_data, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  tester()->histogram_tester().ExpectUniqueSample(
      "PageLoad.Clients.AMP.LayoutInstability.CumulativeShiftScore.Subframe",
      10, 1);

  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
  ASSERT_NE(nullptr, entry.get());
  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(), "SubFrame.LayoutInstability.CumulativeShiftScore", 100);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(),
      "SubFrame.LayoutInstability.CumulativeShiftScore.BeforeInputOrScroll",
      50);
}

TEST_P(AMPPageLoadMetricsObserverTest,
       SubFrameMetrics_Layout_Shift_Normalization) {
  GURL amp_url("https://ampviewer.com/page");

  NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
                                               main_rfh())
      ->Commit();

  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
      ->CommitSameDocument();

  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  base::TimeTicks current_time = base::TimeTicks::Now();
  page_load_metrics::mojom::FrameRenderDataUpdate render_data(0.65, 0.65, 0, 0,
                                                              0, 0, {});

  render_data.new_layout_shifts.emplace_back(
      page_load_metrics::mojom::LayoutShift::New(
          current_time - base::Milliseconds(4000), 0.1));
  render_data.new_layout_shifts.emplace_back(
      page_load_metrics::mojom::LayoutShift::New(
          current_time - base::Milliseconds(3000), 0.1));
  render_data.new_layout_shifts.emplace_back(
      page_load_metrics::mojom::LayoutShift::New(
          current_time - base::Milliseconds(2000), 0.2));
  render_data.new_layout_shifts.emplace_back(
      page_load_metrics::mojom::LayoutShift::New(
          current_time - base::Milliseconds(200), 0.1));
  render_data.new_layout_shifts.emplace_back(
      page_load_metrics::mojom::LayoutShift::New(
          current_time - base::Milliseconds(100), 0.15));

  tester()->SimulateRenderDataUpdate(render_data, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
  ASSERT_NE(nullptr, entry.get());
  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(), "SubFrame.LayoutInstability.CumulativeShiftScore", 65);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(),
      "SubFrame.LayoutInstability.CumulativeShiftScore.BeforeInputOrScroll",
      65);
  // Layout Shift Normalization UKM.
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(),
      "SubFrame.LayoutInstability.MaxCumulativeShiftScore.SessionWindow."
      "Gap1000ms.Max5000ms",
      40);
  tester()->histogram_tester().ExpectUniqueSample(
      "PageLoad.Clients.AMP.LayoutInstability.MaxCumulativeShiftScore."
      "Subframe.SessionWindow.Gap1000ms.Max5000ms",
      4, 1);
}

TEST_P(AMPPageLoadMetricsObserverTest,
       SubFrameResponsivenessMetricsNormalization) {
  GURL amp_url("https://ampviewer.com/page");
  NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
                                               main_rfh())
      ->Commit();
  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
      ->CommitSameDocument();

  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  page_load_metrics::mojom::InputTiming input_timing;
  input_timing.num_interactions = 3;
  input_timing.max_event_durations =
      UserInteractionLatencies::NewUserInteractionLatencies({});
  auto& max_event_durations =
      input_timing.max_event_durations->get_user_interaction_latencies();
  max_event_durations.emplace_back(UserInteractionLatency::New(
      base::Milliseconds(50), UserInteractionType::kKeyboard));
  max_event_durations.emplace_back(UserInteractionLatency::New(
      base::Milliseconds(100), UserInteractionType::kTapOrClick));
  max_event_durations.emplace_back(UserInteractionLatency::New(
      base::Milliseconds(150), UserInteractionType::kDrag));

  tester()->SimulateInputTimingUpdate(input_timing, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();
  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);

  std::vector<std::pair<std::string, int64_t>> ukm_list = {
      std::make_pair("SubFrame.InteractiveTiming.WorstUserInteractionLatency."
                     "MaxEventDuration2",
                     150),
      std::make_pair(
          "SubFrame.InteractiveTiming.AverageUserInteractionLatencyOverBudget."
          "MaxEventDuration2",
          16),
      std::make_pair(
          "SubFrame.InteractiveTiming.SumOfUserInteractionLatencyOverBudget."
          "MaxEventDuration2",
          50),
      std::make_pair(
          "SubFrame.InteractiveTiming.SlowUserInteractionLatencyOverBudget."
          "HighPercentile2.MaxEventDuration2",
          50),
      std::make_pair("SubFrame.InteractiveTiming.UserInteractionLatency."
                     "HighPercentile2.MaxEventDuration",
                     150),
      std::make_pair("SubFrame.InteractiveTiming.NumInteractions", 3)};

  for (auto& metric : ukm_list) {
    tester()->test_ukm_recorder().ExpectEntryMetric(entry.get(), metric.first,
                                                    metric.second);
  }

  std::vector<std::string> uma_list = {
      "PageLoad.Clients.AMP.InteractiveTiming."
      "AverageUserInteractionLatencyOverBudget.MaxEventDuration.Subframe",
      "PageLoad.Clients.AMP.InteractiveTiming."
      "SumOfUserInteractionLatencyOverBudget.MaxEventDuration.Subframe",
      "PageLoad.Clients.AMP.InteractiveTiming."
      "SlowUserInteractionLatencyOverBudget.HighPercentile2.MaxEventDuration."
      "Subframe",
      "PageLoad.Clients.AMP.InteractiveTiming."
      "UserInteractionLatency.HighPercentile2.MaxEventDuration.Subframe",
      "PageLoad.Clients.AMP.InteractiveTiming.WorstUserInteractionLatency."
      "MaxEventDuration.Subframe",
  };

  for (auto& metric : uma_list) {
    tester()->histogram_tester().ExpectTotalCount(metric, 1);
  }
}

TEST_P(AMPPageLoadMetricsObserverTest,
       SubFrameResponsivenessMetricsNormalizations) {
  GURL amp_url("https://ampviewer.com/page");

  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())->Commit();

  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  page_load_metrics::mojom::InputTiming input_timing;
  input_timing.num_interactions = 3;
  input_timing.max_event_durations =
      UserInteractionLatencies::NewUserInteractionLatencies({});
  auto& max_event_durations =
      input_timing.max_event_durations->get_user_interaction_latencies();
  max_event_durations.emplace_back(UserInteractionLatency::New(
      base::Milliseconds(50), UserInteractionType::kKeyboard));
  max_event_durations.emplace_back(UserInteractionLatency::New(
      base::Milliseconds(100), UserInteractionType::kTapOrClick));
  max_event_durations.emplace_back(UserInteractionLatency::New(
      base::Milliseconds(150), UserInteractionType::kDrag));

  tester()->SimulateInputTimingUpdate(input_timing, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();
  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);

  std::vector<std::pair<std::string, int64_t>> ukm_list = {
      std::make_pair("SubFrame.InteractiveTiming.WorstUserInteractionLatency."
                     "MaxEventDuration2",
                     150),
      std::make_pair(
          "SubFrame.InteractiveTiming.AverageUserInteractionLatencyOverBudget."
          "MaxEventDuration2",
          16),
      std::make_pair(
          "SubFrame.InteractiveTiming.SumOfUserInteractionLatencyOverBudget."
          "MaxEventDuration2",
          50),
      std::make_pair("SubFrame.InteractiveTiming.UserInteractionLatency."
                     "HighPercentile2.MaxEventDuration",
                     150),
      std::make_pair(
          "SubFrame.InteractiveTiming.SlowUserInteractionLatencyOverBudget."
          "HighPercentile2.MaxEventDuration2",
          50),
  };

  for (auto& metric : ukm_list) {
    tester()->test_ukm_recorder().ExpectEntryMetric(entry.get(), metric.first,
                                                    metric.second);
  }

  std::vector<std::string> uma_list = {
      "PageLoad.Clients.AMP.InteractiveTiming."
      "AverageUserInteractionLatencyOverBudget.MaxEventDuration.Subframe."
      "FullNavigation",
      "PageLoad.Clients.AMP.InteractiveTiming."
      "SumOfUserInteractionLatencyOverBudget.MaxEventDuration.Subframe."
      "FullNavigation",
      "PageLoad.Clients.AMP.InteractiveTiming."
      "SlowUserInteractionLatencyOverBudget.HighPercentile2.MaxEventDuration."
      "Subframe.FullNavigation",
      "PageLoad.Clients.AMP.InteractiveTiming."
      "UserInteractionLatency.HighPercentile2.MaxEventDuration."
      "Subframe.FullNavigation",
      "PageLoad.Clients.AMP.InteractiveTiming.WorstUserInteractionLatency."
      "MaxEventDuration.Subframe.FullNavigation",
  };

  for (auto& metric : uma_list) {
    tester()->histogram_tester().ExpectTotalCount(metric, 1);
  }
}

TEST_P(AMPPageLoadMetricsObserverTest, SubFrameMetricsFullNavigation) {
  GURL amp_url("https://ampviewer.com/page");

  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())->Commit();

  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  page_load_metrics::mojom::PageLoadTiming subframe_timing;
  page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
  subframe_timing.navigation_start = base::Time::FromDoubleT(2);
  subframe_timing.paint_timing->first_contentful_paint = base::Milliseconds(5);
  subframe_timing.paint_timing->largest_contentful_paint
      ->largest_image_paint_size = 1;
  subframe_timing.paint_timing->largest_contentful_paint->largest_image_paint =
      base::Milliseconds(10);
  subframe_timing.paint_timing->experimental_largest_contentful_paint
      ->largest_image_paint_size = 1;
  subframe_timing.paint_timing->experimental_largest_contentful_paint
      ->largest_image_paint = base::Milliseconds(5);
  subframe_timing.interactive_timing->first_input_timestamp =
      base::Milliseconds(20);
  subframe_timing.interactive_timing->first_input_delay = base::Milliseconds(3);
  PopulateRequiredTimingFields(&subframe_timing);

  tester()->SimulateTimingUpdate(subframe_timing, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.PaintTiming.InputToFirstContentfulPaint.Subframe."
      "FullNavigation",
      1);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.PaintTiming.InputToLargestContentfulPaint.Subframe."
      "FullNavigation",
      1);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.InteractiveTiming.FirstInputDelay4.Subframe."
      "FullNavigation",
      1);

  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(), "SubFrame.InteractiveTiming.FirstInputDelay4", 3);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(), "SubFrame.PaintTiming.NavigationToFirstContentfulPaint", 5);
  tester()->test_ukm_recorder().ExpectEntryMetric(
      entry.get(), "SubFrame.PaintTiming.NavigationToLargestContentfulPaint2",
      10);
}

TEST_P(AMPPageLoadMetricsObserverTest, SubFrameRecordOnFullNavigation) {
  GURL amp_url("https://ampviewer.com/page");

  NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
                                               main_rfh())
      ->Commit();

  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
      ->CommitSameDocument();

  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(GURL("https://www.example.com/"),
                                               main_rfh())
      ->Commit();

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      1);

  // We expect a source with a negative NavigationDelta metric, since the main
  // frame navigation occurred before the AMP subframe navigation.
  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
  const int64_t* nav_delta_metric =
      tester()->test_ukm_recorder().GetEntryMetric(
          entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
  EXPECT_NE(nullptr, nav_delta_metric);
  EXPECT_GE(*nav_delta_metric, 0ll);
}

TEST_P(AMPPageLoadMetricsObserverTest, SubFrameRecordOnFrameDeleted) {
  GURL amp_url("https://ampviewer.com/page");

  NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
                                               main_rfh())
      ->Commit();

  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
      ->CommitSameDocument();

  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      0);

  // Delete the subframe, which should trigger metrics recording.
  content::RenderFrameHostTester::For(subframe)->Detach();

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      1);

  // We expect a source with a negative NavigationDelta metric, since the main
  // frame navigation occurred before the AMP subframe navigation.
  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
  const int64_t* nav_delta_metric =
      tester()->test_ukm_recorder().GetEntryMetric(
          entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
  EXPECT_NE(nullptr, nav_delta_metric);
  EXPECT_GE(*nav_delta_metric, 0ll);
}

TEST_P(AMPPageLoadMetricsObserverTest, SubFrameMultipleFrames) {
  GURL main_frame_url("https://ampviewer.com/");
  GURL amp_url1("https://ampviewer.com/page");
  GURL amp_url2("https://ampviewer.com/page2");

  NavigationSimulator::CreateRendererInitiated(main_frame_url, main_rfh())
      ->Commit();

  // Simulate a prerender.
  GURL subframe_url2(
      "https://ampsubframe.com/page2"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage2");
  content::RenderFrameHost* subframe2 = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe2", subframe_url2);

  // Perform a main-frame navigation to a different AMP document (not the
  // prerender).
  NavigationSimulator::CreateRendererInitiated(amp_url1, main_rfh())
      ->CommitSameDocument();

  // Load the associated AMP document in an iframe.
  GURL subframe_url1(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe1 = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe1", subframe_url1);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe1);
  tester()->SimulateMetadataUpdate(metadata, subframe2);

  // Navigate the main frame to trigger metrics recording - we expect metrics to
  // have been recorded for 1 AMP page (the non-prerendered page).
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      1);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming."
      "MainFrameToSubFrameNavigationDelta.Subframe",
      0);

  // Now navigate to the main-frame URL for the prerendered AMP document. This
  // should trigger metrics recording for that document.
  NavigationSimulator::CreateRendererInitiated(amp_url2, main_rfh())
      ->CommitSameDocument();

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  // We now expect one NavigationToInput (for the prerender) and one
  // InputToNavigation (for the non-prerender).
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
      1);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      1);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming."
      "MainFrameToSubFrameNavigationDelta.Subframe",
      0);

  std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> entries =
      tester()->test_ukm_recorder().GetMergedEntriesByName(
          ukm::builders::AmpPageLoad::kEntryName);
  EXPECT_EQ(3ull, entries.size());

  const ukm::UkmSource* source1 = nullptr;
  const ukm::UkmSource* source2 = nullptr;
  for (const auto& kv : entries) {
    const ukm::UkmSource* candidate =
        tester()->test_ukm_recorder().GetSourceForSourceId(kv.first);
    ASSERT_NE(nullptr, candidate);
    if (candidate->url() == amp_url1) {
      source1 = candidate;
    } else if (candidate->url() == amp_url2) {
      source2 = candidate;
    } else if (candidate->url() == main_frame_url) {
      // Ignore.
    } else {
      FAIL() << "Encountered unexpected source for: " << candidate->url();
    }
  }
  EXPECT_NE(source1, source2);

  const ukm::mojom::UkmEntry* entry1 = entries.at(source1->id()).get();
  EXPECT_NE(nullptr, entry1);
  const ukm::mojom::UkmEntry* entry2 = entries.at(source2->id()).get();
  EXPECT_NE(nullptr, entry2);

  // The entry for amp_url1 should have a negative NavigationDelta metric, since
  // the main frame navigation occurred before the AMP subframe navigation.
  const int64_t* entry1_nav_delta_metric =
      tester()->test_ukm_recorder().GetEntryMetric(
          entry1, "SubFrame.MainFrameToSubFrameNavigationDelta");
  EXPECT_NE(nullptr, entry1_nav_delta_metric);
  EXPECT_GE(*entry1_nav_delta_metric, 0ll);

  // The entry for amp_url2 should have a positive NavigationDelta metric, since
  // the main frame navigation occurred after the AMP subframe navigation.
  const int64_t* entry2_nav_delta_metric =
      tester()->test_ukm_recorder().GetEntryMetric(
          entry2, "SubFrame.MainFrameToSubFrameNavigationDelta");
  EXPECT_NE(nullptr, entry2_nav_delta_metric);
  EXPECT_LE(*entry2_nav_delta_metric, 0ll);
}

TEST_P(AMPPageLoadMetricsObserverTest,
       SubFrameWithNonSameDocumentMainFrameNavigation) {
  GURL amp_url("https://ampviewer.com/page");

  NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())->Commit();

  // Load the associated AMP document in an iframe.
  GURL subframe_url(
      "https://ampsubframe.com/page"
      "?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage");
  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming."
      "MainFrameToSubFrameNavigationDelta.Subframe",
      1);

  // We expect a source with a negative NavigationDelta metric, since the main
  // frame navigation occurred before the AMP subframe navigation.
  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
  const int64_t* nav_delta_metric =
      tester()->test_ukm_recorder().GetEntryMetric(
          entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
  EXPECT_NE(nullptr, nav_delta_metric);
  EXPECT_GE(*nav_delta_metric, 0ll);
}

TEST_P(AMPPageLoadMetricsObserverTest, NoSubFrameMetricsForNonAmpSubFrame) {
  NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
                                               main_rfh())
      ->Commit();

  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/page"), main_rfh())
      ->CommitSameDocument();

  // Create a non-AMP subframe document.
  GURL subframe_url("https://example.com/");
  AppendChildFrameAndNavigateAndCommit(web_contents()->GetPrimaryMainFrame(),
                                       "subframe", subframe_url);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming."
      "MainFrameToSubFrameNavigationDelta.Subframe",
      0);

  EXPECT_TRUE(tester()
                  ->test_ukm_recorder()
                  .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                  .empty());
}

TEST_P(AMPPageLoadMetricsObserverTest,
       NoSubFrameMetricsForSubFrameWithoutViewerUrl) {
  GURL subframe_url("https://ampviewer.com/page");
  NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
                                               main_rfh())
      ->Commit();

  NavigationSimulator::CreateRendererInitiated(GURL(subframe_url), main_rfh())
      ->CommitSameDocument();

  content::RenderFrameHost* subframe = AppendChildFrameAndNavigateAndCommit(
      web_contents()->GetPrimaryMainFrame(), "subframe", subframe_url);

  page_load_metrics::mojom::FrameMetadata metadata;
  metadata.behavior_flags =
      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
  tester()->SimulateMetadataUpdate(metadata, subframe);

  // Navigate the main frame to trigger metrics recording.
  NavigationSimulator::CreateRendererInitiated(
      GURL("https://ampviewer.com/other"), main_rfh())
      ->CommitSameDocument();

  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
      0);
  tester()->histogram_tester().ExpectTotalCount(
      "PageLoad.Clients.AMP.Experimental.PageTiming."
      "MainFrameToSubFrameNavigationDelta.Subframe",
      0);

  ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(subframe_url);
  EXPECT_EQ(nullptr, entry.get());
}


text:
cask "ananas-analytics-desktop-edition" do
  version "0.9.0"
  sha256 "cb4a73521d5f5ffb97405b3c7e041f73fd5044ab1ffe1ff6e967c0fb1555dec3"

  # github.com/ananas-analytics/ananas-desktop/ was verified as official when first introduced to the cask
  url "https://github.com/ananas-analytics/ananas-desktop/releases/download/v#{version}/Ananas.Analytics.Desktop.Edition-#{version}.macos.dmg"
  appcast "https://github.com/ananas-analytics/ananas-desktop/releases.atom"
  name "Ananas Analytics Desktop Edition"
  desc "Hackable data integration & analysis tool"
  homepage "https://ananasanalytics.com/"

  app "Ananas Analytics Desktop Edition.app"
end

redpajama_set_name: RedPajamaGithub


text:
<component>
	<!-- Virgo archive base content -->
	<fileSets>
		<fileSet>
			<directory>${project.basedir}/target/virgo-tomcat-server-3.7.2.RELEASE
			</directory>
			<outputDirectory></outputDirectory>
			<useDefaultExcludes>true</useDefaultExcludes>
			<!-- The following files will be overwritten or unnecessary in the resulting
				archive -->
			<excludes>
				<exclude>**/dmk.bat</exclude>
				<exclude>**/startup.bat</exclude>
				<!-- Exclude all *.sh file, as we will add 755 permission later during
					copy -->
				<exclude>**/*.sh</exclude>
				<exclude>**/java-server.profile</exclude>
				<exclude>**/org.eclipse.virgo.kernel.userregion.properties</exclude>
				<exclude>**/org.eclipse.virgo.kernel.users.properties</exclude>
				<exclude>**/serviceability.xml</exclude>
				<exclude>**/org.eclipse.virgo.web.properties</exclude>
				<exclude>**/tomcat-server.xml</exclude>
				<exclude>**/bundles.info</exclude>
				<!-- Remove splash app -->
				<exclude>**/org.eclipse.virgo.apps.splash_3.7.2.RELEASE.jar</exclude>
				<exclude>repository/ext/osgi.console.properties</exclude>
				<exclude>plugins/org.apache.sshd.core_0.5.0.v201108120515.jar</exclude>
			</excludes>
		</fileSet>
		<!-- Common configuration -->
		<fileSet>
			<directory>${project.basedir}/assembly/common</directory>
			<outputDirectory></outputDirectory>
			<useDefaultExcludes>true</useDefaultExcludes>
			<excludes>
				<!-- Exclude all *.sh file, as we will add 755 permission later during
					copy -->
				<exclude>**/*.sh</exclude>
			</excludes>
		</fileSet>
		<!-- Copy *.sh files from Virgo default folder, except dmk.sh and startup.sh -->
		<fileSet>
			<directory>${project.basedir}/target/virgo-tomcat-server-3.7.2.RELEASE
			</directory>
			<outputDirectory></outputDirectory>
			<useDefaultExcludes>true</useDefaultExcludes>
			<fileMode>0755</fileMode>
			<includes>
				<include>**/*.sh</include>
			</includes>
			<excludes>
				<exclude>**/dmk.sh</exclude>
				<exclude>**/startup.sh</exclude>
			</excludes>
		</fileSet>
		<!-- Copy dmk.sh and startup.sh from common folder -->
		<fileSet>
			<directory>${project.basedir}/assembly/common</directory>
			<outputDirectory></outputDirectory>
			<useDefaultExcludes>true</useDefaultExcludes>
			<fileMode>0755</fileMode>
			<includes>
				<include>**/dmk.sh</include>
				<include>**/startup.sh</include>
				<include>**/snowowl*.sh</include>
			</includes>
		</fileSet>
		<!-- Application bundles to repository/usr -->
		<fileSet>
			<directory>${project.basedir}/target/repository/plugins</directory>
			<outputDirectory>repository/usr</outputDirectory>
			<useDefaultExcludes>true</useDefaultExcludes>
			<!-- Make sure we exclude already existing or unnecessary bundles -->
			<excludes>
				<exclude>**/*osgi.enterprise*.jar</exclude>
				<exclude>**/*system.bundle.package.exporter*.jar</exclude>
				<exclude>org.slf4j.api*.jar</exclude>
				<exclude>org.slf4j.logback*.jar</exclude>
				<exclude>*logback*.jar</exclude>
				<exclude>javax.el*.jar</exclude>
				<exclude>org.apache.el*.jar</exclude>
				<exclude>com.b2international.snowowl.logging.amq*.jar</exclude>
			</excludes>
		</fileSet>
		<fileSet>
			<directory>${project.basedir}/target/repository/plugins</directory>
			<outputDirectory>plugins</outputDirectory>
			<useDefaultExcludes>true</useDefaultExcludes>
			<!-- Make sure we exclude already existing or unnecessary bundles -->
			<includes>
				<include>com.b2international.snowowl.logging.amq*.jar</include>
			</includes>
		</fileSet>
	</fileSets>
	<!-- Copy updated SSHD bundle that fixes SSH connection from newer, smarter clients -->
	<dependencySets>
		<dependencySet>
			<includes>
				<include>org.apache.sshd:sshd-core</include>
			</includes>
			<outputDirectory>plugins</outputDirectory>
		</dependencySet>
		<dependencySet>
			<includes>
				<include>org.semanticweb.elk:elk-protege</include>
			</includes>
			<outputDirectory>repository/usr</outputDirectory>
		</dependencySet>
	</dependencySets>
</component>


text:
// Copyright (c) MarinAtanasov. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the project root for license information.
//
using System;

namespace AppBrix.Backgammon.Rules.Strategies;

internal class DefaultGameRuleStrategyContext : IGameRuleStrategyContext
{
    #region Properties
    public bool IsDone
    {
        get
        {
            return this.isDone;
        }
        set
        {
            if (this.IsDone != value)
            {
                if (this.IsDone)
                    throw new InvalidOperationException("Once the context is finished, it cannot be resumed.");

                this.isDone = value;
            }
        }
    }
    #endregion

    #region Private fields and constants
    private bool isDone;
    #endregion
}


text:
package com.microsoft.office365.profile.model;

import android.support.annotation.NonNull;

/**
 * Model class to store OneDrive for Business data.
 */
public class File implements CharSequence {
    public String name;
    public Node lastModifiedBy;

    @Override
    public int length() {
        return name.length();
    }

    @Override
    public char charAt(int index) {
        return name.charAt(index);
    }

    /**
     * Implemented this method to be able to use this object with the default ArrayAdapter<> class.
     * @return
     */
    @NonNull
    @Override
    public String toString() {
        return name;
    }

    @Override
    public CharSequence subSequence(int start, int end) {
        return name.subSequence(start, end);
    }

    public static class Node{
        public User user;
    }
}

// *********************************************************
//
// O365-Android-Profile, https://github.com/OfficeDev/O365-Android-Profile
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// *********************************************************


text:
Anti-pattern: Fetching too many objects at once with ray.get causes failure
===========================================================================

**TLDR:** Avoid calling :ref:`ray.get() <ray-get-ref>` on too many objects since this will lead to heap out-of-memory or object store out-of-space. Instead fetch and process one batch at a time.

If you have a large number of tasks that you want to run in parallel, trying to do ``ray.get()`` on all of them at once could lead to failure with heap out-of-memory or object store out-of-space since Ray needs to fetch all the objects to the caller at the same time.
Instead you should get and process the results one batch at a time. Once a batch is processed, Ray will evict objects in that batch to make space for future batches.

.. figure:: ../images/ray-get-too-many-objects.svg

    Fetching too many objects at once with ``ray.get()``

Code example
------------

**Anti-pattern:**

.. literalinclude:: ../doc_code/anti_pattern_ray_get_too_many_objects.py
    :language: python
    :start-after: __anti_pattern_start__
    :end-before: __anti_pattern_end__

**Better approach:**

.. literalinclude:: ../doc_code/anti_pattern_ray_get_too_many_objects.py
    :language: python
    :start-after: __better_approach_start__
    :end-before: __better_approach_end__

Here besides getting one batch at a time to avoid failure, we are also using ``ray.wait()`` to process results in the finish order instead of the submission order to reduce the runtime. See :doc:`ray-get-submission-order` for more details.


text:
package com.github.dozermapper.spring;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.github.dozermapper.core.BeanFactory;
import com.github.dozermapper.core.CustomConverter;
import com.github.dozermapper.core.CustomFieldMapper;
import com.github.dozermapper.core.DozerBeanMapperBuilder;
import com.github.dozermapper.core.Mapper;
import com.github.dozermapper.core.events.EventListener;
import com.github.dozermapper.core.loader.api.BeanMappingBuilder;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.core.io.Resource;

/**
 * {@link FactoryBean} that can be used to create an instance of {@link Mapper}
 */
public class DozerBeanMapperFactoryBean extends ApplicationObjectSupport implements InitializingBean, FactoryBean<Mapper> {

    private CustomFieldMapper customFieldMapper;
    private final List<String> mappingFileUrls = new ArrayList<>(1);
    private final List<CustomConverter> customConverters = new ArrayList<>(0);
    private final List<BeanMappingBuilder> mappingBuilders = new ArrayList<>(0);
    private final List<EventListener> eventListeners = new ArrayList<>(0);
    private final Map<String, BeanFactory> beanFactories = new HashMap<>(0);
    private final Map<String, CustomConverter> customConvertersWithId = new HashMap<>(0);

    private Mapper mapper;

    /**
     * Registers a {@link CustomFieldMapper} for the mapper.
     *

     * By default, no custom field mapper is registered.
     *
     * @param customFieldMapper custom field mapper to be registered for the mapper.
     */
    public void setCustomFieldMapper(CustomFieldMapper customFieldMapper) {
        this.customFieldMapper = customFieldMapper;
    }

    /**
     * Spring resources definition for providing mapping file location.
     * Could be used for loading all mapping files by wildcard definition for example
     *

     * {@code
     *  <bean class="com.github.dozermapper.core.spring.DozerBeanMapperFactoryBean">
     *      <property name="mappingFiles" value="classpath*:/*.dozer.xml"/>
     *  <\/bean>
     * }
     *

     *
     * @param mappingFiles Spring resource definition
     * @throws IOException if URL fails to resolve
     */
    public void setMappingFiles(Resource[] mappingFiles) throws IOException {
        if (mappingFiles != null && mappingFiles.length > 0) {
            for (Resource mappingFile : mappingFiles) {
                URL url = mappingFile.getURL();
                mappingFileUrls.add(url.toString());
            }
        }
    }

    /**
     * Registers a collection of {@link CustomConverter} for the mapper.
     *

     * By default, no custom converters are used by generated mapper.
     *
     * @param customConverters converters to be registered.
     */
    public void setCustomConverters(List<CustomConverter> customConverters) {
        this.customConverters.addAll(customConverters);
    }

    /**
     * Registers a {@link BeanMappingBuilder} for the mapper.
     *

     * By default, no API builders are registered.
     *
     * @param mappingBuilders mapping builders to be registered for the mapper.
     */
    public void setMappingBuilders(List<BeanMappingBuilder> mappingBuilders) {
        this.mappingBuilders.addAll(mappingBuilders);
    }

    /**
     * Registers a {@link EventListener} for the mapper.
     *

     * By default, no listeners are registered.
     *
     * @param eventListeners listeners to be registered for the mapper.
     */
    public void setEventListeners(List<EventListener> eventListeners) {
        this.eventListeners.addAll(eventListeners);
    }

    /**
     * Registers a {@link BeanFactory} for the mapper.
     *

     * By default, no custom bean factories are registered.
     *
     * @param beanFactories factorys to be used by mapper.
     */
    public void setFactories(Map<String, BeanFactory> beanFactories) {
        this.beanFactories.putAll(beanFactories);
    }

    /**
     * Registers a {@link CustomConverter} which can be referenced in mapping by provided ID.
     *

     * Converter instances provided this way are considered stateful and will not be initialized for each mapping.
     *

     * By default, no converters with IDs are registered.
     *
     * @param customConvertersWithId converters to be used for provided ID.
     */
    public void setCustomConvertersWithId(Map<String, CustomConverter> customConvertersWithId) {
        this.customConvertersWithId.putAll(customConvertersWithId);
    }

    // ===
    // Methods for: InitializingBean
    // ===

    /**
     * {@inheritDoc}
     */
    @Override
    public void afterPropertiesSet() {
        Map<String, CustomConverter> contextCustomConvertersWithId = getApplicationContext().getBeansOfType(CustomConverter.class);
        Map<String, BeanMappingBuilder> contextBeanMappingBuilders = getApplicationContext().getBeansOfType(BeanMappingBuilder.class);
        Map<String, EventListener> contextEventListeners = getApplicationContext().getBeansOfType(EventListener.class);
        Map<String, BeanFactory> contextBeanFactories = getApplicationContext().getBeansOfType(BeanFactory.class);

        customConverters.addAll(contextCustomConvertersWithId.values());
        mappingBuilders.addAll(contextBeanMappingBuilders.values());
        beanFactories.putAll(contextBeanFactories);
        eventListeners.addAll(contextEventListeners.values());
        customConvertersWithId.putAll(contextCustomConvertersWithId);

        this.mapper = DozerBeanMapperBuilder.create()
                .withMappingFiles(mappingFileUrls)
                .withCustomFieldMapper(customFieldMapper)
                .withCustomConverters(customConverters)
                .withMappingBuilders(mappingBuilders)
                .withEventListeners(eventListeners)
                .withBeanFactorys(beanFactories)
                .withCustomConvertersWithIds(customConvertersWithId)
                .build();
    }

    // ===
    // Methods for: FactoryBean<Mapper>
    // ===

    /**
     * {@inheritDoc}
     */
    @Override
    public Mapper getObject() {
        return this.mapper;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Class<Mapper> getObjectType() {
        return Mapper.class;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}


text:
const tags = require('./tags')
const tagger = require('./tagger')

const methods = Object.assign(
  {},
  require('./misc/append'),
  require('./misc/json'),
  require('./misc/negative'),
  require('./questions'),
  require('./tense'),
  require('./phrases')
)

const plugin = function (Doc, world) {
  // our new tags
  world.addTags(tags)
  // run our tagger
  world.postProcess(tagger)
  /**  */
  class Sentences extends Doc {
    constructor(list, from, w) {
      list = list.map((p) => p.clone(true))
      super(list, from, w)
    }
  }
  // add some aliases
  methods.questions = methods.isQuestion
  methods.exclamations = methods.isExclamation
  methods.statements = methods.isStatement
  // keep backups of these methods
  methods._prepend = Sentences.prototype.prepend
  methods._append = Sentences.prototype.append
  methods._json = Sentences.prototype.json
  Object.assign(Sentences.prototype, methods)

  /** create a new Sentences object */
  Sentences.prototype.buildFrom = function (list) {
    list = list.map((p) => p.clone(true))
    let doc = new Sentences(list, this, this.world)
    return doc
  }
  /** create a new Doc object */
  Sentences.prototype.toDoc = function () {
    return Doc.prototype.buildFrom(this.list)
  }

  /** overload original sentences() method and return Sentence class**/
  Doc.prototype.sentences = function (n) {
    let arr = []
    this.list.forEach((p) => {
      arr.push(p.fullSentence())
    })
    let s = new Sentences(arr, this, this.world)
    if (typeof n === 'number') {
      s = s.get(n)
    }
    return s
  }
  return Doc
}
module.exports = plugin


text:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.design.widget.TabLayout
        android:id="@+id/food_kind_tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        android:minHeight="?attr/actionBarSize"
        app:backgroundTintMode="multiply"
        app:tabIndicatorColor="@color/white"
        app:tabMode="scrollable"
        app:tabSelectedTextColor="@color/white"
        app:tabTextColor="@color/gray"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/food_kind_viewpager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </android.support.v4.view.ViewPager>

</LinearLayout>


text:
ACCEPTED

#### According to
Index Fungorum

#### Published in
Atti R. Accad. Fisiocrit. Siena, Sér. 4 8: 64 (1896)

#### Original name
Diplodia saccardiana Tassi

### Remarks
null


text:
package com.superweather.app.util;

public interface HttpCallbackListener {
	void onFinish(String response);

	void onError(Exception e);
}


text:
namespace eanim
{

class TranslateSpriteState : public ee::TranslateSpriteState
{
public:
	TranslateSpriteState(ee::SpriteSelection* selection, const sm::vec2& first_pos);

protected:
	virtual void Translate(const sm::vec2& offset) override;

}; // TranslateSpriteState

}

#endif // _EASYANIM_TRANSLATE_SPRITE_STATE_H_


text:
from setuptools import setup

def get_version():
    return __import__('clonegithub').__version__

setup(
    name='clonegithub',
    version=get_version(),
    description='Clona repositórios do GitHub de um usuário',
    author='Eduardo Klosowski',
    author_email='eduardo_klosowski@yahoo.com',
    license='MIT',
    py_modules=['clonegithub'],
    zip_safe=False,
    entry_points={
        'console_scripts': [
            'clonegithub = clonegithub:main_exec',
        ],
    },
)


text:
package com.microsoft.azure.management.datafactory.v2018_06_01;

import java.util.Collection;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.microsoft.rest.ExpandableStringEnum;

/**
 * Defines values for TeradataPartitionOption.
 */
public final class TeradataPartitionOption extends ExpandableStringEnum<TeradataPartitionOption> {
    /** Static value None for TeradataPartitionOption. */
    public static final TeradataPartitionOption NONE = fromString("None");

    /** Static value Hash for TeradataPartitionOption. */
    public static final TeradataPartitionOption HASH = fromString("Hash");

    /** Static value DynamicRange for TeradataPartitionOption. */
    public static final TeradataPartitionOption DYNAMIC_RANGE = fromString("DynamicRange");

    /**
     * Creates or finds a TeradataPartitionOption from its string representation.
     * @param name a name to look for
     * @return the corresponding TeradataPartitionOption
     */
    @JsonCreator
    public static TeradataPartitionOption fromString(String name) {
        return fromString(name, TeradataPartitionOption.class);
    }

    /**
     * @return known TeradataPartitionOption values
     */
    public static Collection<TeradataPartitionOption> values() {
        return values(TeradataPartitionOption.class);
    }
}


text:
Reports JUnit 4.0 @BeforeClass or @AfterClass method
is not declared
public static, does not return void, or takes arguments.
Such methods are easy to create inadvertently, and will not be executed by JUnit tests runners.


text:
<?php

namespace Rascal;

use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use Rascal\NodeVisitor\NameResolver as NameResolverRascal;
use PhpParser\Parser;
use PhpParser\Lexer;
use PhpParser\ParserFactory;

if (!class_exists('Autoloader'))
    require_once __DIR__ . '/../bootstrap.php';

ini_set('xdebug.max_nesting_level', 10000);

if (count($argv) < 2) {
    echo "Expected at least 1 argument\n";
    exit(-1);
}

if (!isset($opts))
    $opts = getopt("f:lslirp:n:d:", array(
        "file:",
        "enableLocations",
        "addDecl",
        "uniqueIds",
        "relativeLocations",
        "prefix:",
        "phpdocs",
        "resolveNames",
        "projectName:",
        "projectDir:",
    ));

if (isset($opts["f"]))
    $file = $opts["f"];
else
    if (isset($opts["file"]))
        $file = $opts["file"];
    else
        if (count($argv) == 2) {
            $file = $argv[1];
        } else {
            echo "errscript(\"The file must be provided using either -f or --file\")";
            exit(-1);
        }

$usesProjectLoc = false;
if (isset($opts["projectName"]) && isset($opts["projectDir"])) {
    $projectName = $opts["projectName"];
    $projectDir = $opts["projectDir"];
    $usesProjectLoc = true;
} elseif (isset($opts["n"]) && isset($opts["d"])) {
    $projectName = $opts["n"];
    $projectDir = $opts["d"];
    $usesProjectLoc = true;
} else {
    $projectName = "";
    $projectDir = "";
}

$enableLocations = false;
if (isset($opts["l"]) || isset($opts["enableLocations"]))
    $enableLocations = true;

$addDeclarations = false;
if (isset($opts["addDecl"]))
    $addDeclarations = true;

$uniqueIds = false;
if (isset($opts["i"]) || isset($opts["uniqueIds"]))
    $uniqueIds = true;

if (isset($opts["p"]))
    $prefix = $opts["p"] . '.';
else
    if (isset($opts["prefix"]))
        $prefix = $opts["prefix"] . '.';
    else {
        $prefix = "";
    }

$relativeLocations = false;
if (isset($opts["r"]) || isset($opts["relativeLocations"]))
    $relativeLocations = true;

if ($usesProjectLoc && $relativeLocations) {
    echo "errscript(\"Cannot use a project location and relative locations at the same time\")";
    exit(-1);
}

$addPHPDocs = false;
if (isset($opts["phpdocs"]))
  $addPHPDocs = true;

if (isset($_SERVER['HOME'])) {
    $homedir = $_SERVER['HOME'];
} else {
    $homedir = $_SERVER['HOMEDRIVE'] . $_SERVER['HOMEPATH'];
}

$inputCode = '';
if (! $relativeLocations && ! $usesProjectLoc && file_exists($file)) {
    $inputCode = file_get_contents($file);
} elseif ($usesProjectLoc && file_exists($projectDir . $file)) {
    $inputCode = file_get_contents($projectDir . $file);
} elseif ($relativeLocations && file_exists($homedir . $file)) {
    $inputCode = file_get_contents($homedir . $file);
} else {
    echo "errscript(\"The given file, $file, does not exist, 1 is $projectDir and 2 is $projectName and 3 is $usesProjectLoc\")";
    exit(-1);
}

$resolveNames = isset($opts['resolveNames']) ? true : false;

$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
$printer = new RascalPrinter($file, $enableLocations, $relativeLocations, $uniqueIds, $prefix, $projectName, $addPHPDocs, $addDeclarations);

try {
    $parseTree = $parser->parse($inputCode);

    if ($resolveNames) {
        $traverser = new NodeTraverser;
        $traverser->addVisitor(new NameResolver);
        $traverser->addVisitor(new NameResolverRascal);
        $traverser->traverse($parseTree);
    }

    $stmts = array();

    foreach ($parseTree as $stmt)
        $stmts[] = $printer->pprint($stmt);

    $script = implode(",\n", $stmts);

    echo sprintf("script([%s])", $script);

} catch (\PhpParser\Error $e) {
    echo "errscript(\"" . $printer->rascalizeString($e->getMessage()) . "\")";
} catch (\Exception $e) {
    echo "errscript(\"" . $printer->rascalizeString($e->getMessage()) . "\")";
}


text:
CREATE FUNCTION levenshtein_distance(text, text) RETURNS INT
AS 'libplayground', 'levenshtein_distance' LANGUAGE C STRICT;
CREATE FUNCTION jaccard_index(text, text) RETURNS DOUBLE PRECISION
AS 'libplayground', 'jaccard_index' LANGUAGE C STRICT;


text:
#ifndef __AUTORELEASEPOOL_H__
#define __AUTORELEASEPOOL_H__

#include <vector>
#include <string>
#include "base/CCRef.h"

NS_CC_BEGIN

/**
 * @addtogroup base_nodes
 * @{
 */

class CC_DLL AutoreleasePool
{
public:
    /**
     * @warn Don't create an auto release pool in heap, create it in stack.
     * @js NA
     * @lua NA
     */
    AutoreleasePool();

    /**
     * Create an autorelease pool with specific name. This name is useful for debugging.
     */
    AutoreleasePool(const std::string &name);

    /**
     * @js NA
     * @lua NA
     */
    ~AutoreleasePool();

    /**
     * Add a given object to this pool.
     *
     * The same object may be added several times to the same pool; When the
     * pool is destructed, the object's Ref::release() method will be called
     * for each time it was added.
     *
     * @param object    The object to add to the pool.
     * @js NA
     * @lua NA
     */
    void addObject(Ref *object);

    /**
     * Clear the autorelease pool.
     *
     * Ref::release() will be called for each time the managed object is
     * added to the pool.
     * @js NA
     * @lua NA
     */
    void clear();

#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    /**
     * Whether the pool is doing `clear` operation.
     */
    bool isClearing() const { return _isClearing; };
#endif

    /**
     * Checks whether the pool contains the specified object.
     */
    bool contains(Ref* object) const;

    /**
     * Dump the objects that are put into autorelease pool. It is used for debugging.
     *
     * The result will look like:
     * Object pointer address     object id     reference count
     *
     */
    void dump();

private:
    /**
     * The underlying array of object managed by the pool.
     *
     * Although Array retains the object once when an object is added, proper
     * Ref::release() is called outside the array to make sure that the pool
     * does not affect the managed object's reference count. So an object can
     * be destructed properly by calling Ref::release() even if the object
     * is in the pool.
     */
    std::vector<Ref*> _managedObjectArray;
    std::string _name;

#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    /**
     *  The flag for checking whether the pool is doing `clear` operation.
     */
    bool _isClearing;
#endif
};

class CC_DLL PoolManager
{
public:
    /**
     * @js NA
     * @lua NA
     */
    CC_DEPRECATED_ATTRIBUTE static PoolManager* sharedPoolManager() { return getInstance(); }
    static PoolManager* getInstance();

    /**
     * @js NA
     * @lua NA
     */
    CC_DEPRECATED_ATTRIBUTE static void purgePoolManager() { destroyInstance(); }
    static void destroyInstance();

    /**
     * Get current auto release pool, there is at least one auto release pool that created by engine.
     * You can create your own auto release pool at demand, which will be put into auto releae pool stack.
     */
    AutoreleasePool *getCurrentPool() const;

    bool isObjectInPools(Ref* obj) const;

    /**
     * @js NA
     * @lua NA
     */
    friend class AutoreleasePool;

private:
    PoolManager();
    ~PoolManager();

    void push(AutoreleasePool *pool);
    void pop();

    static PoolManager* s_singleInstance;

    std::vector<AutoreleasePool*> _releasePoolStack;
};

// end of base_nodes group
/// @}

NS_CC_END

#endif //__AUTORELEASEPOOL_H__


text:
package org.ethereum.vm;

/**
 * @author Roman Mandeleil
 * @since 03.07.2014
 */
public class CallCreate {

    final byte[] data;
    final byte[] destination;
    final byte[] gasLimit;
    final byte[] value;

    public CallCreate(byte[] data, byte[] destination, byte[] gasLimit, byte[] value) {
        this.data = data;
        this.destination = destination;
        this.gasLimit = gasLimit;
        this.value = value;
    }

    public byte[] getData() {
        return data;
    }

    public byte[] getDestination() {
        return destination;
    }

    public byte[] getGasLimit() {
        return gasLimit;
    }

    public byte[] getValue() {
        return value;
    }
}


text:
namespace Machete.Parsers
{
    public class PeekParser<TInput, TResult> :
        IParser<TInput, TResult>
    {
        readonly IParser<TInput, TResult> _parser;

        public PeekParser(IParser<TInput, TResult> parser)
        {
            _parser = parser;
        }

        public Result<Cursor<TInput>, TResult> Parse(Cursor<TInput> input)
        {
            Result<Cursor<TInput>, TResult> result = _parser.Parse(input);
            if (result.HasResult)
                return new Success<Cursor<TInput>, TResult>(result.Result, input);

            return new Unmatched<Cursor<TInput>, TResult>(input);
        }
    }
}


text:
// -*- c++ -*-
#ifndef _GLIBMM_RANDOM_H
#define _GLIBMM_RANDOM_H

/* $Id$ */

/* random.h
 *
 * Copyright (C) 2002 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <glib.h>

extern "C" { typedef struct _GRand GRand; }

namespace Glib
{

/** @defgroup Random Random Numbers
 * Pseudo random number generator.
 * @{
 */

class Rand
{
public:
  Rand();
  explicit Rand(guint32 seed);
  ~Rand();

  void set_seed(guint32 seed);

  bool get_bool();

  guint32 get_int();
  gint32  get_int_range(gint32 begin, gint32 end);

  double get_double();
  double get_double_range(double begin, double end);

  GRand*       gobj()       { return gobject_; }
  const GRand* gobj() const { return gobject_; }

private:
  GRand* gobject_;

  // noncopyable
  Rand(const Rand&);
  Rand& operator=(const Rand&);
};

/** @} group Random */

} // namespace Glib

#endif /* _GLIBMM_RANDOM_H */


text:
#include <sdbusplus/bus.hpp>

#include <cstdint>
#include <iostream>

/** An example dbus client application.
 *  Calls org.freedesktop.login1's ListUsers interface to find all active
 *  users in the system and displays their username.
 */

int main()
{
    using namespace sdbusplus;

    auto b = bus::new_default_system();
    auto m = b.new_method_call("org.freedesktop.login1",
                               "/org/freedesktop/login1",
                               "org.freedesktop.login1.Manager", "ListUsers");
    auto reply = b.call(m);

    using return_type =
        std::vector<std::tuple<uint32_t, std::string, message::object_path>>;
    auto users = reply.unpack<return_type>();

    for (auto& user : users)
    {
        std::cout << std::get<std::string>(user) << "\n";
    }

    return 0;
}