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

Side by Side Diff: content/browser/service_worker/service_worker_storage_unittest.cc

Issue 1945753002: Make Service Worker DB UserData methods accept multiple keys at once (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@iid6encrypt
Patch Set: Rebase Created 4 years, 7 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
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 "content/browser/service_worker/service_worker_storage.h" 5 #include "content/browser/service_worker/service_worker_storage.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 #include <string> 8 #include <string>
9 #include <utility> 9 #include <utility>
10 10
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 } 110 }
111 111
112 ServiceWorkerStorage::GetRegistrationsInfosCallback 112 ServiceWorkerStorage::GetRegistrationsInfosCallback
113 MakeGetRegistrationsInfosCallback( 113 MakeGetRegistrationsInfosCallback(
114 bool* was_called, 114 bool* was_called,
115 ServiceWorkerStatusCode* status, 115 ServiceWorkerStatusCode* status,
116 std::vector<ServiceWorkerRegistrationInfo>* all) { 116 std::vector<ServiceWorkerRegistrationInfo>* all) {
117 return base::Bind(&GetAllInfosCallback, was_called, status, all); 117 return base::Bind(&GetAllInfosCallback, was_called, status, all);
118 } 118 }
119 119
120 void GetUserDataCallback( 120 void GetUserDataCallback(bool* was_called,
121 bool* was_called, 121 std::vector<std::string>* data_out,
122 std::string* data_out, 122 ServiceWorkerStatusCode* status_out,
123 ServiceWorkerStatusCode* status_out, 123 const std::vector<std::string>& data,
124 const std::string& data, 124 ServiceWorkerStatusCode status) {
125 ServiceWorkerStatusCode status) {
126 *was_called = true; 125 *was_called = true;
127 *data_out = data; 126 *data_out = data;
128 *status_out = status; 127 *status_out = status;
129 } 128 }
130 129
131 void GetUserDataForAllRegistrationsCallback( 130 void GetUserDataForAllRegistrationsCallback(
132 bool* was_called, 131 bool* was_called,
133 std::vector<std::pair<int64_t, std::string>>* data_out, 132 std::vector<std::pair<int64_t, std::string>>* data_out,
134 ServiceWorkerStatusCode* status_out, 133 ServiceWorkerStatusCode* status_out,
135 const std::vector<std::pair<int64_t, std::string>>& data, 134 const std::vector<std::pair<int64_t, std::string>>& data,
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after
374 storage()->GetRegistrationsForOrigin( 373 storage()->GetRegistrationsForOrigin(
375 origin, 374 origin,
376 MakeGetRegistrationsCallback(&was_called, &result, registrations)); 375 MakeGetRegistrationsCallback(&was_called, &result, registrations));
377 EXPECT_FALSE(was_called); // always async 376 EXPECT_FALSE(was_called); // always async
378 base::RunLoop().RunUntilIdle(); 377 base::RunLoop().RunUntilIdle();
379 EXPECT_TRUE(was_called); 378 EXPECT_TRUE(was_called);
380 return result; 379 return result;
381 } 380 }
382 381
383 ServiceWorkerStatusCode GetUserData(int64_t registration_id, 382 ServiceWorkerStatusCode GetUserData(int64_t registration_id,
384 const std::string& key, 383 const std::vector<std::string>& keys,
385 std::string* data) { 384 std::vector<std::string>* data) {
386 bool was_called = false; 385 bool was_called = false;
387 ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_MAX_VALUE; 386 ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_MAX_VALUE;
388 storage()->GetUserData( 387 storage()->GetUserData(
389 registration_id, key, 388 registration_id, keys,
390 base::Bind(&GetUserDataCallback, &was_called, data, &result)); 389 base::Bind(&GetUserDataCallback, &was_called, data, &result));
391 EXPECT_FALSE(was_called); // always async 390 EXPECT_FALSE(was_called); // always async
392 base::RunLoop().RunUntilIdle(); 391 base::RunLoop().RunUntilIdle();
393 EXPECT_TRUE(was_called); 392 EXPECT_TRUE(was_called);
394 return result; 393 return result;
395 } 394 }
396 395
397 ServiceWorkerStatusCode StoreUserData(int64_t registration_id, 396 ServiceWorkerStatusCode StoreUserData(
398 const GURL& origin, 397 int64_t registration_id,
399 const std::string& key, 398 const GURL& origin,
400 const std::string& data) { 399 const std::vector<std::pair<std::string, std::string>>& key_value_pairs) {
401 bool was_called = false; 400 bool was_called = false;
402 ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_MAX_VALUE; 401 ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_MAX_VALUE;
403 storage()->StoreUserData( 402 storage()->StoreUserData(registration_id, origin, key_value_pairs,
404 registration_id, origin, key, data, 403 MakeStatusCallback(&was_called, &result));
405 MakeStatusCallback(&was_called, &result));
406 EXPECT_FALSE(was_called); // always async 404 EXPECT_FALSE(was_called); // always async
407 base::RunLoop().RunUntilIdle(); 405 base::RunLoop().RunUntilIdle();
408 EXPECT_TRUE(was_called); 406 EXPECT_TRUE(was_called);
409 return result; 407 return result;
410 } 408 }
411 409
412 ServiceWorkerStatusCode ClearUserData(int64_t registration_id, 410 ServiceWorkerStatusCode ClearUserData(int64_t registration_id,
413 const std::string& key) { 411 const std::vector<std::string>& keys) {
414 bool was_called = false; 412 bool was_called = false;
415 ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_MAX_VALUE; 413 ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_MAX_VALUE;
416 storage()->ClearUserData( 414 storage()->ClearUserData(registration_id, keys,
417 registration_id, key, MakeStatusCallback(&was_called, &result)); 415 MakeStatusCallback(&was_called, &result));
418 EXPECT_FALSE(was_called); // always async 416 EXPECT_FALSE(was_called); // always async
419 base::RunLoop().RunUntilIdle(); 417 base::RunLoop().RunUntilIdle();
420 EXPECT_TRUE(was_called); 418 EXPECT_TRUE(was_called);
421 return result; 419 return result;
422 } 420 }
423 421
424 ServiceWorkerStatusCode GetUserDataForAllRegistrations( 422 ServiceWorkerStatusCode GetUserDataForAllRegistrations(
425 const std::string& key, 423 const std::string& key,
426 std::vector<std::pair<int64_t, std::string>>* data) { 424 std::vector<std::pair<int64_t, std::string>>* data) {
427 bool was_called = false; 425 bool was_called = false;
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
559 // access the disk cache. 557 // access the disk cache.
560 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer = 558 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
561 new HttpResponseInfoIOBuffer(); 559 new HttpResponseInfoIOBuffer();
562 EXPECT_EQ(net::ERR_CACHE_MISS, 560 EXPECT_EQ(net::ERR_CACHE_MISS,
563 ReadResponseInfo(storage(), kResourceId, info_buffer.get())); 561 ReadResponseInfo(storage(), kResourceId, info_buffer.get()));
564 EXPECT_EQ(net::ERR_FAILED, WriteBasicResponse(storage(), kResourceId)); 562 EXPECT_EQ(net::ERR_FAILED, WriteBasicResponse(storage(), kResourceId));
565 EXPECT_EQ(net::ERR_FAILED, 563 EXPECT_EQ(net::ERR_FAILED,
566 WriteResponseMetadata(storage(), kResourceId, "foo")); 564 WriteResponseMetadata(storage(), kResourceId, "foo"));
567 565
568 const std::string kUserDataKey = "key"; 566 const std::string kUserDataKey = "key";
569 std::string user_data_out; 567 std::vector<std::string> user_data_out;
570 EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT, 568 EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT,
571 GetUserData(kRegistrationId, kUserDataKey, &user_data_out)); 569 GetUserData(kRegistrationId, {kUserDataKey}, &user_data_out));
572 EXPECT_EQ(
573 SERVICE_WORKER_ERROR_ABORT,
574 StoreUserData(kRegistrationId, kScope.GetOrigin(), kUserDataKey, "foo"));
575 EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT, 570 EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT,
576 ClearUserData(kRegistrationId, kUserDataKey)); 571 StoreUserData(kRegistrationId, kScope.GetOrigin(),
572 {{kUserDataKey, "foo"}}));
573 EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT,
574 ClearUserData(kRegistrationId, {kUserDataKey}));
577 std::vector<std::pair<int64_t, std::string>> data_list_out; 575 std::vector<std::pair<int64_t, std::string>> data_list_out;
578 EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT, 576 EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT,
579 GetUserDataForAllRegistrations(kUserDataKey, &data_list_out)); 577 GetUserDataForAllRegistrations(kUserDataKey, &data_list_out));
580 578
581 EXPECT_FALSE( 579 EXPECT_FALSE(
582 storage()->OriginHasForeignFetchRegistrations(kScope.GetOrigin())); 580 storage()->OriginHasForeignFetchRegistrations(kScope.GetOrigin()));
583 581
584 // Next available ids should be invalid. 582 // Next available ids should be invalid.
585 EXPECT_EQ(kInvalidServiceWorkerRegistrationId, 583 EXPECT_EQ(kInvalidServiceWorkerRegistrationId,
586 storage()->NewRegistrationId()); 584 storage()->NewRegistrationId());
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after
935 std::vector<ServiceWorkerDatabase::ResourceRecord> records; 933 std::vector<ServiceWorkerDatabase::ResourceRecord> records;
936 records.push_back(ServiceWorkerDatabase::ResourceRecord( 934 records.push_back(ServiceWorkerDatabase::ResourceRecord(
937 1, live_version->script_url(), 100)); 935 1, live_version->script_url(), 100));
938 live_version->script_cache_map()->SetResources(records); 936 live_version->script_cache_map()->SetResources(records);
939 live_version->SetStatus(ServiceWorkerVersion::INSTALLED); 937 live_version->SetStatus(ServiceWorkerVersion::INSTALLED);
940 live_registration->SetWaitingVersion(live_version); 938 live_registration->SetWaitingVersion(live_version);
941 EXPECT_EQ(SERVICE_WORKER_OK, 939 EXPECT_EQ(SERVICE_WORKER_OK,
942 StoreRegistration(live_registration, live_version)); 940 StoreRegistration(live_registration, live_version));
943 941
944 // Store user data associated with the registration. 942 // Store user data associated with the registration.
945 std::string data_out; 943 std::vector<std::string> data_out;
944 EXPECT_EQ(
945 SERVICE_WORKER_OK,
946 StoreUserData(kRegistrationId, kScope.GetOrigin(), {{"key", "data"}}));
946 EXPECT_EQ(SERVICE_WORKER_OK, 947 EXPECT_EQ(SERVICE_WORKER_OK,
947 StoreUserData(kRegistrationId, kScope.GetOrigin(), "key", "data")); 948 GetUserData(kRegistrationId, {"key"}, &data_out));
948 EXPECT_EQ(SERVICE_WORKER_OK, GetUserData(kRegistrationId, "key", &data_out)); 949 ASSERT_EQ(1u, data_out.size());
949 EXPECT_EQ("data", data_out); 950 EXPECT_EQ("data", data_out[0]);
950 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND, 951 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
951 GetUserData(kRegistrationId, "unknown_key", &data_out)); 952 GetUserData(kRegistrationId, {"unknown_key"}, &data_out));
952 std::vector<std::pair<int64_t, std::string>> data_list_out; 953 std::vector<std::pair<int64_t, std::string>> data_list_out;
953 EXPECT_EQ(SERVICE_WORKER_OK, 954 EXPECT_EQ(SERVICE_WORKER_OK,
954 GetUserDataForAllRegistrations("key", &data_list_out)); 955 GetUserDataForAllRegistrations("key", &data_list_out));
955 ASSERT_EQ(1u, data_list_out.size()); 956 ASSERT_EQ(1u, data_list_out.size());
956 EXPECT_EQ(kRegistrationId, data_list_out[0].first); 957 EXPECT_EQ(kRegistrationId, data_list_out[0].first);
957 EXPECT_EQ("data", data_list_out[0].second); 958 EXPECT_EQ("data", data_list_out[0].second);
958 data_list_out.clear(); 959 data_list_out.clear();
959 EXPECT_EQ(SERVICE_WORKER_OK, 960 EXPECT_EQ(SERVICE_WORKER_OK,
960 GetUserDataForAllRegistrations("unknown_key", &data_list_out)); 961 GetUserDataForAllRegistrations("unknown_key", &data_list_out));
961 EXPECT_EQ(0u, data_list_out.size()); 962 EXPECT_EQ(0u, data_list_out.size());
962 EXPECT_EQ(SERVICE_WORKER_OK, ClearUserData(kRegistrationId, "key")); 963 EXPECT_EQ(SERVICE_WORKER_OK, ClearUserData(kRegistrationId, {"key"}));
963 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND, 964 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
964 GetUserData(kRegistrationId, "key", &data_out)); 965 GetUserData(kRegistrationId, {"key"}, &data_out));
966
967 // Write/overwrite multiple user data keys.
968 EXPECT_EQ(SERVICE_WORKER_OK,
969 StoreUserData(
970 kRegistrationId, kScope.GetOrigin(),
971 {{"key", "overwrite"}, {"key3", "data3"}, {"key4", "data4"}}));
972 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
973 GetUserData(kRegistrationId, {"key2"}, &data_out));
974 EXPECT_TRUE(data_out.empty());
975 EXPECT_EQ(SERVICE_WORKER_OK,
976 GetUserData(kRegistrationId, {"key", "key3", "key4"}, &data_out));
977 ASSERT_EQ(3u, data_out.size());
978 EXPECT_EQ("overwrite", data_out[0]);
979 EXPECT_EQ("data3", data_out[1]);
980 EXPECT_EQ("data4", data_out[2]);
981 // Multiple gets fail if one is not found.
982 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
983 GetUserData(kRegistrationId, {"key", "key2"}, &data_out));
984 EXPECT_TRUE(data_out.empty());
985
986 // Delete multiple user data keys, even if some are not found.
987 EXPECT_EQ(SERVICE_WORKER_OK,
988 ClearUserData(kRegistrationId, {"key", "key2", "key3"}));
989 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
990 GetUserData(kRegistrationId, {"key"}, &data_out));
991 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
992 GetUserData(kRegistrationId, {"key2"}, &data_out));
993 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
994 GetUserData(kRegistrationId, {"key3"}, &data_out));
995 EXPECT_EQ(SERVICE_WORKER_OK,
996 GetUserData(kRegistrationId, {"key4"}, &data_out));
997 ASSERT_EQ(1u, data_out.size());
998 EXPECT_EQ("data4", data_out[0]);
965 999
966 // User data should be deleted when the associated registration is deleted. 1000 // User data should be deleted when the associated registration is deleted.
1001 ASSERT_EQ(
1002 SERVICE_WORKER_OK,
1003 StoreUserData(kRegistrationId, kScope.GetOrigin(), {{"key", "data"}}));
967 ASSERT_EQ(SERVICE_WORKER_OK, 1004 ASSERT_EQ(SERVICE_WORKER_OK,
968 StoreUserData(kRegistrationId, kScope.GetOrigin(), "key", "data")); 1005 GetUserData(kRegistrationId, {"key"}, &data_out));
969 ASSERT_EQ(SERVICE_WORKER_OK, 1006 ASSERT_EQ(1u, data_out.size());
970 GetUserData(kRegistrationId, "key", &data_out)); 1007 ASSERT_EQ("data", data_out[0]);
971 ASSERT_EQ("data", data_out);
972 1008
973 EXPECT_EQ(SERVICE_WORKER_OK, 1009 EXPECT_EQ(SERVICE_WORKER_OK,
974 DeleteRegistration(kRegistrationId, kScope.GetOrigin())); 1010 DeleteRegistration(kRegistrationId, kScope.GetOrigin()));
975 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND, 1011 EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
976 GetUserData(kRegistrationId, "key", &data_out)); 1012 GetUserData(kRegistrationId, {"key"}, &data_out));
977 data_list_out.clear(); 1013 data_list_out.clear();
978 EXPECT_EQ(SERVICE_WORKER_OK, 1014 EXPECT_EQ(SERVICE_WORKER_OK,
979 GetUserDataForAllRegistrations("key", &data_list_out)); 1015 GetUserDataForAllRegistrations("key", &data_list_out));
980 EXPECT_EQ(0u, data_list_out.size()); 1016 EXPECT_EQ(0u, data_list_out.size());
981 1017
982 // Data access with an invalid registration id should be failed. 1018 // Data access with an invalid registration id should be failed.
983 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED, 1019 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
984 StoreUserData(kInvalidServiceWorkerRegistrationId, 1020 StoreUserData(kInvalidServiceWorkerRegistrationId,
985 kScope.GetOrigin(), "key", "data")); 1021 kScope.GetOrigin(), {{"key", "data"}}));
1022 EXPECT_EQ(
1023 SERVICE_WORKER_ERROR_FAILED,
1024 GetUserData(kInvalidServiceWorkerRegistrationId, {"key"}, &data_out));
986 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED, 1025 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
987 GetUserData(kInvalidServiceWorkerRegistrationId, "key", &data_out)); 1026 ClearUserData(kInvalidServiceWorkerRegistrationId, {"key"}));
988 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
989 ClearUserData(kInvalidServiceWorkerRegistrationId, "key"));
990 1027
991 // Data access with an empty key should be failed. 1028 // Data access with an empty key should be failed.
992 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED, 1029 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
993 StoreUserData( 1030 StoreUserData(kRegistrationId, kScope.GetOrigin(),
994 kRegistrationId, kScope.GetOrigin(), std::string(), "data")); 1031 std::vector<std::pair<std::string, std::string>>()));
995 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED, 1032 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
996 GetUserData(kRegistrationId, std::string(), &data_out)); 1033 StoreUserData(kRegistrationId, kScope.GetOrigin(),
1034 {{std::string(), "data"}}));
997 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED, 1035 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
998 ClearUserData(kRegistrationId, std::string())); 1036 StoreUserData(kRegistrationId, kScope.GetOrigin(),
1037 {{std::string(), "data"}, {"key", "data"}}));
1038 EXPECT_EQ(
1039 SERVICE_WORKER_ERROR_FAILED,
1040 GetUserData(kRegistrationId, std::vector<std::string>(), &data_out));
1041 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
1042 GetUserData(kRegistrationId, {std::string()}, &data_out));
1043 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
1044 GetUserData(kRegistrationId, {std::string(), "key"}, &data_out));
1045 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
1046 ClearUserData(kRegistrationId, std::vector<std::string>()));
1047 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
1048 ClearUserData(kRegistrationId, {std::string()}));
1049 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
1050 ClearUserData(kRegistrationId, {std::string(), "key"}));
999 data_list_out.clear(); 1051 data_list_out.clear();
1000 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED, 1052 EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED,
1001 GetUserDataForAllRegistrations(std::string(), &data_list_out)); 1053 GetUserDataForAllRegistrations(std::string(), &data_list_out));
1002 } 1054 }
1003 1055
1004 class ServiceWorkerResourceStorageTest : public ServiceWorkerStorageTest { 1056 class ServiceWorkerResourceStorageTest : public ServiceWorkerStorageTest {
1005 public: 1057 public:
1006 void SetUp() override { 1058 void SetUp() override {
1007 ServiceWorkerStorageTest::SetUp(); 1059 ServiceWorkerStorageTest::SetUp();
1008 LazyInitialize(); 1060 LazyInitialize();
(...skipping 674 matching lines...) Expand 10 before | Expand all | Expand 10 after
1683 // Remove other registration at first origin. 1735 // Remove other registration at first origin.
1684 EXPECT_EQ(SERVICE_WORKER_OK, 1736 EXPECT_EQ(SERVICE_WORKER_OK,
1685 DeleteRegistration(kRegistrationId2, kScope2.GetOrigin())); 1737 DeleteRegistration(kRegistrationId2, kScope2.GetOrigin()));
1686 1738
1687 // No foreign fetch registrations remain. 1739 // No foreign fetch registrations remain.
1688 EXPECT_FALSE(storage()->OriginHasForeignFetchRegistrations(kOrigin1)); 1740 EXPECT_FALSE(storage()->OriginHasForeignFetchRegistrations(kOrigin1));
1689 EXPECT_FALSE(storage()->OriginHasForeignFetchRegistrations(kOrigin2)); 1741 EXPECT_FALSE(storage()->OriginHasForeignFetchRegistrations(kOrigin2));
1690 } 1742 }
1691 1743
1692 } // namespace content 1744 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/service_worker/service_worker_storage.cc ('k') | content/public/browser/push_messaging_service.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698