resource_fetcher_unittest.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "webkit/glue/resource_fetcher.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/callback.h"
83345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/message_loop.h"
972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/timer.h"
1072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
1172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h"
1272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "webkit/glue/unittest_test_server.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "webkit/tools/test_shell/test_shell_test.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing WebKit::WebFrame;
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing WebKit::WebURLResponse;
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing webkit_glue::ResourceFetcher;
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing webkit_glue::ResourceFetcherWithTimeout;
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass ResourceFetcherTests : public TestShellTest {
253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick protected:
263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  UnittestTestServer test_server_;
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kMaxWaitTimeMs = 5000;
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass FetcherDelegate {
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FetcherDelegate()
3472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      : completed_(false),
3572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        timed_out_(false) {
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Start a repeating timer waiting for the download to complete.  The
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // callback has to be a static function, so we hold on to our instance.
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FetcherDelegate::instance_ = this;
3972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    StartTimer();
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ResourceFetcher::Callback* NewCallback() {
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return ::NewCallback(this, &FetcherDelegate::OnURLFetchComplete);
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
4672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  virtual void OnURLFetchComplete(const WebURLResponse& response,
4772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                  const std::string& data) {
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    response_ = response;
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    data_ = data;
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    completed_ = true;
5172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    timer_.Stop();
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    MessageLoop::current()->Quit();
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool completed() const { return completed_; }
5672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  bool timed_out() const { return timed_out_; }
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string data() const { return data_; }
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const WebURLResponse& response() const { return response_; }
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Wait for the request to complete or timeout.  We use a loop here b/c the
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // testing infrastructure (test_shell) can generate spurious calls to the
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // MessageLoop's Quit method.
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void WaitForResponse() {
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    while (!completed() && !timed_out())
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      MessageLoop::current()->Run();
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  void StartTimer() {
7072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    timer_.Start(base::TimeDelta::FromMilliseconds(kMaxWaitTimeMs),
7172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                 this,
7272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                 &FetcherDelegate::TimerFired);
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void TimerFired() {
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ASSERT_FALSE(completed_);
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    timed_out_ = true;
7972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    MessageLoop::current()->Quit();
8072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    FAIL() << "fetch timed out";
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  static FetcherDelegate* instance_;
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
8672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::OneShotTimer<FetcherDelegate> timer_;
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool completed_;
8872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  bool timed_out_;
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  WebURLResponse response_;
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string data_;
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochFetcherDelegate* FetcherDelegate::instance_ = NULL;
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Test a fetch from the test server.
96731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Flaky, http://crbug.com/51622.
97731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickTEST_F(ResourceFetcherTests, FLAKY_ResourceFetcherDownload) {
983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(test_server_.Start());
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  WebFrame* frame = test_shell_->webView()->mainFrame();
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  GURL url(test_server_.GetURL("files/test_shell/index.html"));
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      url, frame, delegate->NewCallback()));
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  delegate->WaitForResponse();
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ASSERT_TRUE(delegate->completed());
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(delegate->response().httpStatusCode(), 200);
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string text = delegate->data();
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_TRUE(text.find("What is this page?") != std::string::npos);
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Test 404 response.
1153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  url = test_server_.GetURL("files/thisfiledoesntexist.html");
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  delegate.reset(new FetcherDelegate);
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  fetcher.reset(new ResourceFetcher(url, frame, delegate->NewCallback()));
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  delegate->WaitForResponse();
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ASSERT_TRUE(delegate->completed());
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(delegate->response().httpStatusCode(), 404);
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_TRUE(delegate->data().find("Not Found.") != std::string::npos);
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
126731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Flaky, http://crbug.com/51622.
127731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickTEST_F(ResourceFetcherTests, FLAKY_ResourceFetcherDidFail) {
1283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(test_server_.Start());
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  WebFrame* frame = test_shell_->webView()->mainFrame();
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Try to fetch a page on a site that doesn't exist.
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GURL url("http://localhost:1339/doesnotexist");
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      url, frame, delegate->NewCallback()));
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  delegate->WaitForResponse();
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // When we fail, we still call the Delegate callback but we pass in empty
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // values.
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_TRUE(delegate->completed());
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_TRUE(delegate->response().isNull());
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(delegate->data(), std::string());
14572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_FALSE(delegate->timed_out());
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTEST_F(ResourceFetcherTests, ResourceFetcherTimeout) {
1493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(test_server_.Start());
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  WebFrame* frame = test_shell_->webView()->mainFrame();
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Grab a page that takes at least 1 sec to respond, but set the fetcher to
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // timeout in 0 sec.
1553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  GURL url(test_server_.GetURL("slow?1"));
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout(
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      url, frame, 0, delegate->NewCallback()));
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  delegate->WaitForResponse();
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // When we timeout, we still call the Delegate callback but we pass in empty
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // values.
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_TRUE(delegate->completed());
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_TRUE(delegate->response().isNull());
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(delegate->data(), std::string());
16772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_FALSE(delegate->timed_out());
16872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
16972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
17072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenclass EvilFetcherDelegate : public FetcherDelegate {
17172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen public:
17272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  void SetFetcher(ResourceFetcher* fetcher) {
17372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    fetcher_.reset(fetcher);
17472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
17572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
17672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  void OnURLFetchComplete(const WebURLResponse& response,
17772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                          const std::string& data) {
17872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // Destroy the ResourceFetcher here.  We are testing that upon returning
17972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // to the ResourceFetcher that it does not crash.
18072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    fetcher_.reset();
18172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    FetcherDelegate::OnURLFetchComplete(response, data);
18272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
18372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
18472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen private:
18572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  scoped_ptr<ResourceFetcher> fetcher_;
18672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen};
18772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
18872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenTEST_F(ResourceFetcherTests, ResourceFetcherDeletedInCallback) {
18972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(test_server_.Start());
19072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
19172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  WebFrame* frame = test_shell_->webView()->mainFrame();
19272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Grab a page that takes at least 1 sec to respond, but set the fetcher to
19472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // timeout in 0 sec.
19572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  GURL url(test_server_.GetURL("slow?1"));
19672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  scoped_ptr<EvilFetcherDelegate> delegate(new EvilFetcherDelegate);
19772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout(
19872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      url, frame, 0, delegate->NewCallback()));
19972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  delegate->SetFetcher(fetcher.release());
20072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
20172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  delegate->WaitForResponse();
20272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_FALSE(delegate->timed_out());
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
206