File: ukm_url_recorder_unittest.mm

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (190 lines) | stat: -rw-r--r-- 7,124 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/ukm/ios/ukm_url_recorder.h"

#include <optional>

#include "base/functional/bind.h"
#import "base/test/ios/wait_util.h"
#include "components/ukm/test_ukm_recorder.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/test/fakes/fake_navigation_context.h"
#import "ios/web/public/test/web_test_with_web_state.h"
#import "ios/web/public/web_state.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "url/gurl.h"

namespace ukm {
namespace {

std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
    const net::test_server::HttpRequest& request) {
  if (request.GetURL().path() == "/title1.html") {
    auto result = std::make_unique<net::test_server::BasicHttpResponse>();
    result->set_content_type("text/html");
    result->set_content("<html><head></head><body>NoTitle</body></html>");
    return std::move(result);
  }
  if (request.GetURL().path() == "/page_with_iframe.html") {
    auto result = std::make_unique<net::test_server::BasicHttpResponse>();
    result->set_content_type("text/html");
    result->set_content(
        "<html><head></head><body><iframe src=\"title1.html\"></body></html>");
    return std::move(result);
  }
  if (request.GetURL().path() == "/download") {
    auto result = std::make_unique<net::test_server::BasicHttpResponse>();
    result->set_content_type("application/vnd.test");
    result->set_content("TestDownloadContent");
    return std::move(result);
  }
  if (request.GetURL().path() == "/redirect") {
    auto result = std::make_unique<net::test_server::BasicHttpResponse>();
    result->set_code(net::HTTP_MOVED_PERMANENTLY);
    result->AddCustomHeader("Location", "/title1.html");
    return std::move(result);
  }
  return nullptr;
}

}  // namespace

class UkmUrlRecorderTest : public web::WebTestWithWebState {
 protected:
  UkmUrlRecorderTest() {
    server_.RegisterDefaultHandler(base::BindRepeating(&HandleRequest));
  }

  void SetUp() override {
    web::WebTestWithWebState::SetUp();
    ASSERT_TRUE(server_.Start());
    ukm::InitializeSourceUrlRecorderForWebState(web_state());
  }

  bool LoadUrlAndWait(const GURL& url) {
    web::NavigationManager::WebLoadParams params(url);
    web_state()->GetNavigationManager()->LoadURLWithParams(params);
    return base::test::ios::WaitUntilConditionOrTimeout(
        base::test::ios::kWaitForPageLoadTimeout, ^{
          return !web_state()->IsLoading();
        });
  }

  void MaybeRecordUrl(web::NavigationContext* context, const GURL& url) {
    internal::SourceUrlRecorderWebStateObserver* observer =
        GetSourceUrlRecorderForWebStateForWebState(web_state());
    observer->MaybeRecordUrl(context, url);
  }

  testing::AssertionResult RecordedUrl(
      ukm::SourceId source_id,
      GURL expected_url,
      std::optional<GURL> expected_initial_url) {
    auto* source = test_ukm_recorder_.GetSourceForSourceId(source_id);
    if (!source)
      return testing::AssertionFailure() << "No URL recorded";
    if (source->url() != expected_url)
      return testing::AssertionFailure()
             << "Url was " << source->url() << ", expected: " << expected_url;
    std::optional<GURL> initial_url;
    if (source->urls().size() > 1u)
      initial_url = source->urls().front();
    if (expected_initial_url != initial_url) {
      return testing::AssertionFailure()
             << "Initial Url was " << initial_url.value_or(GURL())
             << ", expected: " << expected_initial_url.value_or(GURL());
    }
    return testing::AssertionSuccess();
  }

  testing::AssertionResult DidNotRecordUrl(GURL url) {
    const auto& sources = test_ukm_recorder_.GetSources();
    for (const auto& kv : sources) {
      if (kv.second->url() == url)
        return testing::AssertionFailure()
               << "Url " << url << " was recorded with SourceId: " << kv.first;
      if (kv.second->url() == url)
        return testing::AssertionFailure()
               << "Url " << url
               << " was recorded as an initial URL with SourceId: " << kv.first;
    }
    return testing::AssertionSuccess();
  }

 protected:
  net::EmbeddedTestServer server_;
  ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
};

// Tests that URLs get recorded for pages visited.
TEST_F(UkmUrlRecorderTest, Basic) {
  GURL url = server_.GetURL("/title1.html");
  EXPECT_TRUE(LoadUrlAndWait(url));
  ukm::SourceId source_id = ukm::GetSourceIdForWebStateDocument(web_state());
  EXPECT_TRUE(RecordedUrl(source_id, url, std::nullopt));
}

// Tests that subframe URLs do not get recorded.
TEST_F(UkmUrlRecorderTest, IgnoreUrlInSubframe) {
  GURL main_url = server_.GetURL("/page_with_iframe.html");
  GURL subframe_url = server_.GetURL("/title1.html");
  EXPECT_TRUE(LoadUrlAndWait(main_url));
  ukm::SourceId source_id = ukm::GetSourceIdForWebStateDocument(web_state());
  EXPECT_TRUE(RecordedUrl(source_id, main_url, std::nullopt));
  EXPECT_TRUE(DidNotRecordUrl(subframe_url));
}

// Tests that download URLs do not get recorded.
TEST_F(UkmUrlRecorderTest, IgnoreDownloadUrl) {
  GURL url = server_.GetURL("/download");
  EXPECT_TRUE(LoadUrlAndWait(url));
  EXPECT_TRUE(DidNotRecordUrl(url));
}

// Tests that redirected URLs record initial and final URL.
TEST_F(UkmUrlRecorderTest, InitialUrl) {
  GURL redirect_url = server_.GetURL("/redirect");
  GURL target_url = server_.GetURL("/title1.html");
  EXPECT_TRUE(LoadUrlAndWait(redirect_url));
  ukm::SourceId source_id = ukm::GetSourceIdForWebStateDocument(web_state());
  EXPECT_TRUE(RecordedUrl(source_id, target_url, redirect_url));
}

// Checks that if a NavigationContext is erroneously reused, its reuse is
// handled gracefully by the UKM recorder. See crbug.com/346017703.
TEST_F(UkmUrlRecorderTest, ReusedNavigationContext) {
  EXPECT_EQ(0u, test_ukm_recorder_.sources_count());

  web::FakeNavigationContext context_1;
  const int64_t navigation_id_1 = context_1.GetNavigationId();
  const GURL url_1("https://example.com#foo");
  context_1.SetIsSameDocument(false);
  context_1.SetUrl(url_1);

  MaybeRecordUrl(&context_1, url_1);
  EXPECT_EQ(1u, test_ukm_recorder_.sources_count());

  web::FakeNavigationContext context_2;
  const int64_t navigation_id_2 = context_2.GetNavigationId();
  const GURL url_2("https://example.com#bar");
  context_2.SetIsSameDocument(false);
  context_2.SetUrl(url_2);

  EXPECT_NE(navigation_id_1, navigation_id_2);

  MaybeRecordUrl(&context_2, url_2);
  EXPECT_EQ(2u, test_ukm_recorder_.sources_count());

  // If a NavigationContext is reused, UKM recorder should not crash on
  // receiving the same navigation id, hence also the UKM source id, again.
  // Instead no new source should be added to the UKM recorder.
  MaybeRecordUrl(&context_1, url_1);
  EXPECT_EQ(2u, test_ukm_recorder_.sources_count());
}

}  // namespace ukm