OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/bookmarks/browser/bookmark_index.h" | 5 #include "components/bookmarks/browser/bookmark_index.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/macros.h" | 10 #include "base/macros.h" |
11 #include "base/strings/string_number_conversions.h" | 11 #include "base/strings/string_number_conversions.h" |
12 #include "base/strings/string_split.h" | 12 #include "base/strings/string_split.h" |
13 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
14 #include "base/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
15 #include "components/bookmarks/browser/bookmark_match.h" | 15 #include "components/bookmarks/browser/bookmark_match.h" |
16 #include "components/bookmarks/browser/bookmark_model.h" | 16 #include "components/bookmarks/browser/bookmark_model.h" |
17 #include "components/bookmarks/test/bookmark_test_helpers.h" | 17 #include "components/bookmarks/test/bookmark_test_helpers.h" |
18 #include "components/bookmarks/test/test_bookmark_client.h" | 18 #include "components/bookmarks/test/test_bookmark_client.h" |
19 #include "testing/gtest/include/gtest/gtest.h" | 19 #include "testing/gtest/include/gtest/gtest.h" |
20 | 20 |
21 using base::ASCIIToUTF16; | 21 using base::ASCIIToUTF16; |
22 using base::UTF8ToUTF16; | 22 using base::UTF8ToUTF16; |
23 | 23 |
24 namespace bookmarks { | 24 namespace bookmarks { |
25 namespace { | 25 namespace { |
26 | 26 |
27 const char kAboutBlankURL[] = "about:blank"; | 27 const char kAboutBlankURL[] = "about:blank"; |
28 | 28 |
29 class BookmarkClientMock : public TestBookmarkClient { | 29 class BookmarkClientMock : public BookmarkClient { |
30 public: | 30 public: |
31 BookmarkClientMock(const std::map<GURL, int>& typed_count_map) | 31 BookmarkClientMock(const std::map<GURL, int>& typed_count_map) |
32 : typed_count_map_(typed_count_map) {} | 32 : typed_count_map_(typed_count_map) {} |
33 | 33 |
| 34 void Init(BookmarkModel* bookmark_model) override {} |
| 35 |
| 36 base::CancelableTaskTracker::TaskId GetFaviconImageForPageURL( |
| 37 const GURL& page_url, |
| 38 favicon_base::IconType type, |
| 39 const favicon_base::FaviconImageCallback& callback, |
| 40 base::CancelableTaskTracker* tracker) override { |
| 41 return base::CancelableTaskTracker::kBadTaskId; |
| 42 } |
| 43 |
34 bool SupportsTypedCountForNodes() override { return true; } | 44 bool SupportsTypedCountForNodes() override { return true; } |
35 | 45 |
36 void GetTypedCountForNodes( | 46 void GetTypedCountForNodes( |
37 const NodeSet& nodes, | 47 const NodeSet& nodes, |
38 NodeTypedCountPairs* node_typed_count_pairs) override { | 48 NodeTypedCountPairs* node_typed_count_pairs) override { |
39 for (NodeSet::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { | 49 for (NodeSet::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { |
40 const BookmarkNode* node = *it; | 50 const BookmarkNode* node = *it; |
41 std::map<GURL, int>::const_iterator found = | 51 std::map<GURL, int>::const_iterator found = |
42 typed_count_map_.find(node->url()); | 52 typed_count_map_.find(node->url()); |
43 if (found == typed_count_map_.end()) | 53 if (found == typed_count_map_.end()) |
44 continue; | 54 continue; |
45 | 55 |
46 node_typed_count_pairs->push_back(std::make_pair(node, found->second)); | 56 node_typed_count_pairs->push_back(std::make_pair(node, found->second)); |
47 } | 57 } |
48 } | 58 } |
49 | 59 |
| 60 void RecordAction(const base::UserMetricsAction& action) override {} |
| 61 |
| 62 LoadExtraCallback GetLoadExtraNodesCallback( |
| 63 scoped_ptr<BookmarkPermanentNode> managed_node, |
| 64 scoped_ptr<BookmarkPermanentNode> supervised_node, |
| 65 const ExtraNodeLoadedCallback& callback) override { |
| 66 return LoadExtraCallback(); |
| 67 } |
| 68 |
| 69 void DoneLoading(BookmarkPermanentNode* managed_node, |
| 70 BookmarkPermanentNode* supervised_node) override {} |
| 71 |
50 private: | 72 private: |
51 const std::map<GURL, int> typed_count_map_; | 73 const std::map<GURL, int> typed_count_map_; |
52 | 74 |
53 DISALLOW_COPY_AND_ASSIGN(BookmarkClientMock); | 75 DISALLOW_COPY_AND_ASSIGN(BookmarkClientMock); |
54 }; | 76 }; |
| 77 } // namespace |
55 | 78 |
56 class BookmarkIndexTest : public testing::Test { | 79 class BookmarkIndexTest : public testing::Test { |
57 public: | 80 public: |
58 BookmarkIndexTest() : model_(client_.CreateModel()) {} | 81 BookmarkIndexTest() : model_(TestBookmarkClient::CreateModel()) {} |
59 | 82 |
60 typedef std::pair<std::string, std::string> TitleAndURL; | 83 typedef std::pair<std::string, std::string> TitleAndURL; |
61 | 84 |
62 void AddBookmarks(const char** titles, const char** urls, size_t count) { | 85 void AddBookmarks(const char** titles, const char** urls, size_t count) { |
63 // The pair is (title, url). | 86 // The pair is (title, url). |
64 std::vector<TitleAndURL> bookmarks; | 87 std::vector<TitleAndURL> bookmarks; |
65 for (size_t i = 0; i < count; ++i) { | 88 for (size_t i = 0; i < count; ++i) { |
66 TitleAndURL bookmark(titles[i], urls[i]); | 89 TitleAndURL bookmark(titles[i], urls[i]); |
67 bookmarks.push_back(bookmark); | 90 bookmarks.push_back(bookmark); |
68 } | 91 } |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
127 void ExpectMatchPositions( | 150 void ExpectMatchPositions( |
128 const BookmarkMatch::MatchPositions& actual_positions, | 151 const BookmarkMatch::MatchPositions& actual_positions, |
129 const BookmarkMatch::MatchPositions& expected_positions) { | 152 const BookmarkMatch::MatchPositions& expected_positions) { |
130 ASSERT_EQ(expected_positions.size(), actual_positions.size()); | 153 ASSERT_EQ(expected_positions.size(), actual_positions.size()); |
131 for (size_t i = 0; i < expected_positions.size(); ++i) { | 154 for (size_t i = 0; i < expected_positions.size(); ++i) { |
132 EXPECT_EQ(expected_positions[i].first, actual_positions[i].first); | 155 EXPECT_EQ(expected_positions[i].first, actual_positions[i].first); |
133 EXPECT_EQ(expected_positions[i].second, actual_positions[i].second); | 156 EXPECT_EQ(expected_positions[i].second, actual_positions[i].second); |
134 } | 157 } |
135 } | 158 } |
136 | 159 |
| 160 scoped_ptr<BookmarkModel> CreateModelWithMockClient( |
| 161 const std::map<GURL, int>& typed_count_map) { |
| 162 scoped_ptr<BookmarkModel> model(new BookmarkModel( |
| 163 make_scoped_ptr(new BookmarkClientMock(typed_count_map)))); |
| 164 scoped_ptr<BookmarkLoadDetails> details = |
| 165 model->CreateLoadDetails(std::string()); |
| 166 details->LoadExtraNodes(); |
| 167 model->DoneLoading(details.Pass()); |
| 168 return model.Pass(); |
| 169 } |
| 170 |
137 protected: | 171 protected: |
138 TestBookmarkClient client_; | |
139 scoped_ptr<BookmarkModel> model_; | 172 scoped_ptr<BookmarkModel> model_; |
140 | 173 |
141 private: | 174 private: |
142 DISALLOW_COPY_AND_ASSIGN(BookmarkIndexTest); | 175 DISALLOW_COPY_AND_ASSIGN(BookmarkIndexTest); |
143 }; | 176 }; |
144 | 177 |
145 // Various permutations with differing input, queries and output that exercises | 178 // Various permutations with differing input, queries and output that exercises |
146 // all query paths. | 179 // all query paths. |
147 TEST_F(BookmarkIndexTest, GetBookmarksMatching) { | 180 TEST_F(BookmarkIndexTest, GetBookmarksMatching) { |
148 struct TestData { | 181 struct TestData { |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 } | 234 } |
202 AddBookmarks(bookmarks); | 235 AddBookmarks(bookmarks); |
203 | 236 |
204 std::vector<std::string> expected; | 237 std::vector<std::string> expected; |
205 if (!data[i].expected.empty()) | 238 if (!data[i].expected.empty()) |
206 base::SplitString(data[i].expected, ';', &expected); | 239 base::SplitString(data[i].expected, ';', &expected); |
207 | 240 |
208 ExpectMatches(data[i].query, query_parser::MatchingAlgorithm::DEFAULT, | 241 ExpectMatches(data[i].query, query_parser::MatchingAlgorithm::DEFAULT, |
209 expected); | 242 expected); |
210 | 243 |
211 model_ = client_.CreateModel(); | 244 model_ = TestBookmarkClient::CreateModel(); |
212 } | 245 } |
213 } | 246 } |
214 | 247 |
215 TEST_F(BookmarkIndexTest, GetBookmarksMatchingAlwaysPrefixSearch) { | 248 TEST_F(BookmarkIndexTest, GetBookmarksMatchingAlwaysPrefixSearch) { |
216 struct TestData { | 249 struct TestData { |
217 const std::string titles; | 250 const std::string titles; |
218 const std::string query; | 251 const std::string query; |
219 const std::string expected; | 252 const std::string expected; |
220 } data[] = { | 253 } data[] = { |
221 // Trivial test case of only one term, exact match. | 254 // Trivial test case of only one term, exact match. |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 AddBookmarks(bookmarks); | 294 AddBookmarks(bookmarks); |
262 | 295 |
263 std::vector<std::string> expected; | 296 std::vector<std::string> expected; |
264 if (!data[i].expected.empty()) | 297 if (!data[i].expected.empty()) |
265 base::SplitString(data[i].expected, ';', &expected); | 298 base::SplitString(data[i].expected, ';', &expected); |
266 | 299 |
267 ExpectMatches(data[i].query, | 300 ExpectMatches(data[i].query, |
268 query_parser::MatchingAlgorithm::ALWAYS_PREFIX_SEARCH, | 301 query_parser::MatchingAlgorithm::ALWAYS_PREFIX_SEARCH, |
269 expected); | 302 expected); |
270 | 303 |
271 model_ = client_.CreateModel(); | 304 model_ = TestBookmarkClient::CreateModel(); |
272 } | 305 } |
273 } | 306 } |
274 | 307 |
275 // Analogous to GetBookmarksMatching, this test tests various permutations | 308 // Analogous to GetBookmarksMatching, this test tests various permutations |
276 // of title, URL, and input to see if the title/URL matches the input as | 309 // of title, URL, and input to see if the title/URL matches the input as |
277 // expected. | 310 // expected. |
278 TEST_F(BookmarkIndexTest, GetBookmarksMatchingWithURLs) { | 311 TEST_F(BookmarkIndexTest, GetBookmarksMatchingWithURLs) { |
279 struct TestData { | 312 struct TestData { |
280 const std::string query; | 313 const std::string query; |
281 const std::string title; | 314 const std::string title; |
(...skipping 28 matching lines...) Expand all Loading... |
310 { "foo bar", "Baz Barly", "http://blah.com/foodie", true }, | 343 { "foo bar", "Baz Barly", "http://blah.com/foodie", true }, |
311 { "foo bar", "Baz Bur", "http://blah.com/foo/bar", true }, | 344 { "foo bar", "Baz Bur", "http://blah.com/foo/bar", true }, |
312 { "foo bar", "Baz Bur", "http://blah.com/food/barly", true }, | 345 { "foo bar", "Baz Bur", "http://blah.com/food/barly", true }, |
313 { "foo bar", "Baz Bur", "http://bar.com/blah/foo", true }, | 346 { "foo bar", "Baz Bur", "http://bar.com/blah/foo", true }, |
314 { "foo bar", "Baz Bur", "http://barly.com/blah/food", true }, | 347 { "foo bar", "Baz Bur", "http://barly.com/blah/food", true }, |
315 { "foo bar", "Baz Bur", "http://bar.com/blah/flub", false }, | 348 { "foo bar", "Baz Bur", "http://bar.com/blah/flub", false }, |
316 { "foo bar", "Baz Bur", "http://foo.com/blah/flub", false } | 349 { "foo bar", "Baz Bur", "http://foo.com/blah/flub", false } |
317 }; | 350 }; |
318 | 351 |
319 for (size_t i = 0; i < arraysize(data); ++i) { | 352 for (size_t i = 0; i < arraysize(data); ++i) { |
320 model_ = client_.CreateModel(); | 353 model_ = TestBookmarkClient::CreateModel(); |
321 std::vector<TitleAndURL> bookmarks; | 354 std::vector<TitleAndURL> bookmarks; |
322 bookmarks.push_back(TitleAndURL(data[i].title, data[i].url)); | 355 bookmarks.push_back(TitleAndURL(data[i].title, data[i].url)); |
323 AddBookmarks(bookmarks); | 356 AddBookmarks(bookmarks); |
324 | 357 |
325 std::vector<std::string> expected; | 358 std::vector<std::string> expected; |
326 if (data[i].should_be_retrieved) | 359 if (data[i].should_be_retrieved) |
327 expected.push_back(data[i].title); | 360 expected.push_back(data[i].title); |
328 | 361 |
329 ExpectMatches(data[i].query, query_parser::MatchingAlgorithm::DEFAULT, | 362 ExpectMatches(data[i].query, query_parser::MatchingAlgorithm::DEFAULT, |
330 expected); | 363 expected); |
(...skipping 17 matching lines...) Expand all Loading... |
348 { "foo\xc3\xa4-test", "foo" }, | 381 { "foo\xc3\xa4-test", "foo" }, |
349 { "foo", "foo" } | 382 { "foo", "foo" } |
350 }; | 383 }; |
351 | 384 |
352 GURL url(kAboutBlankURL); | 385 GURL url(kAboutBlankURL); |
353 for (size_t i = 0; i < arraysize(data); ++i) { | 386 for (size_t i = 0; i < arraysize(data); ++i) { |
354 model_->AddURL(model_->other_node(), 0, UTF8ToUTF16(data[i].title), url); | 387 model_->AddURL(model_->other_node(), 0, UTF8ToUTF16(data[i].title), url); |
355 std::vector<BookmarkMatch> matches; | 388 std::vector<BookmarkMatch> matches; |
356 model_->GetBookmarksMatching(UTF8ToUTF16(data[i].query), 10, &matches); | 389 model_->GetBookmarksMatching(UTF8ToUTF16(data[i].query), 10, &matches); |
357 EXPECT_EQ(1u, matches.size()); | 390 EXPECT_EQ(1u, matches.size()); |
358 model_ = client_.CreateModel(); | 391 model_ = TestBookmarkClient::CreateModel(); |
359 } | 392 } |
360 } | 393 } |
361 | 394 |
362 // Makes sure match positions are updated appropriately for title matches. | 395 // Makes sure match positions are updated appropriately for title matches. |
363 TEST_F(BookmarkIndexTest, MatchPositionsTitles) { | 396 TEST_F(BookmarkIndexTest, MatchPositionsTitles) { |
364 struct TestData { | 397 struct TestData { |
365 const std::string title; | 398 const std::string title; |
366 const std::string query; | 399 const std::string query; |
367 const std::string expected_title_match_positions; | 400 const std::string expected_title_match_positions; |
368 } data[] = { | 401 } data[] = { |
(...skipping 16 matching lines...) Expand all Loading... |
385 std::vector<BookmarkMatch> matches; | 418 std::vector<BookmarkMatch> matches; |
386 model_->GetBookmarksMatching(ASCIIToUTF16(data[i].query), 1000, &matches); | 419 model_->GetBookmarksMatching(ASCIIToUTF16(data[i].query), 1000, &matches); |
387 ASSERT_EQ(1U, matches.size()); | 420 ASSERT_EQ(1U, matches.size()); |
388 | 421 |
389 BookmarkMatch::MatchPositions expected_title_matches; | 422 BookmarkMatch::MatchPositions expected_title_matches; |
390 ExtractMatchPositions(data[i].expected_title_match_positions, | 423 ExtractMatchPositions(data[i].expected_title_match_positions, |
391 &expected_title_matches); | 424 &expected_title_matches); |
392 ExpectMatchPositions(matches[0].title_match_positions, | 425 ExpectMatchPositions(matches[0].title_match_positions, |
393 expected_title_matches); | 426 expected_title_matches); |
394 | 427 |
395 model_ = client_.CreateModel(); | 428 model_ = TestBookmarkClient::CreateModel(); |
396 } | 429 } |
397 } | 430 } |
398 | 431 |
399 // Makes sure match positions are updated appropriately for URL matches. | 432 // Makes sure match positions are updated appropriately for URL matches. |
400 TEST_F(BookmarkIndexTest, MatchPositionsURLs) { | 433 TEST_F(BookmarkIndexTest, MatchPositionsURLs) { |
401 // The encoded stuff between /wiki/ and the # is 第二次世界大戦 | 434 // The encoded stuff between /wiki/ and the # is 第二次世界大戦 |
402 const std::string ja_wiki_url = "http://ja.wikipedia.org/wiki/%E7%AC%AC%E4" | 435 const std::string ja_wiki_url = "http://ja.wikipedia.org/wiki/%E7%AC%AC%E4" |
403 "%BA%8C%E6%AC%A1%E4%B8%96%E7%95%8C%E5%A4%A7%E6%88%A6#.E3.83.B4.E3.82.A7" | 436 "%BA%8C%E6%AC%A1%E4%B8%96%E7%95%8C%E5%A4%A7%E6%88%A6#.E3.83.B4.E3.82.A7" |
404 ".E3.83.AB.E3.82.B5.E3.82.A4.E3.83.A6.E4.BD.93.E5.88.B6"; | 437 ".E3.83.AB.E3.82.B5.E3.82.A4.E3.83.A6.E4.BD.93.E5.88.B6"; |
405 struct TestData { | 438 struct TestData { |
(...skipping 15 matching lines...) Expand all Loading... |
421 { "http foo", "http://www.foo.com/", "0,4:11,14" }, | 454 { "http foo", "http://www.foo.com/", "0,4:11,14" }, |
422 { "http foo", "http://www.bar.com/baz/foodie/hi", "0,4:23,26" }, | 455 { "http foo", "http://www.bar.com/baz/foodie/hi", "0,4:23,26" }, |
423 { "第二次", ja_wiki_url, "29,56" }, | 456 { "第二次", ja_wiki_url, "29,56" }, |
424 { "ja 第二次", ja_wiki_url, "7,9:29,56" }, | 457 { "ja 第二次", ja_wiki_url, "7,9:29,56" }, |
425 { "第二次 E3.8", ja_wiki_url, "29,56:94,98:103,107:" | 458 { "第二次 E3.8", ja_wiki_url, "29,56:94,98:103,107:" |
426 "112,116:121,125:" | 459 "112,116:121,125:" |
427 "130,134:139,143" } | 460 "130,134:139,143" } |
428 }; | 461 }; |
429 | 462 |
430 for (size_t i = 0; i < arraysize(data); ++i) { | 463 for (size_t i = 0; i < arraysize(data); ++i) { |
431 model_ = client_.CreateModel(); | 464 model_ = TestBookmarkClient::CreateModel(); |
432 std::vector<TitleAndURL> bookmarks; | 465 std::vector<TitleAndURL> bookmarks; |
433 TitleAndURL bookmark("123456", data[i].url); | 466 TitleAndURL bookmark("123456", data[i].url); |
434 bookmarks.push_back(bookmark); | 467 bookmarks.push_back(bookmark); |
435 AddBookmarks(bookmarks); | 468 AddBookmarks(bookmarks); |
436 | 469 |
437 std::vector<BookmarkMatch> matches; | 470 std::vector<BookmarkMatch> matches; |
438 model_->GetBookmarksMatching(UTF8ToUTF16(data[i].query), 1000, &matches); | 471 model_->GetBookmarksMatching(UTF8ToUTF16(data[i].query), 1000, &matches); |
439 ASSERT_EQ(1U, matches.size()) << data[i].url << data[i].query; | 472 ASSERT_EQ(1U, matches.size()) << data[i].url << data[i].query; |
440 | 473 |
441 BookmarkMatch::MatchPositions expected_url_matches; | 474 BookmarkMatch::MatchPositions expected_url_matches; |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
514 { GURL("http://www.google.com/"), "Google", 100 }, | 547 { GURL("http://www.google.com/"), "Google", 100 }, |
515 { GURL("http://maps.google.com/"), "Google Maps", 40 }, | 548 { GURL("http://maps.google.com/"), "Google Maps", 40 }, |
516 { GURL("http://docs.google.com/"), "Google Docs", 50 }, | 549 { GURL("http://docs.google.com/"), "Google Docs", 50 }, |
517 { GURL("http://reader.google.com/"), "Google Reader", 80 }, | 550 { GURL("http://reader.google.com/"), "Google Reader", 80 }, |
518 }; | 551 }; |
519 | 552 |
520 std::map<GURL, int> typed_count_map; | 553 std::map<GURL, int> typed_count_map; |
521 for (size_t i = 0; i < arraysize(data); ++i) | 554 for (size_t i = 0; i < arraysize(data); ++i) |
522 typed_count_map.insert(std::make_pair(data[i].url, data[i].typed_count)); | 555 typed_count_map.insert(std::make_pair(data[i].url, data[i].typed_count)); |
523 | 556 |
524 BookmarkClientMock client(typed_count_map); | 557 scoped_ptr<BookmarkModel> model(CreateModelWithMockClient(typed_count_map)); |
525 scoped_ptr<BookmarkModel> model = client.CreateModel(); | |
526 | 558 |
527 for (size_t i = 0; i < arraysize(data); ++i) | 559 for (size_t i = 0; i < arraysize(data); ++i) |
528 // Populate the BookmarkIndex. | 560 // Populate the BookmarkIndex. |
529 model->AddURL( | 561 model->AddURL( |
530 model->other_node(), i, UTF8ToUTF16(data[i].title), data[i].url); | 562 model->other_node(), i, UTF8ToUTF16(data[i].title), data[i].url); |
531 | 563 |
532 // Populate match nodes. | 564 // Populate match nodes. |
533 std::vector<BookmarkMatch> matches; | 565 std::vector<BookmarkMatch> matches; |
534 model->GetBookmarksMatching(ASCIIToUTF16("google"), 4, &matches); | 566 model->GetBookmarksMatching(ASCIIToUTF16("google"), 4, &matches); |
535 | 567 |
(...skipping 10 matching lines...) Expand all Loading... |
546 | 578 |
547 matches.clear(); | 579 matches.clear(); |
548 // Select top two matches. | 580 // Select top two matches. |
549 model->GetBookmarksMatching(ASCIIToUTF16("google"), 2, &matches); | 581 model->GetBookmarksMatching(ASCIIToUTF16("google"), 2, &matches); |
550 | 582 |
551 ASSERT_EQ(2U, matches.size()); | 583 ASSERT_EQ(2U, matches.size()); |
552 EXPECT_EQ(data[0].url, matches[0].node->url()); | 584 EXPECT_EQ(data[0].url, matches[0].node->url()); |
553 EXPECT_EQ(data[3].url, matches[1].node->url()); | 585 EXPECT_EQ(data[3].url, matches[1].node->url()); |
554 } | 586 } |
555 | 587 |
556 } // namespace | |
557 } // namespace bookmarks | 588 } // namespace bookmarks |
OLD | NEW |