| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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 <fstream> | 5 #include <fstream> |
| 6 #include <iostream> | 6 #include <iostream> |
| 7 | 7 |
| 8 #include "base/file_util.h" | 8 #include "base/file_util.h" |
| 9 #include "base/path_service.h" | 9 #include "base/path_service.h" |
| 10 #include "base/platform_test.h" | 10 #include "base/platform_test.h" |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 file_util::ReadFileToString(file_path, &source_buffer_); | 67 file_util::ReadFileToString(file_path, &source_buffer_); |
| 68 | 68 |
| 69 // Encode the data with deflate | 69 // Encode the data with deflate |
| 70 deflate_encode_buffer_ = new char[kDefaultBufferSize]; | 70 deflate_encode_buffer_ = new char[kDefaultBufferSize]; |
| 71 ASSERT_TRUE(deflate_encode_buffer_ != NULL); | 71 ASSERT_TRUE(deflate_encode_buffer_ != NULL); |
| 72 | 72 |
| 73 deflate_encode_len_ = kDefaultBufferSize; | 73 deflate_encode_len_ = kDefaultBufferSize; |
| 74 int code = CompressAll(ENCODE_DEFLATE , source_buffer(), source_len(), | 74 int code = CompressAll(ENCODE_DEFLATE , source_buffer(), source_len(), |
| 75 deflate_encode_buffer_, &deflate_encode_len_); | 75 deflate_encode_buffer_, &deflate_encode_len_); |
| 76 ASSERT_TRUE(code == Z_STREAM_END); | 76 ASSERT_TRUE(code == Z_STREAM_END); |
| 77 ASSERT_TRUE(deflate_encode_len_ > 0); | 77 ASSERT_GT(deflate_encode_len_, 0); |
| 78 ASSERT_TRUE(deflate_encode_len_ <= kDefaultBufferSize); | 78 ASSERT_TRUE(deflate_encode_len_ <= kDefaultBufferSize); |
| 79 | 79 |
| 80 // Encode the data with gzip | 80 // Encode the data with gzip |
| 81 gzip_encode_buffer_ = new char[kDefaultBufferSize]; | 81 gzip_encode_buffer_ = new char[kDefaultBufferSize]; |
| 82 ASSERT_TRUE(gzip_encode_buffer_ != NULL); | 82 ASSERT_TRUE(gzip_encode_buffer_ != NULL); |
| 83 | 83 |
| 84 gzip_encode_len_ = kDefaultBufferSize; | 84 gzip_encode_len_ = kDefaultBufferSize; |
| 85 code = CompressAll(ENCODE_GZIP, source_buffer(), source_len(), | 85 code = CompressAll(ENCODE_GZIP, source_buffer(), source_len(), |
| 86 gzip_encode_buffer_, &gzip_encode_len_); | 86 gzip_encode_buffer_, &gzip_encode_len_); |
| 87 ASSERT_TRUE(code == Z_STREAM_END); | 87 ASSERT_TRUE(code == Z_STREAM_END); |
| 88 ASSERT_TRUE(gzip_encode_len_ > 0); | 88 ASSERT_GT(gzip_encode_len_, 0); |
| 89 ASSERT_TRUE(gzip_encode_len_ <= kDefaultBufferSize); | 89 ASSERT_TRUE(gzip_encode_len_ <= kDefaultBufferSize); |
| 90 } | 90 } |
| 91 | 91 |
| 92 virtual void TearDown() { | 92 virtual void TearDown() { |
| 93 delete[] deflate_encode_buffer_; | 93 delete[] deflate_encode_buffer_; |
| 94 deflate_encode_buffer_ = NULL; | 94 deflate_encode_buffer_ = NULL; |
| 95 | 95 |
| 96 delete[] gzip_encode_buffer_; | 96 delete[] gzip_encode_buffer_; |
| 97 gzip_encode_buffer_ = NULL; | 97 gzip_encode_buffer_ = NULL; |
| 98 | 98 |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 221 char* deflate_encode_buffer_; | 221 char* deflate_encode_buffer_; |
| 222 int deflate_encode_len_; | 222 int deflate_encode_len_; |
| 223 | 223 |
| 224 char* gzip_encode_buffer_; | 224 char* gzip_encode_buffer_; |
| 225 int gzip_encode_len_; | 225 int gzip_encode_len_; |
| 226 }; | 226 }; |
| 227 | 227 |
| 228 // Basic scenario: decoding deflate data with big enough buffer. | 228 // Basic scenario: decoding deflate data with big enough buffer. |
| 229 TEST_F(GZipUnitTest, DecodeDeflate) { | 229 TEST_F(GZipUnitTest, DecodeDeflate) { |
| 230 // Decode the compressed data with filter | 230 // Decode the compressed data with filter |
| 231 std::vector<std::string> filters; | 231 std::vector<Filter::FilterType> filter_types; |
| 232 filters.push_back("deflate"); | 232 filter_types.push_back(Filter::FILTER_TYPE_DEFLATE); |
| 233 scoped_ptr<Filter> filter( | 233 scoped_ptr<Filter> filter(Filter::Factory(filter_types, kDefaultBufferSize)); |
| 234 Filter::Factory(filters, kApplicationOctetStream, kDefaultBufferSize)); | |
| 235 ASSERT_TRUE(filter.get()); | 234 ASSERT_TRUE(filter.get()); |
| 236 memcpy(filter->stream_buffer(), deflate_encode_buffer_, deflate_encode_len_); | 235 memcpy(filter->stream_buffer(), deflate_encode_buffer_, deflate_encode_len_); |
| 237 filter->FlushStreamBuffer(deflate_encode_len_); | 236 filter->FlushStreamBuffer(deflate_encode_len_); |
| 238 | 237 |
| 239 char deflate_decode_buffer[kDefaultBufferSize]; | 238 char deflate_decode_buffer[kDefaultBufferSize]; |
| 240 int deflate_decode_size = kDefaultBufferSize; | 239 int deflate_decode_size = kDefaultBufferSize; |
| 241 filter->ReadData(deflate_decode_buffer, &deflate_decode_size); | 240 filter->ReadData(deflate_decode_buffer, &deflate_decode_size); |
| 242 | 241 |
| 243 // Compare the decoding result with source data | 242 // Compare the decoding result with source data |
| 244 EXPECT_TRUE(deflate_decode_size == source_len()); | 243 EXPECT_TRUE(deflate_decode_size == source_len()); |
| 245 EXPECT_EQ(memcmp(source_buffer(), deflate_decode_buffer, source_len()), 0); | 244 EXPECT_EQ(memcmp(source_buffer(), deflate_decode_buffer, source_len()), 0); |
| 246 } | 245 } |
| 247 | 246 |
| 248 // Basic scenario: decoding gzip data with big enough buffer. | 247 // Basic scenario: decoding gzip data with big enough buffer. |
| 249 TEST_F(GZipUnitTest, DecodeGZip) { | 248 TEST_F(GZipUnitTest, DecodeGZip) { |
| 250 // Decode the compressed data with filter | 249 // Decode the compressed data with filter |
| 251 std::vector<std::string> filters; | 250 std::vector<Filter::FilterType> filter_types; |
| 252 filters.push_back("gzip"); | 251 filter_types.push_back(Filter::FILTER_TYPE_GZIP); |
| 253 scoped_ptr<Filter> filter( | 252 scoped_ptr<Filter> filter(Filter::Factory(filter_types, kDefaultBufferSize)); |
| 254 Filter::Factory(filters, kApplicationOctetStream, kDefaultBufferSize)); | |
| 255 ASSERT_TRUE(filter.get()); | 253 ASSERT_TRUE(filter.get()); |
| 256 memcpy(filter->stream_buffer(), gzip_encode_buffer_, gzip_encode_len_); | 254 memcpy(filter->stream_buffer(), gzip_encode_buffer_, gzip_encode_len_); |
| 257 filter->FlushStreamBuffer(gzip_encode_len_); | 255 filter->FlushStreamBuffer(gzip_encode_len_); |
| 258 | 256 |
| 259 char gzip_decode_buffer[kDefaultBufferSize]; | 257 char gzip_decode_buffer[kDefaultBufferSize]; |
| 260 int gzip_decode_size = kDefaultBufferSize; | 258 int gzip_decode_size = kDefaultBufferSize; |
| 261 filter->ReadData(gzip_decode_buffer, &gzip_decode_size); | 259 filter->ReadData(gzip_decode_buffer, &gzip_decode_size); |
| 262 | 260 |
| 263 // Compare the decoding result with source data | 261 // Compare the decoding result with source data |
| 264 EXPECT_TRUE(gzip_decode_size == source_len()); | 262 EXPECT_TRUE(gzip_decode_size == source_len()); |
| 265 EXPECT_EQ(memcmp(source_buffer(), gzip_decode_buffer, source_len()), 0); | 263 EXPECT_EQ(memcmp(source_buffer(), gzip_decode_buffer, source_len()), 0); |
| 266 } | 264 } |
| 267 | 265 |
| 268 // Tests we can call filter repeatedly to get all the data decoded. | 266 // Tests we can call filter repeatedly to get all the data decoded. |
| 269 // To do that, we create a filter with a small buffer that can not hold all | 267 // To do that, we create a filter with a small buffer that can not hold all |
| 270 // the input data. | 268 // the input data. |
| 271 TEST_F(GZipUnitTest, DecodeWithSmallBuffer) { | 269 TEST_F(GZipUnitTest, DecodeWithSmallBuffer) { |
| 272 std::vector<std::string> filters; | 270 std::vector<Filter::FilterType> filter_types; |
| 273 filters.push_back("deflate"); | 271 filter_types.push_back(Filter::FILTER_TYPE_DEFLATE); |
| 274 scoped_ptr<Filter> filter( | 272 scoped_ptr<Filter> filter(Filter::Factory(filter_types, kSmallBufferSize)); |
| 275 Filter::Factory(filters, kApplicationOctetStream, kSmallBufferSize)); | |
| 276 ASSERT_TRUE(filter.get()); | 273 ASSERT_TRUE(filter.get()); |
| 277 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), | 274 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), |
| 278 deflate_encode_buffer_, deflate_encode_len_, | 275 deflate_encode_buffer_, deflate_encode_len_, |
| 279 kDefaultBufferSize); | 276 kDefaultBufferSize); |
| 280 } | 277 } |
| 281 | 278 |
| 282 // Tests we can still decode with just 1 byte buffer in the filter. | 279 // Tests we can still decode with just 1 byte buffer in the filter. |
| 283 // The purpose of this tests are two: (1) Verify filter can parse partial GZip | 280 // The purpose of this tests are two: (1) Verify filter can parse partial GZip |
| 284 // header correctly. (2) Sometimes the filter will consume input without | 281 // header correctly. (2) Sometimes the filter will consume input without |
| 285 // generating output. Verify filter can handle it correctly. | 282 // generating output. Verify filter can handle it correctly. |
| 286 TEST_F(GZipUnitTest, DecodeWithOneByteBuffer) { | 283 TEST_F(GZipUnitTest, DecodeWithOneByteBuffer) { |
| 287 std::vector<std::string> filters; | 284 std::vector<Filter::FilterType> filter_types; |
| 288 filters.push_back("gzip"); | 285 filter_types.push_back(Filter::FILTER_TYPE_GZIP); |
| 289 scoped_ptr<Filter> filter( | 286 scoped_ptr<Filter> filter(Filter::Factory(filter_types, 1)); |
| 290 Filter::Factory(filters, kApplicationOctetStream, 1)); | |
| 291 ASSERT_TRUE(filter.get()); | 287 ASSERT_TRUE(filter.get()); |
| 292 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), | 288 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), |
| 293 gzip_encode_buffer_, gzip_encode_len_, | 289 gzip_encode_buffer_, gzip_encode_len_, |
| 294 kDefaultBufferSize); | 290 kDefaultBufferSize); |
| 295 } | 291 } |
| 296 | 292 |
| 297 // Tests we can decode when caller has small buffer to read out from filter. | 293 // Tests we can decode when caller has small buffer to read out from filter. |
| 298 TEST_F(GZipUnitTest, DecodeWithSmallOutputBuffer) { | 294 TEST_F(GZipUnitTest, DecodeWithSmallOutputBuffer) { |
| 299 std::vector<std::string> filters; | 295 std::vector<Filter::FilterType> filter_types; |
| 300 filters.push_back("deflate"); | 296 filter_types.push_back(Filter::FILTER_TYPE_DEFLATE); |
| 301 scoped_ptr<Filter> filter( | 297 scoped_ptr<Filter> filter(Filter::Factory(filter_types, kDefaultBufferSize)); |
| 302 Filter::Factory(filters, kApplicationOctetStream, kDefaultBufferSize)); | |
| 303 ASSERT_TRUE(filter.get()); | 298 ASSERT_TRUE(filter.get()); |
| 304 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), | 299 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), |
| 305 deflate_encode_buffer_, deflate_encode_len_, | 300 deflate_encode_buffer_, deflate_encode_len_, |
| 306 kSmallBufferSize); | 301 kSmallBufferSize); |
| 307 } | 302 } |
| 308 | 303 |
| 309 // Tests we can still decode with just 1 byte buffer in the filter and just 1 | 304 // Tests we can still decode with just 1 byte buffer in the filter and just 1 |
| 310 // byte buffer in the caller. | 305 // byte buffer in the caller. |
| 311 TEST_F(GZipUnitTest, DecodeWithOneByteInputAndOutputBuffer) { | 306 TEST_F(GZipUnitTest, DecodeWithOneByteInputAndOutputBuffer) { |
| 312 std::vector<std::string> filters; | 307 std::vector<Filter::FilterType> filter_types; |
| 313 filters.push_back("gzip"); | 308 filter_types.push_back(Filter::FILTER_TYPE_GZIP); |
| 314 scoped_ptr<Filter> filter( | 309 scoped_ptr<Filter> filter(Filter::Factory(filter_types, 1)); |
| 315 Filter::Factory(filters, kApplicationOctetStream, 1)); | |
| 316 ASSERT_TRUE(filter.get()); | 310 ASSERT_TRUE(filter.get()); |
| 317 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), | 311 DecodeAndCompareWithFilter(filter.get(), source_buffer(), source_len(), |
| 318 gzip_encode_buffer_, gzip_encode_len_, 1); | 312 gzip_encode_buffer_, gzip_encode_len_, 1); |
| 319 } | 313 } |
| 320 | 314 |
| 321 // Decoding deflate stream with corrupted data. | 315 // Decoding deflate stream with corrupted data. |
| 322 TEST_F(GZipUnitTest, DecodeCorruptedData) { | 316 TEST_F(GZipUnitTest, DecodeCorruptedData) { |
| 323 char corrupt_data[kDefaultBufferSize]; | 317 char corrupt_data[kDefaultBufferSize]; |
| 324 int corrupt_data_len = deflate_encode_len_; | 318 int corrupt_data_len = deflate_encode_len_; |
| 325 memcpy(corrupt_data, deflate_encode_buffer_, deflate_encode_len_); | 319 memcpy(corrupt_data, deflate_encode_buffer_, deflate_encode_len_); |
| 326 | 320 |
| 327 int pos = corrupt_data_len / 2; | 321 int pos = corrupt_data_len / 2; |
| 328 corrupt_data[pos] = !corrupt_data[pos]; | 322 corrupt_data[pos] = !corrupt_data[pos]; |
| 329 | 323 |
| 330 // Decode the corrupted data with filter | 324 // Decode the corrupted data with filter |
| 331 std::vector<std::string> filters; | 325 std::vector<Filter::FilterType> filter_types; |
| 332 filters.push_back("deflate"); | 326 filter_types.push_back(Filter::FILTER_TYPE_DEFLATE); |
| 333 scoped_ptr<Filter> filter( | 327 scoped_ptr<Filter> filter(Filter::Factory(filter_types, kDefaultBufferSize)); |
| 334 Filter::Factory(filters, kApplicationOctetStream, kDefaultBufferSize)); | |
| 335 ASSERT_TRUE(filter.get()); | 328 ASSERT_TRUE(filter.get()); |
| 336 char corrupt_decode_buffer[kDefaultBufferSize]; | 329 char corrupt_decode_buffer[kDefaultBufferSize]; |
| 337 int corrupt_decode_size = kDefaultBufferSize; | 330 int corrupt_decode_size = kDefaultBufferSize; |
| 338 | 331 |
| 339 int code = DecodeAllWithFilter(filter.get(), corrupt_data, corrupt_data_len, | 332 int code = DecodeAllWithFilter(filter.get(), corrupt_data, corrupt_data_len, |
| 340 corrupt_decode_buffer, &corrupt_decode_size); | 333 corrupt_decode_buffer, &corrupt_decode_size); |
| 341 | 334 |
| 342 // Expect failures | 335 // Expect failures |
| 343 EXPECT_TRUE(code == Filter::FILTER_ERROR); | 336 EXPECT_TRUE(code == Filter::FILTER_ERROR); |
| 344 } | 337 } |
| 345 | 338 |
| 346 // Decoding deflate stream with missing data. | 339 // Decoding deflate stream with missing data. |
| 347 TEST_F(GZipUnitTest, DecodeMissingData) { | 340 TEST_F(GZipUnitTest, DecodeMissingData) { |
| 348 char corrupt_data[kDefaultBufferSize]; | 341 char corrupt_data[kDefaultBufferSize]; |
| 349 int corrupt_data_len = deflate_encode_len_; | 342 int corrupt_data_len = deflate_encode_len_; |
| 350 memcpy(corrupt_data, deflate_encode_buffer_, deflate_encode_len_); | 343 memcpy(corrupt_data, deflate_encode_buffer_, deflate_encode_len_); |
| 351 | 344 |
| 352 int pos = corrupt_data_len / 2; | 345 int pos = corrupt_data_len / 2; |
| 353 int len = corrupt_data_len - pos - 1; | 346 int len = corrupt_data_len - pos - 1; |
| 354 memmove(&corrupt_data[pos], &corrupt_data[pos+1], len); | 347 memmove(&corrupt_data[pos], &corrupt_data[pos+1], len); |
| 355 --corrupt_data_len; | 348 --corrupt_data_len; |
| 356 | 349 |
| 357 // Decode the corrupted data with filter | 350 // Decode the corrupted data with filter |
| 358 std::vector<std::string> filters; | 351 std::vector<Filter::FilterType> filter_types; |
| 359 filters.push_back("deflate"); | 352 filter_types.push_back(Filter::FILTER_TYPE_DEFLATE); |
| 360 scoped_ptr<Filter> filter( | 353 scoped_ptr<Filter> filter(Filter::Factory(filter_types, kDefaultBufferSize)); |
| 361 Filter::Factory(filters, kApplicationOctetStream, kDefaultBufferSize)); | |
| 362 ASSERT_TRUE(filter.get()); | 354 ASSERT_TRUE(filter.get()); |
| 363 char corrupt_decode_buffer[kDefaultBufferSize]; | 355 char corrupt_decode_buffer[kDefaultBufferSize]; |
| 364 int corrupt_decode_size = kDefaultBufferSize; | 356 int corrupt_decode_size = kDefaultBufferSize; |
| 365 | 357 |
| 366 int code = DecodeAllWithFilter(filter.get(), corrupt_data, corrupt_data_len, | 358 int code = DecodeAllWithFilter(filter.get(), corrupt_data, corrupt_data_len, |
| 367 corrupt_decode_buffer, &corrupt_decode_size); | 359 corrupt_decode_buffer, &corrupt_decode_size); |
| 368 | 360 |
| 369 // Expect failures | 361 // Expect failures |
| 370 EXPECT_EQ(Filter::FILTER_ERROR, code); | 362 EXPECT_EQ(Filter::FILTER_ERROR, code); |
| 371 } | 363 } |
| 372 | 364 |
| 373 // Decoding gzip stream with corrupted header. | 365 // Decoding gzip stream with corrupted header. |
| 374 TEST_F(GZipUnitTest, DecodeCorruptedHeader) { | 366 TEST_F(GZipUnitTest, DecodeCorruptedHeader) { |
| 375 char corrupt_data[kDefaultBufferSize]; | 367 char corrupt_data[kDefaultBufferSize]; |
| 376 int corrupt_data_len = gzip_encode_len_; | 368 int corrupt_data_len = gzip_encode_len_; |
| 377 memcpy(corrupt_data, gzip_encode_buffer_, gzip_encode_len_); | 369 memcpy(corrupt_data, gzip_encode_buffer_, gzip_encode_len_); |
| 378 | 370 |
| 379 corrupt_data[2] = !corrupt_data[2]; | 371 corrupt_data[2] = !corrupt_data[2]; |
| 380 | 372 |
| 381 // Decode the corrupted data with filter | 373 // Decode the corrupted data with filter |
| 382 std::vector<std::string> filters; | 374 std::vector<Filter::FilterType> filter_types; |
| 383 filters.push_back("gzip"); | 375 filter_types.push_back(Filter::FILTER_TYPE_GZIP); |
| 384 scoped_ptr<Filter> filter( | 376 scoped_ptr<Filter> filter(Filter::Factory(filter_types, kDefaultBufferSize)); |
| 385 Filter::Factory(filters, kApplicationOctetStream, kDefaultBufferSize)); | |
| 386 ASSERT_TRUE(filter.get()); | 377 ASSERT_TRUE(filter.get()); |
| 387 char corrupt_decode_buffer[kDefaultBufferSize]; | 378 char corrupt_decode_buffer[kDefaultBufferSize]; |
| 388 int corrupt_decode_size = kDefaultBufferSize; | 379 int corrupt_decode_size = kDefaultBufferSize; |
| 389 | 380 |
| 390 int code = DecodeAllWithFilter(filter.get(), corrupt_data, corrupt_data_len, | 381 int code = DecodeAllWithFilter(filter.get(), corrupt_data, corrupt_data_len, |
| 391 corrupt_decode_buffer, &corrupt_decode_size); | 382 corrupt_decode_buffer, &corrupt_decode_size); |
| 392 | 383 |
| 393 // Expect failures | 384 // Expect failures |
| 394 EXPECT_TRUE(code == Filter::FILTER_ERROR); | 385 EXPECT_TRUE(code == Filter::FILTER_ERROR); |
| 395 } | 386 } |
| 396 | 387 |
| 397 TEST_F(GZipUnitTest, ApacheWorkaround) { | |
| 398 const int kBufferSize = kDefaultBufferSize; // To fit in 80 cols. | |
| 399 scoped_ptr<Filter> filter; | |
| 400 | |
| 401 std::vector<std::string> gzip_filters, x_gzip_filters; | |
| 402 gzip_filters.push_back("gzip"); | |
| 403 x_gzip_filters.push_back("x-gzip"); | |
| 404 | |
| 405 filter.reset(Filter::Factory(gzip_filters, kApplicationXGzip, kBufferSize)); | |
| 406 EXPECT_FALSE(filter.get()); | |
| 407 filter.reset(Filter::Factory(gzip_filters, kApplicationGzip, kBufferSize)); | |
| 408 EXPECT_FALSE(filter.get()); | |
| 409 filter.reset(Filter::Factory(gzip_filters, kApplicationXGunzip, kBufferSize)); | |
| 410 EXPECT_FALSE(filter.get()); | |
| 411 | |
| 412 filter.reset(Filter::Factory(x_gzip_filters, kApplicationXGzip, kBufferSize)); | |
| 413 EXPECT_FALSE(filter.get()); | |
| 414 filter.reset(Filter::Factory(x_gzip_filters, kApplicationGzip, kBufferSize)); | |
| 415 EXPECT_FALSE(filter.get()); | |
| 416 filter.reset(Filter::Factory(x_gzip_filters, kApplicationXGunzip, | |
| 417 kBufferSize)); | |
| 418 EXPECT_FALSE(filter.get()); | |
| 419 } | |
| 420 | |
| 421 } // namespace | 388 } // namespace |
| OLD | NEW |