Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: chrome/worker/worker_uitest.cc

Issue 147125: Support running http layout tests for workers in UI test framework. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « DEPS ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/file_path.h" 5 #include "base/file_path.h"
6 #include "base/file_util.h" 6 #include "base/file_util.h"
7 #include "base/path_service.h" 7 #include "base/path_service.h"
8 #include "base/string_util.h" 8 #include "base/string_util.h"
9 #include "chrome/browser/worker_host/worker_service.h" 9 #include "chrome/browser/worker_host/worker_service.h"
10 #include "chrome/common/chrome_paths.h" 10 #include "chrome/common/chrome_paths.h"
(...skipping 19 matching lines...) Expand all
30 const int kTestIntervalMs = 250; 30 const int kTestIntervalMs = 250;
31 const int kTestWaitTimeoutMs = 30 * 1000; 31 const int kTestWaitTimeoutMs = 30 * 1000;
32 32
33 class WorkerTest : public UITest { 33 class WorkerTest : public UITest {
34 protected: 34 protected:
35 WorkerTest(); 35 WorkerTest();
36 virtual ~WorkerTest(); 36 virtual ~WorkerTest();
37 37
38 void RunTest(const std::wstring& test_case); 38 void RunTest(const std::wstring& test_case);
39 39
40 void InitializeForLayoutTest(const FilePath& test_case_dir); 40 void InitializeForLayoutTest(const FilePath& test_parent_dir,
41 void RunLayoutTest(const std::string& test_case_file_name); 41 const FilePath& test_case_dir,
42 void RunHttpTest(const std::string& test_case_name); 42 bool for_http);
Dmitry Titov 2009/06/25 18:09:13 bool vars are usually named using "is_foo" pattern
43 void RunLayoutTest(const std::string& test_case_file_name, bool for_http);
43 44
44 private: 45 protected:
45 bool ReadExpectedResult(const FilePath& result_dir_path, 46 bool ReadExpectedResult(const FilePath& result_dir_path,
46 const std::string test_case_file_name, 47 const std::string test_case_file_name,
47 std::string* expected_result_value); 48 std::string* expected_result_value);
48 49
49 bool initialized_for_layout_test_; 50 bool initialized_for_layout_test_;
50 int test_count_; 51 int test_count_;
51 FilePath temp_test_dir_; 52 FilePath temp_test_dir_;
52 FilePath layout_test_dir_; 53 FilePath layout_test_dir_;
54 FilePath test_case_dir_;
55 FilePath new_http_root_dir_;
53 FilePath new_layout_test_dir_; 56 FilePath new_layout_test_dir_;
54 FilePath rebase_result_dir_; 57 FilePath rebase_result_dir_;
55 std::string layout_test_controller_; 58 std::string layout_test_controller_;
56 }; 59 };
57 60
58 WorkerTest::WorkerTest() 61 WorkerTest::WorkerTest()
59 : UITest(), initialized_for_layout_test_(false), test_count_(0) { 62 : UITest(), initialized_for_layout_test_(false), test_count_(0) {
60 launch_arguments_.AppendSwitch(switches::kEnableWebWorkers); 63 launch_arguments_.AppendSwitch(switches::kEnableWebWorkers);
61 } 64 }
62 65
63 WorkerTest::~WorkerTest() { 66 WorkerTest::~WorkerTest() {
64 if (!temp_test_dir_.empty()) 67 if (!temp_test_dir_.empty())
65 file_util::Delete(temp_test_dir_, true); 68 file_util::Delete(temp_test_dir_, true);
66 } 69 }
67 70
68 void WorkerTest::RunTest(const std::wstring& test_case) { 71 void WorkerTest::RunTest(const std::wstring& test_case) {
69 scoped_refptr<TabProxy> tab(GetActiveTab()); 72 scoped_refptr<TabProxy> tab(GetActiveTab());
70 73
71 GURL url = GetTestUrl(L"workers", test_case); 74 GURL url = GetTestUrl(L"workers", test_case);
72 ASSERT_TRUE(tab->NavigateToURL(url)); 75 ASSERT_TRUE(tab->NavigateToURL(url));
73 76
74 std::string value = WaitUntilCookieNonEmpty(tab.get(), url, 77 std::string value = WaitUntilCookieNonEmpty(tab.get(), url,
75 kTestCompleteCookie, kTestIntervalMs, kTestWaitTimeoutMs); 78 kTestCompleteCookie, kTestIntervalMs, kTestWaitTimeoutMs);
76 ASSERT_STREQ(kTestCompleteSuccess, value.c_str()); 79 ASSERT_STREQ(kTestCompleteSuccess, value.c_str());
77 } 80 }
78 81
79 void WorkerTest::RunHttpTest(const std::string& test_case_name) { 82 void WorkerTest::InitializeForLayoutTest(const FilePath& test_parent_dir,
80 scoped_refptr<TabProxy> tab(GetActiveTab()); 83 const FilePath& test_case_dir,
81 84 bool for_http) {
82 std::string test_url_string(std::string("http://localhost:8080/") +
83 test_case_name);
84 GURL url(test_url_string);
85 ASSERT_TRUE(tab->NavigateToURL(url));
86
87 std::string value = WaitUntilCookieNonEmpty(tab.get(), url,
88 kTestCompleteCookie, kTestIntervalMs, kTestWaitTimeoutMs);
89 ASSERT_STREQ(kTestCompleteSuccess, value.c_str());
90 }
91
92 void WorkerTest::InitializeForLayoutTest(const FilePath& test_case_dir) {
93 FilePath src_dir; 85 FilePath src_dir;
94 PathService::Get(base::DIR_SOURCE_ROOT, &src_dir); 86 PathService::Get(base::DIR_SOURCE_ROOT, &src_dir);
95 87
96 // Gets the file path to WebKit layout tests for workers, that is, 88 // Gets the file path to WebKit layout tests for workers, that is,
97 // chrome/test/data/workers/LayoutTests/.../workers 89 // chrome/test/data/workers/LayoutTests/.../workers
98 // Note that we have to use our copy of WebKit layout tests for workers. 90 // Note that we have to use our copy of WebKit layout tests for workers.
99 // This is because our build machines do not have WebKit layout tests added. 91 // This is because our build machines do not have WebKit layout tests added.
100 layout_test_dir_ = src_dir.AppendASCII("chrome"); 92 layout_test_dir_ = src_dir.AppendASCII("chrome");
101 layout_test_dir_ = layout_test_dir_.AppendASCII("test"); 93 layout_test_dir_ = layout_test_dir_.AppendASCII("test");
102 layout_test_dir_ = layout_test_dir_.AppendASCII("data"); 94 layout_test_dir_ = layout_test_dir_.AppendASCII("data");
103 layout_test_dir_ = layout_test_dir_.AppendASCII("workers"); 95 layout_test_dir_ = layout_test_dir_.AppendASCII("workers");
96 layout_test_dir_ = layout_test_dir_.Append(test_parent_dir);
104 layout_test_dir_ = layout_test_dir_.Append(test_case_dir); 97 layout_test_dir_ = layout_test_dir_.Append(test_case_dir);
105 98
99 // If not found, try to use the original copy of WebKit layout tests for
100 // workers. For testing only in local machine only.
101 // webkit/data/layout_tests/LayoutTests/.../workers
102 if (!file_util::DirectoryExists(layout_test_dir_)) {
103 layout_test_dir_ = src_dir.AppendASCII("webkit");
104 layout_test_dir_ = layout_test_dir_.AppendASCII("data");
105 layout_test_dir_ = layout_test_dir_.AppendASCII("layout_tests");
106 layout_test_dir_ = layout_test_dir_.Append(test_parent_dir);
107 layout_test_dir_ = layout_test_dir_.Append(test_case_dir);
108 ASSERT_TRUE(file_util::DirectoryExists(layout_test_dir_));
109 }
110
106 // Gets the file path to rebased expected result directory for workers. 111 // Gets the file path to rebased expected result directory for workers.
107 // webkit/data/layout_tests/platform/chromium_***/LayoutTests/.../workers 112 // webkit/data/layout_tests/platform/chromium_***/LayoutTests/.../workers
108 rebase_result_dir_ = src_dir.AppendASCII("webkit"); 113 rebase_result_dir_ = src_dir.AppendASCII("webkit");
109 rebase_result_dir_ = rebase_result_dir_.AppendASCII("data"); 114 rebase_result_dir_ = rebase_result_dir_.AppendASCII("data");
110 rebase_result_dir_ = rebase_result_dir_.AppendASCII("layout_tests"); 115 rebase_result_dir_ = rebase_result_dir_.AppendASCII("layout_tests");
111 rebase_result_dir_ = rebase_result_dir_.AppendASCII("platform"); 116 rebase_result_dir_ = rebase_result_dir_.AppendASCII("platform");
112 rebase_result_dir_ = rebase_result_dir_.AppendASCII(kPlatformName); 117 rebase_result_dir_ = rebase_result_dir_.AppendASCII(kPlatformName);
118 rebase_result_dir_ = rebase_result_dir_.Append(test_parent_dir);
113 rebase_result_dir_ = rebase_result_dir_.Append(test_case_dir); 119 rebase_result_dir_ = rebase_result_dir_.Append(test_case_dir);
114 120
115 // Creates the temporary directory. 121 // Creates the temporary directory.
116 ASSERT_TRUE(file_util::CreateNewTempDirectory( 122 ASSERT_TRUE(file_util::CreateNewTempDirectory(
117 FILE_PATH_LITERAL("chrome_worker_test_"), &temp_test_dir_)); 123 FILE_PATH_LITERAL("chrome_worker_test_"), &temp_test_dir_));
118 124
119 // Creates the new layout test subdirectory under the temp directory. 125 // Creates the new layout test subdirectory under the temp directory.
120 // Note that we have to mimic the same layout test directory structure, 126 // Note that we have to mimic the same layout test directory structure,
121 // like .../LayoutTests/fast/workers/.... Otherwise those layout tests 127 // like .../LayoutTests/fast/workers/.... Otherwise those layout tests
122 // dealing with location property, like worker-location.html, could fail. 128 // dealing with location property, like worker-location.html, could fail.
123 new_layout_test_dir_ = temp_test_dir_; 129 new_layout_test_dir_ = temp_test_dir_;
130 new_layout_test_dir_ = new_layout_test_dir_.Append(test_parent_dir);
131 if (for_http) {
132 new_http_root_dir_ = new_layout_test_dir_;
133 test_case_dir_ = test_case_dir;
134 }
124 new_layout_test_dir_ = new_layout_test_dir_.Append(test_case_dir); 135 new_layout_test_dir_ = new_layout_test_dir_.Append(test_case_dir);
125 ASSERT_TRUE(file_util::CreateDirectory(new_layout_test_dir_)); 136 ASSERT_TRUE(file_util::CreateDirectory(new_layout_test_dir_));
126 137
127 // Copies the resource subdirectory. 138 // Copies the resource subdirectory.
128 FilePath layout_test_resource_path(layout_test_dir_); 139 FilePath layout_test_resource_path(layout_test_dir_);
129 layout_test_resource_path = 140 layout_test_resource_path =
130 layout_test_resource_path.AppendASCII("resources"); 141 layout_test_resource_path.AppendASCII("resources");
131 FilePath new_layout_test_resource_path(new_layout_test_dir_); 142 FilePath new_layout_test_resource_path(new_layout_test_dir_);
132 new_layout_test_resource_path = 143 new_layout_test_resource_path =
133 new_layout_test_resource_path.AppendASCII("resources"); 144 new_layout_test_resource_path.AppendASCII("resources");
134 ASSERT_TRUE(file_util::DirectoryExists(layout_test_resource_path));
135 ASSERT_TRUE(file_util::CopyDirectory( 145 ASSERT_TRUE(file_util::CopyDirectory(
136 layout_test_resource_path, new_layout_test_resource_path, true)); 146 layout_test_resource_path, new_layout_test_resource_path, true));
137 147
148 // Copies the parent resource subdirectory. This is needed in order to run
149 // http layout tests.
150 if (for_http) {
151 FilePath parent_resource_path(layout_test_dir_.DirName());
152 parent_resource_path = parent_resource_path.AppendASCII("resources");
153 FilePath new_parent_resource_path(new_layout_test_dir_.DirName());
154 new_parent_resource_path =
155 new_parent_resource_path.AppendASCII("resources");
156 ASSERT_TRUE(file_util::CopyDirectory(
157 parent_resource_path, new_parent_resource_path, true));
158 }
159
138 // Reads the layout test controller simulation script. 160 // Reads the layout test controller simulation script.
139 FilePath path; 161 FilePath path;
140 PathService::Get(chrome::DIR_TEST_DATA, &path); 162 PathService::Get(chrome::DIR_TEST_DATA, &path);
141 path = path.AppendASCII("workers"); 163 path = path.AppendASCII("workers");
142 path = path.AppendASCII("layout_test_controller.html"); 164 path = path.AppendASCII("layout_test_controller.html");
143 ASSERT_TRUE(file_util::ReadFileToString(path, &layout_test_controller_)); 165 ASSERT_TRUE(file_util::ReadFileToString(path, &layout_test_controller_));
144 } 166 }
145 167
146 void WorkerTest::RunLayoutTest(const std::string& test_case_file_name) { 168 void WorkerTest::RunLayoutTest(const std::string& test_case_file_name,
169 bool for_http) {
147 SCOPED_TRACE(test_case_file_name.c_str()); 170 SCOPED_TRACE(test_case_file_name.c_str());
148 171
149 ASSERT_TRUE(!layout_test_controller_.empty()); 172 ASSERT_TRUE(!layout_test_controller_.empty());
150 173
151 // Creates a new cookie name. We will have to use a new cookie because 174 // Creates a new cookie name. We will have to use a new cookie because
152 // this function could be called multiple times. 175 // this function could be called multiple times.
153 std::string status_cookie(kTestCompleteCookie); 176 std::string status_cookie(kTestCompleteCookie);
154 status_cookie += IntToString(test_count_); 177 status_cookie += IntToString(test_count_);
155 test_count_++; 178 test_count_++;
156 179
157 // Reads the layout test HTML file. 180 // Reads the layout test HTML file.
158 FilePath test_file_path(layout_test_dir_); 181 FilePath test_file_path(layout_test_dir_);
159 test_file_path = test_file_path.AppendASCII(test_case_file_name); 182 test_file_path = test_file_path.AppendASCII(test_case_file_name);
160 std::string test_html; 183 std::string test_html;
161 ASSERT_TRUE(file_util::ReadFileToString(test_file_path, &test_html)); 184 ASSERT_TRUE(file_util::ReadFileToString(test_file_path, &test_html));
162 185
163 // Injects the layout test controller into the test HTML. 186 // Injects the layout test controller into the test HTML.
164 test_html.insert(0, layout_test_controller_); 187 test_html.insert(0, layout_test_controller_);
165 ReplaceSubstringsAfterOffset( 188 ReplaceSubstringsAfterOffset(
166 &test_html, 0, "%COOKIE%", status_cookie.c_str()); 189 &test_html, 0, "%COOKIE%", status_cookie.c_str());
167 190
168 // Creates the new layout test HTML file. 191 // Creates the new layout test HTML file.
169 FilePath new_test_file_path(new_layout_test_dir_); 192 FilePath new_test_file_path(new_layout_test_dir_);
170 new_test_file_path = new_test_file_path.AppendASCII(test_case_file_name); 193 new_test_file_path = new_test_file_path.AppendASCII(test_case_file_name);
171 ASSERT_TRUE(file_util::WriteFile(new_test_file_path, 194 ASSERT_TRUE(file_util::WriteFile(new_test_file_path,
172 &test_html.at(0), 195 &test_html.at(0),
173 static_cast<int>(test_html.size()))); 196 static_cast<int>(test_html.size())));
174 GURL new_test_file_url = net::FilePathToFileURL(new_test_file_path); 197
198 scoped_ptr<GURL> new_test_url;
199 if (for_http)
200 new_test_url.reset(new GURL(
201 std::string("http://localhost:8080/") +
202 UTF16ToUTF8(test_case_dir_.ToWStringHack()) +
203 "/" +
204 test_case_file_name));
205 else
206 new_test_url.reset(new GURL(net::FilePathToFileURL(new_test_file_path)));
175 207
176 // Runs the new layout test. 208 // Runs the new layout test.
177 scoped_refptr<TabProxy> tab(GetActiveTab()); 209 scoped_refptr<TabProxy> tab(GetActiveTab());
178 ASSERT_TRUE(tab->NavigateToURL(new_test_file_url)); 210 ASSERT_TRUE(tab->NavigateToURL(*new_test_url.get()));
179 std::string escaped_value = 211 std::string escaped_value =
180 WaitUntilCookieNonEmpty(tab.get(), new_test_file_url, 212 WaitUntilCookieNonEmpty(tab.get(), *new_test_url.get(),
181 status_cookie.c_str(), kTestIntervalMs, kTestWaitTimeoutMs); 213 status_cookie.c_str(), kTestIntervalMs, kTestWaitTimeoutMs);
182 214
183 // Unescapes and normalizes the actual result. 215 // Unescapes and normalizes the actual result.
184 std::string value = UnescapeURLComponent(escaped_value, 216 std::string value = UnescapeURLComponent(escaped_value,
185 UnescapeRule::NORMAL | UnescapeRule::SPACES | 217 UnescapeRule::NORMAL | UnescapeRule::SPACES |
186 UnescapeRule::URL_SPECIAL_CHARS | UnescapeRule::CONTROL_CHARS); 218 UnescapeRule::URL_SPECIAL_CHARS | UnescapeRule::CONTROL_CHARS);
187 value += "\n"; 219 value += "\n";
188 ReplaceSubstringsAfterOffset(&value, 0, "\r", ""); 220 ReplaceSubstringsAfterOffset(&value, 0, "\r", "");
189 221
190 // Reads the expected result. First try to read from rebase directory. 222 // Reads the expected result. First try to read from rebase directory.
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
239 "worker-navigator.html", 271 "worker-navigator.html",
240 "worker-replace-global-constructor.html", 272 "worker-replace-global-constructor.html",
241 "worker-replace-self.html", 273 "worker-replace-self.html",
242 "worker-terminate.html", 274 "worker-terminate.html",
243 "worker-timeout.html" 275 "worker-timeout.html"
244 }; 276 };
245 277
246 FilePath fast_test_dir; 278 FilePath fast_test_dir;
247 fast_test_dir = fast_test_dir.AppendASCII("LayoutTests"); 279 fast_test_dir = fast_test_dir.AppendASCII("LayoutTests");
248 fast_test_dir = fast_test_dir.AppendASCII("fast"); 280 fast_test_dir = fast_test_dir.AppendASCII("fast");
249 fast_test_dir = fast_test_dir.AppendASCII("workers"); 281
250 InitializeForLayoutTest(fast_test_dir); 282 FilePath worker_test_dir;
283 worker_test_dir = worker_test_dir.AppendASCII("workers");
284 InitializeForLayoutTest(fast_test_dir, worker_test_dir, false);
251 285
252 for (int i = 0; i < arraysize(kLayoutTestFiles); ++i) 286 for (int i = 0; i < arraysize(kLayoutTestFiles); ++i)
253 RunLayoutTest(kLayoutTestFiles[i]); 287 RunLayoutTest(kLayoutTestFiles[i], false);
288 }
289
290 TEST_F(WorkerTest, WorkerHttpLayoutTests) {
291 static const char* kLayoutTestFiles[] = {
292 //"text-encoding.html",
293 "worker-importScripts.html",
294 "worker-redirect.html",
295 };
296
297 FilePath http_test_dir;
298 http_test_dir = http_test_dir.AppendASCII("LayoutTests");
299 http_test_dir = http_test_dir.AppendASCII("http");
300 http_test_dir = http_test_dir.AppendASCII("tests");
301
302 FilePath worker_test_dir;
303 worker_test_dir = worker_test_dir.AppendASCII("workers");
304 InitializeForLayoutTest(http_test_dir, worker_test_dir, true);
305
306 StartHttpServer(new_http_root_dir_);
307 for (int i = 0; i < arraysize(kLayoutTestFiles); ++i)
308 RunLayoutTest(kLayoutTestFiles[i], true);
309 StopHttpServer();
310 }
311
312 TEST_F(WorkerTest, WorkerXhrHttpLayoutTests) {
313 static const char* kLayoutTestFiles[] = {
314 "abort-exception-assert.html",
315 //"methods-async.html",
316 //"methods.html",
317 "xmlhttprequest-file-not-found.html"
318 };
319
320 FilePath http_test_dir;
321 http_test_dir = http_test_dir.AppendASCII("LayoutTests");
322 http_test_dir = http_test_dir.AppendASCII("http");
323 http_test_dir = http_test_dir.AppendASCII("tests");
324
325 FilePath worker_test_dir;
326 worker_test_dir = worker_test_dir.AppendASCII("xmlhttprequest");
327 worker_test_dir = worker_test_dir.AppendASCII("workers");
328 InitializeForLayoutTest(http_test_dir, worker_test_dir, true);
329
330 StartHttpServer(new_http_root_dir_);
331 for (int i = 0; i < arraysize(kLayoutTestFiles); ++i)
332 RunLayoutTest(kLayoutTestFiles[i], true);
333 StopHttpServer();
254 } 334 }
255 335
256 TEST_F(WorkerTest, LimitPerPage) { 336 TEST_F(WorkerTest, LimitPerPage) {
257 int max_workers_per_tab = WorkerService::kMaxWorkersPerTabWhenSeparate; 337 int max_workers_per_tab = WorkerService::kMaxWorkersPerTabWhenSeparate;
258 GURL url = GetTestUrl(L"workers", L"many_workers.html"); 338 GURL url = GetTestUrl(L"workers", L"many_workers.html");
259 url = GURL(url.spec() + StringPrintf("?count=%d", max_workers_per_tab + 1)); 339 url = GURL(url.spec() + StringPrintf("?count=%d", max_workers_per_tab + 1));
260 340
261 scoped_refptr<TabProxy> tab(GetActiveTab()); 341 scoped_refptr<TabProxy> tab(GetActiveTab());
262 ASSERT_TRUE(tab->NavigateToURL(url)); 342 ASSERT_TRUE(tab->NavigateToURL(url));
263 343
(...skipping 19 matching lines...) Expand all
283 EXPECT_EQ(total_workers + 1 + (UITest::in_process_renderer() ? 0 : tab_count), 363 EXPECT_EQ(total_workers + 1 + (UITest::in_process_renderer() ? 0 : tab_count),
284 UITest::GetBrowserProcessCount()); 364 UITest::GetBrowserProcessCount());
285 365
286 // Now close the first tab and check that the queued workers were started. 366 // Now close the first tab and check that the queued workers were started.
287 tab->Close(true); 367 tab->Close(true);
288 tab->NavigateToURL(GetTestUrl(L"google", L"google.html")); 368 tab->NavigateToURL(GetTestUrl(L"google", L"google.html"));
289 369
290 EXPECT_EQ(total_workers + 1 + (UITest::in_process_renderer() ? 0 : tab_count), 370 EXPECT_EQ(total_workers + 1 + (UITest::in_process_renderer() ? 0 : tab_count),
291 UITest::GetBrowserProcessCount()); 371 UITest::GetBrowserProcessCount());
292 } 372 }
293
294 TEST_F(WorkerTest, TestHttpServer) {
Dmitry Titov 2009/06/25 18:09:13 either leave this test (as a test for StartHttpSer
295 FilePath path;
296 PathService::Get(chrome::DIR_TEST_DATA, &path);
297 path = path.AppendASCII("workers");
298 StartHttpServer(path);
299 RunHttpTest("test_http_server_up.html");
300 StopHttpServer();
301 }
OLDNEW
« no previous file with comments | « DEPS ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698