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

Side by Side Diff: components/ntp_tiles/icon_cacher_impl_unittest.cc

Issue 2695713004: Add baked-in favicons for default popular sites on NTP (Closed)
Patch Set: Rebase. Created 3 years, 9 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
« no previous file with comments | « components/ntp_tiles/icon_cacher_impl.cc ('k') | components/ntp_tiles/most_visited_sites.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "components/ntp_tiles/icon_cacher_impl.h" 5 #include "components/ntp_tiles/icon_cacher_impl.h"
6 6
7 #include <set>
7 #include <utility> 8 #include <utility>
8 9
9 #include "base/files/scoped_temp_dir.h" 10 #include "base/files/scoped_temp_dir.h"
10 #include "base/memory/ptr_util.h" 11 #include "base/memory/ptr_util.h"
12 #include "base/path_service.h"
11 #include "base/run_loop.h" 13 #include "base/run_loop.h"
14 #include "base/test/mock_callback.h"
15 #include "base/test/test_simple_task_runner.h"
12 #include "base/threading/thread_task_runner_handle.h" 16 #include "base/threading/thread_task_runner_handle.h"
13 #include "components/favicon/core/favicon_client.h" 17 #include "components/favicon/core/favicon_client.h"
14 #include "components/favicon/core/favicon_service_impl.h" 18 #include "components/favicon/core/favicon_service_impl.h"
15 #include "components/favicon/core/favicon_util.h" 19 #include "components/favicon/core/favicon_util.h"
16 #include "components/history/core/browser/history_database_params.h" 20 #include "components/history/core/browser/history_database_params.h"
17 #include "components/history/core/browser/history_service.h" 21 #include "components/history/core/browser/history_service.h"
18 #include "components/image_fetcher/image_fetcher.h" 22 #include "components/image_fetcher/image_fetcher.h"
19 #include "testing/gmock/include/gmock/gmock.h" 23 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h" 24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/base/ui_base_paths.h"
21 #include "ui/gfx/image/image_unittest_util.h" 27 #include "ui/gfx/image/image_unittest_util.h"
22 28
23 using ::testing::_; 29 using ::testing::_;
30 using ::testing::Eq;
31 using ::testing::Invoke;
24 using ::testing::InSequence; 32 using ::testing::InSequence;
25 using ::testing::MockFunction; 33 using ::testing::NiceMock;
26 using ::testing::Return; 34 using ::testing::Return;
35 using ::testing::ReturnArg;
27 36
28 namespace ntp_tiles { 37 namespace ntp_tiles {
29 namespace { 38 namespace {
30 39
31 class MockImageFetcher : public image_fetcher::ImageFetcher { 40 class MockImageFetcher : public image_fetcher::ImageFetcher {
32 public: 41 public:
33 MOCK_METHOD1(SetImageFetcherDelegate, 42 MOCK_METHOD1(SetImageFetcherDelegate,
34 void(image_fetcher::ImageFetcherDelegate* delegate)); 43 void(image_fetcher::ImageFetcherDelegate* delegate));
35 MOCK_METHOD1(SetDataUseServiceName, 44 MOCK_METHOD1(SetDataUseServiceName,
36 void(image_fetcher::ImageFetcher::DataUseServiceName name)); 45 void(image_fetcher::ImageFetcher::DataUseServiceName name));
37 MOCK_METHOD3(StartOrQueueNetworkRequest, 46 MOCK_METHOD3(StartOrQueueNetworkRequest,
38 void(const std::string& id, 47 void(const std::string& id,
39 const GURL& image_url, 48 const GURL& image_url,
40 base::Callback<void(const std::string& id, 49 base::Callback<void(const std::string& id,
41 const gfx::Image& image)> callback)); 50 const gfx::Image& image)> callback));
42 MOCK_METHOD1(SetDesiredImageFrameSize, void(const gfx::Size&)); 51 MOCK_METHOD1(SetDesiredImageFrameSize, void(const gfx::Size&));
43 }; 52 };
44 53
54 // This class provides methods to inject an image resource where a real resource
55 // would be necessary otherwise. All other methods have return values that allow
56 // the normal implementation to proceed.
57 class MockResourceDelegate : public ui::ResourceBundle::Delegate {
58 public:
59 ~MockResourceDelegate() override {}
60
61 MOCK_METHOD1(GetImageNamed, gfx::Image(int resource_id));
62 MOCK_METHOD1(GetNativeImageNamed, gfx::Image(int resource_id));
63
64 MOCK_METHOD2(GetPathForResourcePack,
65 base::FilePath(const base::FilePath& pack_path,
66 ui::ScaleFactor scale_factor));
67
68 MOCK_METHOD2(GetPathForLocalePack,
69 base::FilePath(const base::FilePath& pack_path,
70 const std::string& locale));
71
72 MOCK_METHOD2(LoadDataResourceBytes,
73 base::RefCountedMemory*(int resource_id,
74 ui::ScaleFactor scale_factor));
75
76 MOCK_METHOD3(GetRawDataResource,
77 bool(int resource_id,
78 ui::ScaleFactor scale_factor,
79 base::StringPiece* value));
80
81 MOCK_METHOD2(GetLocalizedString, bool(int message_id, base::string16* value));
82 };
83
45 class IconCacherTest : public ::testing::Test { 84 class IconCacherTest : public ::testing::Test {
46 protected: 85 protected:
47 IconCacherTest() 86 IconCacherTest()
48 : site_(base::string16(), // title, unused 87 : site_(base::string16(), // title, unused
49 GURL("http://url.google/"), 88 GURL("http://url.google/"),
50 GURL("http://url.google/icon.png"), 89 GURL("http://url.google/icon.png"),
51 GURL("http://url.google/favicon.ico"), 90 GURL("http://url.google/favicon.ico"),
52 GURL()), // thumbnail, unused 91 GURL()), // thumbnail, unused
53 image_fetcher_(new ::testing::StrictMock<MockImageFetcher>), 92 image_fetcher_(new ::testing::StrictMock<MockImageFetcher>),
54 favicon_service_(/*favicon_client=*/nullptr, &history_service_) { 93 favicon_service_(/*favicon_client=*/nullptr, &history_service_),
94 task_runner_(new base::TestSimpleTaskRunner()) {
55 CHECK(history_dir_.CreateUniqueTempDir()); 95 CHECK(history_dir_.CreateUniqueTempDir());
56 CHECK(history_service_.Init( 96 CHECK(history_service_.Init(
57 history::HistoryDatabaseParams(history_dir_.GetPath(), 0, 0))); 97 history::HistoryDatabaseParams(history_dir_.GetPath(), 0, 0)));
58 } 98 }
59 99
100 void SetUp() override {
101 if (ui::ResourceBundle::HasSharedInstance()) {
102 ui::ResourceBundle::CleanupSharedInstance();
103 }
104 ON_CALL(mock_resource_delegate_, GetPathForResourcePack(_, _))
105 .WillByDefault(ReturnArg<0>());
106 ON_CALL(mock_resource_delegate_, GetPathForLocalePack(_, _))
107 .WillByDefault(ReturnArg<0>());
108 ui::ResourceBundle::InitSharedInstanceWithLocale(
109 "en-US", &mock_resource_delegate_,
110 ui::ResourceBundle::LOAD_COMMON_RESOURCES);
111 }
112
113 void TearDown() override {
114 if (ui::ResourceBundle::HasSharedInstance()) {
115 ui::ResourceBundle::CleanupSharedInstance();
116 }
117 base::FilePath pak_path;
118 #if defined(OS_ANDROID)
119 PathService::Get(ui::DIR_RESOURCE_PAKS_ANDROID, &pak_path);
120 #else
121 PathService::Get(base::DIR_MODULE, &pak_path);
122 #endif
123
124 base::FilePath ui_test_pak_path;
125 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
126 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
127
128 ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
129 pak_path.AppendASCII("components_tests_resources.pak"),
130 ui::SCALE_FACTOR_NONE);
131 }
132
60 void PreloadIcon(const GURL& url, 133 void PreloadIcon(const GURL& url,
61 const GURL& icon_url, 134 const GURL& icon_url,
62 favicon_base::IconType icon_type, 135 favicon_base::IconType icon_type,
63 int width, 136 int width,
64 int height) { 137 int height) {
65 favicon_service_.SetFavicons(url, icon_url, icon_type, 138 favicon_service_.SetFavicons(url, icon_url, icon_type,
66 gfx::test::CreateImage(width, height)); 139 gfx::test::CreateImage(width, height));
67 } 140 }
68 141
69 bool IconIsCachedFor(const GURL& url, favicon_base::IconType icon_type) { 142 bool IconIsCachedFor(const GURL& url, favicon_base::IconType icon_type) {
143 return !GetCachedIconFor(url, icon_type).IsEmpty();
144 }
145
146 gfx::Image GetCachedIconFor(const GURL& url,
147 favicon_base::IconType icon_type) {
70 base::CancelableTaskTracker tracker; 148 base::CancelableTaskTracker tracker;
71 bool is_cached; 149 gfx::Image image;
72 base::RunLoop loop; 150 base::RunLoop loop;
73 favicon::GetFaviconImageForPageURL( 151 favicon::GetFaviconImageForPageURL(
74 &favicon_service_, url, icon_type, 152 &favicon_service_, url, icon_type,
75 base::Bind( 153 base::Bind(
76 [](bool* is_cached, base::RunLoop* loop, 154 [](gfx::Image* image, base::RunLoop* loop,
77 const favicon_base::FaviconImageResult& result) { 155 const favicon_base::FaviconImageResult& result) {
78 *is_cached = !result.image.IsEmpty(); 156 *image = result.image;
79 loop->Quit(); 157 loop->Quit();
80 }, 158 },
81 &is_cached, &loop), 159 &image, &loop),
82 &tracker); 160 &tracker);
83 loop.Run(); 161 loop.Run();
84 return is_cached; 162 return image;
85 } 163 }
86 164
165 void WaitForTasksToFinish() { task_runner_->RunUntilIdle(); }
166
87 base::MessageLoop message_loop_; 167 base::MessageLoop message_loop_;
88 PopularSites::Site site_; 168 PopularSites::Site site_;
89 std::unique_ptr<MockImageFetcher> image_fetcher_; 169 std::unique_ptr<MockImageFetcher> image_fetcher_;
90 base::ScopedTempDir history_dir_; 170 base::ScopedTempDir history_dir_;
91 history::HistoryService history_service_; 171 history::HistoryService history_service_;
92 favicon::FaviconServiceImpl favicon_service_; 172 favicon::FaviconServiceImpl favicon_service_;
173 scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
174 NiceMock<MockResourceDelegate> mock_resource_delegate_;
93 }; 175 };
94 176
95 template <typename Fn>
96 base::Callback<Fn> BindMockFunction(MockFunction<Fn>* function) {
97 return base::Bind(&MockFunction<Fn>::Call, base::Unretained(function));
98 }
99
100 ACTION(FailFetch) { 177 ACTION(FailFetch) {
101 base::ThreadTaskRunnerHandle::Get()->PostTask( 178 base::ThreadTaskRunnerHandle::Get()->PostTask(
102 FROM_HERE, base::Bind(arg2, arg0, gfx::Image())); 179 FROM_HERE, base::Bind(arg2, arg0, gfx::Image()));
103 } 180 }
104 181
105 ACTION_P2(PassFetch, width, height) { 182 ACTION_P2(PassFetch, width, height) {
106 base::ThreadTaskRunnerHandle::Get()->PostTask( 183 base::ThreadTaskRunnerHandle::Get()->PostTask(
107 FROM_HERE, base::Bind(arg2, arg0, gfx::test::CreateImage(width, height))); 184 FROM_HERE, base::Bind(arg2, arg0, gfx::test::CreateImage(width, height)));
108 } 185 }
109 186
110 ACTION_P(Quit, run_loop) { 187 ACTION_P(Quit, run_loop) {
111 run_loop->Quit(); 188 run_loop->Quit();
112 } 189 }
113 190
114 TEST_F(IconCacherTest, LargeCached) { 191 TEST_F(IconCacherTest, LargeCached) {
115 MockFunction<void(bool)> done; 192 base::MockCallback<base::Closure> done;
193 EXPECT_CALL(done, Run()).Times(0);
116 base::RunLoop loop; 194 base::RunLoop loop;
117 { 195 {
118 InSequence s; 196 InSequence s;
119 EXPECT_CALL(*image_fetcher_, 197 EXPECT_CALL(*image_fetcher_,
120 SetDataUseServiceName( 198 SetDataUseServiceName(
121 data_use_measurement::DataUseUserData::NTP_TILES)); 199 data_use_measurement::DataUseUserData::NTP_TILES));
122 EXPECT_CALL(*image_fetcher_, SetDesiredImageFrameSize(gfx::Size(128, 128))); 200 EXPECT_CALL(*image_fetcher_, SetDesiredImageFrameSize(gfx::Size(128, 128)));
123 EXPECT_CALL(done, Call(false)).WillOnce(Quit(&loop));
124 } 201 }
125 PreloadIcon(site_.url, site_.large_icon_url, favicon_base::TOUCH_ICON, 128, 202 PreloadIcon(site_.url, site_.large_icon_url, favicon_base::TOUCH_ICON, 128,
126 128); 203 128);
127
128 IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_)); 204 IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_));
129 cacher.StartFetch(site_, BindMockFunction(&done)); 205 cacher.StartFetch(site_, done.Get(), done.Get());
130 loop.Run(); 206 WaitForTasksToFinish();
131 EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::FAVICON)); 207 EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::FAVICON));
132 EXPECT_TRUE(IconIsCachedFor(site_.url, favicon_base::TOUCH_ICON)); 208 EXPECT_TRUE(IconIsCachedFor(site_.url, favicon_base::TOUCH_ICON));
133 } 209 }
134 210
135 TEST_F(IconCacherTest, LargeNotCachedAndFetchSucceeded) { 211 TEST_F(IconCacherTest, LargeNotCachedAndFetchSucceeded) {
136 MockFunction<void(bool)> done; 212 base::MockCallback<base::Closure> done;
137 base::RunLoop loop; 213 base::RunLoop loop;
138 { 214 {
139 InSequence s; 215 InSequence s;
140 EXPECT_CALL(*image_fetcher_, 216 EXPECT_CALL(*image_fetcher_,
141 SetDataUseServiceName( 217 SetDataUseServiceName(
142 data_use_measurement::DataUseUserData::NTP_TILES)); 218 data_use_measurement::DataUseUserData::NTP_TILES));
143 EXPECT_CALL(*image_fetcher_, SetDesiredImageFrameSize(gfx::Size(128, 128))); 219 EXPECT_CALL(*image_fetcher_, SetDesiredImageFrameSize(gfx::Size(128, 128)));
144 EXPECT_CALL(*image_fetcher_, 220 EXPECT_CALL(*image_fetcher_,
145 StartOrQueueNetworkRequest(_, site_.large_icon_url, _)) 221 StartOrQueueNetworkRequest(_, site_.large_icon_url, _))
146 .WillOnce(PassFetch(128, 128)); 222 .WillOnce(PassFetch(128, 128));
147 EXPECT_CALL(done, Call(true)).WillOnce(Quit(&loop)); 223 EXPECT_CALL(done, Run()).WillOnce(Quit(&loop));
148 } 224 }
149 225
150 IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_)); 226 IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_));
151 cacher.StartFetch(site_, BindMockFunction(&done)); 227 cacher.StartFetch(site_, done.Get(), done.Get());
152 loop.Run(); 228 loop.Run();
153 EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::FAVICON)); 229 EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::FAVICON));
154 EXPECT_TRUE(IconIsCachedFor(site_.url, favicon_base::TOUCH_ICON)); 230 EXPECT_TRUE(IconIsCachedFor(site_.url, favicon_base::TOUCH_ICON));
155 } 231 }
156 232
157 TEST_F(IconCacherTest, SmallNotCachedAndFetchSucceeded) { 233 TEST_F(IconCacherTest, SmallNotCachedAndFetchSucceeded) {
158 site_.large_icon_url = GURL(); 234 site_.large_icon_url = GURL();
159 235
160 MockFunction<void(bool)> done; 236 base::MockCallback<base::Closure> done;
161 base::RunLoop loop; 237 base::RunLoop loop;
162 { 238 {
163 InSequence s; 239 InSequence s;
164 EXPECT_CALL(*image_fetcher_, 240 EXPECT_CALL(*image_fetcher_,
165 SetDataUseServiceName( 241 SetDataUseServiceName(
166 data_use_measurement::DataUseUserData::NTP_TILES)); 242 data_use_measurement::DataUseUserData::NTP_TILES));
167 EXPECT_CALL(*image_fetcher_, SetDesiredImageFrameSize(gfx::Size(128, 128))); 243 EXPECT_CALL(*image_fetcher_, SetDesiredImageFrameSize(gfx::Size(128, 128)));
168 EXPECT_CALL(*image_fetcher_, 244 EXPECT_CALL(*image_fetcher_,
169 StartOrQueueNetworkRequest(_, site_.favicon_url, _)) 245 StartOrQueueNetworkRequest(_, site_.favicon_url, _))
170 .WillOnce(PassFetch(128, 128)); 246 .WillOnce(PassFetch(128, 128));
171 EXPECT_CALL(done, Call(true)).WillOnce(Quit(&loop)); 247 EXPECT_CALL(done, Run()).WillOnce(Quit(&loop));
172 } 248 }
173 249
174 IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_)); 250 IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_));
175 cacher.StartFetch(site_, BindMockFunction(&done)); 251 cacher.StartFetch(site_, done.Get(), done.Get());
176 loop.Run(); 252 loop.Run();
177 EXPECT_TRUE(IconIsCachedFor(site_.url, favicon_base::FAVICON)); 253 EXPECT_TRUE(IconIsCachedFor(site_.url, favicon_base::FAVICON));
178 EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::TOUCH_ICON)); 254 EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::TOUCH_ICON));
179 } 255 }
180 256
181 TEST_F(IconCacherTest, LargeNotCachedAndFetchFailed) { 257 TEST_F(IconCacherTest, LargeNotCachedAndFetchFailed) {
182 MockFunction<void(bool)> done; 258 base::MockCallback<base::Closure> done;
183 base::RunLoop loop; 259 EXPECT_CALL(done, Run()).Times(0);
184 { 260 {
185 InSequence s; 261 InSequence s;
186 EXPECT_CALL(*image_fetcher_, 262 EXPECT_CALL(*image_fetcher_,
187 SetDataUseServiceName( 263 SetDataUseServiceName(
188 data_use_measurement::DataUseUserData::NTP_TILES)); 264 data_use_measurement::DataUseUserData::NTP_TILES));
189 EXPECT_CALL(*image_fetcher_, SetDesiredImageFrameSize(gfx::Size(128, 128))); 265 EXPECT_CALL(*image_fetcher_, SetDesiredImageFrameSize(gfx::Size(128, 128)));
190 EXPECT_CALL(*image_fetcher_, 266 EXPECT_CALL(*image_fetcher_,
191 StartOrQueueNetworkRequest(_, site_.large_icon_url, _)) 267 StartOrQueueNetworkRequest(_, site_.large_icon_url, _))
192 .WillOnce(FailFetch()); 268 .WillOnce(FailFetch());
193 EXPECT_CALL(done, Call(false)).WillOnce(Quit(&loop));
194 } 269 }
195 270
196 IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_)); 271 IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_));
197 cacher.StartFetch(site_, BindMockFunction(&done)); 272 cacher.StartFetch(site_, done.Get(), done.Get());
198 loop.Run(); 273 WaitForTasksToFinish();
199 EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::FAVICON)); 274 EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::FAVICON));
200 EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::TOUCH_ICON)); 275 EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::TOUCH_ICON));
201 } 276 }
202 277
278 TEST_F(IconCacherTest, ProvidesDefaultIconAndSucceedsWithFetching) {
279 // We are not interested which delegate function actually handles the call to
280 // |GetNativeImageNamed| as long as we receive the right image.
281 ON_CALL(mock_resource_delegate_, GetNativeImageNamed(12345))
282 .WillByDefault(Return(gfx::test::CreateImage(64, 64)));
283 ON_CALL(mock_resource_delegate_, GetImageNamed(12345))
284 .WillByDefault(Return(gfx::test::CreateImage(64, 64)));
285 base::MockCallback<base::Closure> preliminary_icon_available;
286 base::MockCallback<base::Closure> icon_available;
287 base::RunLoop default_loop;
288 base::RunLoop fetch_loop;
289 {
290 InSequence s;
291 EXPECT_CALL(*image_fetcher_,
292 SetDataUseServiceName(
293 data_use_measurement::DataUseUserData::NTP_TILES));
294 EXPECT_CALL(*image_fetcher_, SetDesiredImageFrameSize(gfx::Size(128, 128)));
295 EXPECT_CALL(preliminary_icon_available, Run())
296 .WillOnce(Quit(&default_loop));
297 EXPECT_CALL(*image_fetcher_,
298 StartOrQueueNetworkRequest(_, site_.large_icon_url, _))
299 .WillOnce(PassFetch(128, 128));
300 EXPECT_CALL(icon_available, Run()).WillOnce(Quit(&fetch_loop));
301 }
302
303 IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_));
304 site_.default_icon_resource = 12345;
305 cacher.StartFetch(site_, icon_available.Get(),
306 preliminary_icon_available.Get());
307
308 default_loop.Run(); // Wait for the default image.
309 EXPECT_THAT(GetCachedIconFor(site_.url, favicon_base::TOUCH_ICON).Size(),
310 Eq(gfx::Size(64, 64))); // Compares dimensions, not objects.
311
312 // Let the fetcher continue and wait for the second call of the callback.
313 fetch_loop.Run(); // Wait for the updated image.
314 EXPECT_THAT(GetCachedIconFor(site_.url, favicon_base::TOUCH_ICON).Size(),
315 Eq(gfx::Size(128, 128))); // Compares dimensions, not objects.
316 }
317
203 } // namespace 318 } // namespace
204 } // namespace ntp_tiles 319 } // namespace ntp_tiles
OLDNEW
« no previous file with comments | « components/ntp_tiles/icon_cacher_impl.cc ('k') | components/ntp_tiles/most_visited_sites.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698