| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromeos/printing/ppd_metadata_parser.h" |
| |
| #include "base/json/json_reader.h" |
| #include "base/strings/string_piece.h" |
| #include "chromeos/printing/ppd_metadata_matchers.h" |
| #include "testing/gmock/include/gmock/gmock-matchers.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace chromeos { |
| namespace { |
| |
| using ::testing::AllOf; |
| using ::testing::ElementsAre; |
| using ::testing::Eq; |
| using ::testing::ExplainMatchResult; |
| using ::testing::Field; |
| using ::testing::Ne; |
| using ::testing::Optional; |
| using ::testing::Pair; |
| using ::testing::StrEq; |
| using ::testing::UnorderedElementsAre; |
| |
| constexpr base::StringPiece kInvalidJson = "blah blah invalid JSON"; |
| |
| // Verifies that ParseLocales() can parse locales metadata. |
| TEST(PpdMetadataParserTest, CanParseLocales) { |
| constexpr base::StringPiece kLocalesJson = R"( |
| { |
| "locales": [ "de", "en", "es", "jp" ] |
| } |
| )"; |
| |
| const auto parsed = ParseLocales(kLocalesJson); |
| ASSERT_TRUE(parsed.has_value()); |
| |
| EXPECT_THAT(*parsed, |
| ElementsAre(StrEq("de"), StrEq("en"), StrEq("es"), StrEq("jp"))); |
| } |
| |
| // Verifies that ParseLocales() can parse locales and return a partial |
| // list even when it encounters unexpected values. |
| TEST(PpdMetadataParserTest, CanPartiallyParseLocales) { |
| // The values "0.0" and "78" are gibberish that ParseLocales() shall |
| // ignore; however, these don't structurally foul the JSON, so it can |
| // still return the other locales. |
| constexpr base::StringPiece kLocalesJson = R"( |
| { |
| "locales": [ 0.0, "de", 78, "en", "es", "jp" ] |
| } |
| )"; |
| |
| const auto parsed = ParseLocales(kLocalesJson); |
| ASSERT_TRUE(parsed.has_value()); |
| |
| EXPECT_THAT(*parsed, |
| ElementsAre(StrEq("de"), StrEq("en"), StrEq("es"), StrEq("jp"))); |
| } |
| |
| // Verifies that ParseLocales() returns absl::nullopt rather than an |
| // empty container. |
| TEST(PpdMetadataParserTest, ParseLocalesDoesNotReturnEmptyContainer) { |
| // The values "0.0" and "78" are gibberish that ParseLocales() shall |
| // ignore; while the JSON is still well-formed, the parsed list of |
| // locales contains no values. |
| constexpr base::StringPiece kLocalesJson = R"( |
| { |
| "locales": [ 0.0, 78 ] |
| } |
| )"; |
| |
| EXPECT_FALSE(ParseLocales(kLocalesJson).has_value()); |
| } |
| |
| // Verifies that ParseLocales() returns absl::nullopt on irrecoverable |
| // parse error. |
| TEST(PpdMetadataParserTest, ParseLocalesFailsGracefully) { |
| EXPECT_FALSE(ParseLocales(kInvalidJson).has_value()); |
| } |
| |
| // Verifies that ParseManufacturers() can parse manufacturers metadata. |
| TEST(PpdMetadataParserTest, CanParseManufacturers) { |
| constexpr base::StringPiece kManufacturersJson = R"( |
| { |
| "filesMap": { |
| "Andante": "andante-en.json", |
| "Sostenuto": "sostenuto-en.json" |
| } |
| } |
| )"; |
| |
| const auto parsed = ParseManufacturers(kManufacturersJson); |
| ASSERT_TRUE(parsed.has_value()); |
| |
| EXPECT_THAT(*parsed, |
| UnorderedElementsAre( |
| Pair(StrEq("Andante"), StrEq("andante-en.json")), |
| Pair(StrEq("Sostenuto"), StrEq("sostenuto-en.json")))); |
| } |
| |
| // Verifies that ParseManufacturers() can parse manufacturers and return |
| // a partial list even when it encounters unexpected values. |
| TEST(PpdMetadataParserTest, CanPartiallyParseManufacturers) { |
| // Contains an embedded dictionary keyed on "Dearie me." |
| // ParseManufacturers() shall ignore this. |
| constexpr base::StringPiece kManufacturersJson = R"( |
| { |
| "filesMap": { |
| "Dearie me": { |
| "I didn't": "expect", |
| "to go": "deeper" |
| }, |
| "Andante": "andante-en.json", |
| "Sostenuto": "sostenuto-en.json" |
| } |
| } |
| )"; |
| |
| const auto parsed = ParseManufacturers(kManufacturersJson); |
| ASSERT_TRUE(parsed.has_value()); |
| |
| EXPECT_THAT(*parsed, |
| UnorderedElementsAre( |
| Pair(StrEq("Andante"), StrEq("andante-en.json")), |
| Pair(StrEq("Sostenuto"), StrEq("sostenuto-en.json")))); |
| } |
| |
| // Verifies that ParseManufacturers() returns absl::nullopt rather than |
| // an empty container. |
| TEST(PpdMetadataParserTest, ParseManufacturersDoesNotReturnEmptyContainer) { |
| // Contains an embedded dictionary keyed on "Dearie me." |
| // ParseManufacturers() shall ignore this, but in doing so shall leave |
| // its ParsedManufacturers return value empty. |
| constexpr base::StringPiece kManufacturersJson = R"( |
| { |
| "filesMap": { |
| "Dearie me": { |
| "I didn't": "expect", |
| "to go": "deeper" |
| } |
| } |
| } |
| )"; |
| |
| EXPECT_FALSE(ParseManufacturers(kManufacturersJson).has_value()); |
| } |
| |
| // Verifies that ParseManufacturers() returns absl::nullopt on |
| // irrecoverable parse error. |
| TEST(PpdMetadataParserTest, ParseManufacturersFailsGracefully) { |
| EXPECT_FALSE(ParseManufacturers(kInvalidJson).has_value()); |
| } |
| |
| // Verifies that ParsePrinters() can parse printers metadata. |
| TEST(PpdMetadataParserTest, CanParsePrinters) { |
| constexpr base::StringPiece kPrintersJson = R"( |
| { |
| "printers": [ { |
| "emm": "d 547b", |
| "name": "An die Musik" |
| }, { |
| "emm": "d 553", |
| "name": "Auf der Donau" |
| } ] |
| } |
| )"; |
| |
| const auto parsed = ParsePrinters(kPrintersJson); |
| ASSERT_TRUE(parsed.has_value()); |
| |
| EXPECT_THAT(*parsed, UnorderedElementsAre( |
| ParsedPrinterLike("An die Musik", "d 547b"), |
| ParsedPrinterLike("Auf der Donau", "d 553"))); |
| } |
| |
| // Verifies that ParsePrinters() can parse printers and return a partial |
| // list even when it encounters unexpected values. |
| TEST(PpdMetadataParserTest, CanPartiallyParsePrinters) { |
| // Contains an extra value keyed on "hello" in an otherwise valid leaf |
| // value in Printers metadata. ParsePrinters() shall ignore this. |
| constexpr base::StringPiece kPrintersJson = R"( |
| { |
| "printers": [ { |
| "emm": "d 552", |
| "name": "Hänflings Liebeswerbung", |
| "hello": "there!" |
| }, { |
| "emm": "d 553", |
| "name": "Auf der Donau" |
| } ] |
| } |
| )"; |
| |
| const auto parsed = ParsePrinters(kPrintersJson); |
| ASSERT_TRUE(parsed.has_value()); |
| |
| EXPECT_THAT(*parsed, |
| UnorderedElementsAre( |
| ParsedPrinterLike("Hänflings Liebeswerbung", "d 552"), |
| ParsedPrinterLike("Auf der Donau", "d 553"))); |
| } |
| |
| // Verifies that ParsePrinters() can parse printers and their |
| // well-formed restrictions (if any are specified). |
| TEST(PpdMetadataParserTest, CanParsePrintersWithRestrictions) { |
| // Specifies |
| // * a printer with a minimum milestone, |
| // * a printer with a maximum milestone, and |
| // * a printer with both minimum and maximum milestones. |
| constexpr base::StringPiece kPrintersJson = R"( |
| { |
| "printers": [ { |
| "emm": "d 121", |
| "name": "Schäfers Klagelied", |
| "restriction": { |
| "minMilestone": 121 |
| } |
| }, { |
| "emm": "d 216", |
| "name": "Meeres Stille", |
| "restriction": { |
| "maxMilestone": 216 |
| } |
| }, { |
| "emm": "d 257", |
| "name": "Heidenröslein", |
| "restriction": { |
| "minMilestone": 216, |
| "maxMilestone": 257 |
| } |
| } ] |
| } |
| )"; |
| |
| const auto parsed = ParsePrinters(kPrintersJson); |
| ASSERT_TRUE(parsed.has_value()); |
| |
| EXPECT_THAT(*parsed, |
| UnorderedElementsAre( |
| AllOf(ParsedPrinterLike("Schäfers Klagelied", "d 121"), |
| Field(&ParsedPrinter::restrictions, |
| RestrictionsWithMinMilestone(121))), |
| AllOf(ParsedPrinterLike("Meeres Stille", "d 216"), |
| Field(&ParsedPrinter::restrictions, |
| RestrictionsWithMaxMilestone(216))), |
| AllOf(ParsedPrinterLike("Heidenröslein", "d 257"), |
| Field(&ParsedPrinter::restrictions, |
| RestrictionsWithMinAndMaxMilestones(216, 257))))); |
| } |
| |
| // Verifies that ParsePrinters() can parse printers and ignore |
| // malformed restrictions. |
| TEST(PpdMetadataParserTest, CanParsePrintersWithMalformedRestrictions) { |
| // Specifies a printer with invalid restrictions. |
| constexpr base::StringPiece kPrintersJson = R"( |
| { |
| "printers": [ { |
| "emm": "d 368", |
| "name": "Jägers Abendlied", |
| "restriction": { |
| "hello": "there!" |
| } |
| } ] |
| } |
| )"; |
| |
| const auto parsed = ParsePrinters(kPrintersJson); |
| ASSERT_TRUE(parsed.has_value()); |
| |
| EXPECT_THAT( |
| *parsed, |
| UnorderedElementsAre( |
| AllOf(ParsedPrinterLike("Jägers Abendlied", "d 368"), |
| Field(&ParsedPrinter::restrictions, UnboundedRestrictions())))); |
| } |
| |
| // Verifies that ParsePrinters() returns absl::nullopt rather than an |
| // empty container. |
| TEST(PpdMetadataParserTest, ParsePrintersDoesNotReturnEmptyContainer) { |
| // No printers are specified in this otherwise valid JSON dictionary. |
| EXPECT_FALSE(ParsePrinters("{}").has_value()); |
| } |
| |
| // Verifies that ParsePrinters() returns absl::nullopt on irrecoverable |
| // parse error. |
| TEST(PpdMetadataParserTest, ParsePrintersFailsGracefully) { |
| EXPECT_FALSE(ParsePrinters(kInvalidJson).has_value()); |
| } |
| |
| // Verifies that ParseForwardIndex() can parse forward index metadata. |
| TEST(PpdMetadataParserTest, CanParseForwardIndex) { |
| constexpr base::StringPiece kJsonForwardIndex = R"({ |
| "ppdIndex": { |
| "der wanderer": { |
| "ppdMetadata": [ { |
| "name": "d-489.ppd.gz" |
| } ] |
| }, |
| "morgenlied": { |
| "ppdMetadata": [ { |
| "name": "d-685.ppd.gz" |
| } ] |
| }, |
| "wandrers nachtlied": { |
| "ppdMetadata": [ { |
| "name": "d-224.ppd.gz" |
| } ] |
| } |
| } |
| })"; |
| |
| const auto parsed = ParseForwardIndex(kJsonForwardIndex); |
| ASSERT_TRUE(parsed.has_value()); |
| EXPECT_THAT( |
| parsed.value(), |
| UnorderedElementsAre( |
| ParsedIndexEntryLike( |
| "der wanderer", |
| UnorderedElementsAre( |
| ParsedIndexLeafWithPpdBasename("d-489.ppd.gz"))), |
| ParsedIndexEntryLike( |
| "morgenlied", UnorderedElementsAre(ParsedIndexLeafWithPpdBasename( |
| "d-685.ppd.gz"))), |
| ParsedIndexEntryLike( |
| "wandrers nachtlied", |
| UnorderedElementsAre( |
| ParsedIndexLeafWithPpdBasename("d-224.ppd.gz"))))); |
| } |
| |
| // Verifies that ParseForwardIndex() can parse forward index metadata |
| // and return a partial list even when it encounters unexpected values. |
| TEST(PpdMetadataParserTest, CanPartiallyParseForwardIndex) { |
| // Uses the same value as the CanParseForwardIndex test, but |
| // with garbage values mixed in. |
| constexpr base::StringPiece kJsonForwardIndex = R"({ |
| "ppdIndex": { |
| "garbage": "unused value", |
| "more garbage": [ "more", "unused", "values" ], |
| "der wanderer": { |
| "ppdMetadata": [ { |
| "name": "d-489.ppd.gz", |
| "more garbage still": "unused value" |
| }, { |
| "also garbage": "unused value" |
| } ], |
| "unending garbage": "unused value" |
| }, |
| "morgenlied": { |
| "ppdMetadata": [ { |
| "name": "d-685.ppd.gz" |
| } ] |
| }, |
| "wandrers nachtlied": { |
| "ppdMetadata": [ { |
| "name": "d-224.ppd.gz" |
| } ] |
| } |
| } |
| })"; |
| |
| const auto parsed = ParseForwardIndex(kJsonForwardIndex); |
| ASSERT_TRUE(parsed.has_value()); |
| EXPECT_THAT( |
| parsed.value(), |
| UnorderedElementsAre( |
| ParsedIndexEntryLike( |
| "der wanderer", |
| UnorderedElementsAre( |
| ParsedIndexLeafWithPpdBasename("d-489.ppd.gz"))), |
| ParsedIndexEntryLike( |
| "morgenlied", UnorderedElementsAre(ParsedIndexLeafWithPpdBasename( |
| "d-685.ppd.gz"))), |
| ParsedIndexEntryLike( |
| "wandrers nachtlied", |
| UnorderedElementsAre( |
| ParsedIndexLeafWithPpdBasename("d-224.ppd.gz"))))); |
| } |
| |
| // Verifies that ParseForwardIndex() can parse forward index metadata |
| // in which leaf values have multiple ppdMetadata key-value pairs. |
| TEST(PpdMetadataParserTest, CanParseForwardIndexWithMultiplePpdMetadataLeafs) { |
| constexpr base::StringPiece kJsonForwardIndex = R"({ |
| "ppdIndex": { |
| "rastlose liebe": { |
| "ppdMetadata": [ { |
| "name": "d-138.ppd.gz" |
| }, { |
| "name": "1815.ppd.gz" |
| } ] |
| } |
| } |
| })"; |
| |
| const auto parsed = ParseForwardIndex(kJsonForwardIndex); |
| ASSERT_TRUE(parsed.has_value()); |
| EXPECT_THAT(parsed.value(), |
| UnorderedElementsAre(ParsedIndexEntryLike( |
| "rastlose liebe", |
| UnorderedElementsAre( |
| ParsedIndexLeafWithPpdBasename("d-138.ppd.gz"), |
| ParsedIndexLeafWithPpdBasename("1815.ppd.gz"))))); |
| } |
| |
| // Verifies that ParseForwardIndex() can parse forward index metadata |
| // and its well-formed restrictions (if any are specified). |
| TEST(PpdMetadataParserTest, CanParseForwardIndexWithRestrictions) { |
| // Specifies |
| // * a PPD metadata leaf with a minimum milestone, |
| // * a PPD metadata leaf with a maximum milestone, and |
| // * a PPD metadata leaf with both minimum and maximum milestones. |
| constexpr base::StringPiece kJsonForwardIndex = R"({ |
| "ppdIndex": { |
| "nähe des geliebten": { |
| "ppdMetadata": [ { |
| "name": "d-162.ppd.gz", |
| "restriction": { |
| "minMilestone": 25 |
| } |
| } ] |
| }, |
| "der fischer": { |
| "ppdMetadata": [ { |
| "name": "d-225.ppd.gz", |
| "restriction": { |
| "maxMilestone": 35 |
| } |
| } ] |
| }, |
| "erster verlust": { |
| "ppdMetadata": [ { |
| "name": "d-226.ppd.gz", |
| "restriction": { |
| "minMilestone": 45, |
| "maxMilestone": 46 |
| } |
| } ] |
| } |
| } |
| })"; |
| |
| const auto parsed = ParseForwardIndex(kJsonForwardIndex); |
| ASSERT_TRUE(parsed.has_value()); |
| EXPECT_THAT( |
| parsed.value(), |
| UnorderedElementsAre( |
| ParsedIndexEntryLike( |
| "nähe des geliebten", |
| UnorderedElementsAre( |
| AllOf(ParsedIndexLeafWithPpdBasename("d-162.ppd.gz"), |
| Field(&ParsedIndexLeaf::restrictions, |
| RestrictionsWithMinMilestone(25))))), |
| ParsedIndexEntryLike( |
| "der fischer", UnorderedElementsAre(AllOf( |
| ParsedIndexLeafWithPpdBasename("d-225.ppd.gz"), |
| Field(&ParsedIndexLeaf::restrictions, |
| RestrictionsWithMaxMilestone(35))))), |
| ParsedIndexEntryLike( |
| "erster verlust", |
| UnorderedElementsAre( |
| AllOf(ParsedIndexLeafWithPpdBasename("d-226.ppd.gz"), |
| Field(&ParsedIndexLeaf::restrictions, |
| RestrictionsWithMinAndMaxMilestones(45, 46))))))); |
| } |
| |
| // Verifies that ParseForwardIndex() can parse forward index metadata |
| // and ignore malformed restrictions. |
| TEST(PpdMetadataParserTest, CanParseForwardIndexWithMalformedRestrictions) { |
| // Same test data as the CanParseForwardIndexWithRestrictions test |
| // above, but defines |
| // * a PPD metadata leaf with a malformed minimum milestone, |
| // * a PPD metadata leaf with a malformed maximum milestone, and |
| // * a PPD metadata leaf with malformed minimum and maximum |
| // milestones. |
| constexpr base::StringPiece kJsonForwardIndex = R"({ |
| "ppdIndex": { |
| "nähe des geliebten": { |
| "ppdMetadata": [ { |
| "name": "d-162.ppd.gz", |
| "restriction": { |
| "minMilestone": "garbage value", |
| "maxMilestone": 25 |
| } |
| } ] |
| }, |
| "der fischer": { |
| "ppdMetadata": [ { |
| "name": "d-225.ppd.gz", |
| "restriction": { |
| "minMilestone": 35, |
| "maxMilestone": "garbage value" |
| } |
| } ] |
| }, |
| "erster verlust": { |
| "ppdMetadata": [ { |
| "name": "d-226.ppd.gz", |
| "restriction": { |
| "minMilestone": "garbage value", |
| "maxMilestone": "garbage value" |
| } |
| } ] |
| } |
| } |
| })"; |
| |
| const auto parsed = ParseForwardIndex(kJsonForwardIndex); |
| ASSERT_TRUE(parsed.has_value()); |
| EXPECT_THAT( |
| parsed.value(), |
| UnorderedElementsAre( |
| ParsedIndexEntryLike( |
| "nähe des geliebten", |
| UnorderedElementsAre( |
| AllOf(ParsedIndexLeafWithPpdBasename("d-162.ppd.gz"), |
| Field(&ParsedIndexLeaf::restrictions, |
| RestrictionsWithMaxMilestone(25))))), |
| ParsedIndexEntryLike( |
| "der fischer", UnorderedElementsAre(AllOf( |
| ParsedIndexLeafWithPpdBasename("d-225.ppd.gz"), |
| Field(&ParsedIndexLeaf::restrictions, |
| RestrictionsWithMinMilestone(35))))), |
| ParsedIndexEntryLike( |
| "erster verlust", |
| UnorderedElementsAre( |
| AllOf(ParsedIndexLeafWithPpdBasename("d-226.ppd.gz"), |
| Field(&ParsedIndexLeaf::restrictions, |
| UnboundedRestrictions())))))); |
| } |
| |
| // Verifies that ParseForwardIndex() can parse forward index metadata |
| // specifying PPD licenses. |
| TEST(PpdMetadataParserTest, CanParseForwardIndexWithLicenses) { |
| // Specifies two PPDs, each with a license associated. |
| constexpr base::StringPiece kJsonForwardIndex = R"({ |
| "ppdIndex": { |
| "der fischer": { |
| "ppdMetadata": [ { |
| "name": "d-225.ppd.gz", |
| "license": "two two five license" |
| } ] |
| }, |
| "erster verlust": { |
| "ppdMetadata": [ { |
| "name": "d-226.ppd.gz", |
| "license": "two two six license" |
| } ] |
| } |
| } |
| })"; |
| const auto parsed = ParseForwardIndex(kJsonForwardIndex); |
| ASSERT_TRUE(parsed.has_value()); |
| EXPECT_THAT( |
| parsed.value(), |
| UnorderedElementsAre( |
| ParsedIndexEntryLike( |
| "der fischer", UnorderedElementsAre(AllOf( |
| ParsedIndexLeafWithPpdBasename("d-225.ppd.gz"), |
| Field(&ParsedIndexLeaf::license, |
| StrEq("two two five license"))))), |
| ParsedIndexEntryLike( |
| "erster verlust", |
| UnorderedElementsAre( |
| AllOf(ParsedIndexLeafWithPpdBasename("d-226.ppd.gz"), |
| Field(&ParsedIndexLeaf::license, |
| StrEq("two two six license"))))))); |
| } |
| |
| // Verifies that ParseForwardIndex() returns absl::nullopt rather than |
| // an empty container. |
| TEST(PpdMetadataParserTest, ParseForwardIndexDoesNotReturnEmptyContainer) { |
| // Specifies a forward index that is valid JSON but which has |
| // no PPDs whose leaf values are non-empty. |
| constexpr base::StringPiece kJsonForwardIndex = R"({ |
| "ppdIndex": { |
| "der könig in thule": { |
| "ppdMetadata": [ { |
| "garbage": "unused value" |
| } ] |
| } |
| } |
| })"; |
| |
| EXPECT_FALSE(ParseForwardIndex(kJsonForwardIndex).has_value()); |
| } |
| |
| // Verifies that ParseForwardIndex() returns absl::nullopt on |
| // irrecoverable parse error. |
| TEST(PpdMetadataParserTest, ParseForwardIndexFailsGracefully) { |
| EXPECT_FALSE(ParseForwardIndex(kInvalidJson).has_value()); |
| } |
| |
| // Verifies that ParseUsbIndex() can parse USB index metadata. |
| TEST(PpdMetadataParserTest, CanParseUsbIndex) { |
| constexpr base::StringPiece kJsonUsbIndex = R"({ |
| "usbIndex": { |
| "1": { |
| "effectiveMakeAndModel": "d 541" |
| }, |
| "10": { |
| "effectiveMakeAndModel": "d 504" |
| } |
| } |
| })"; |
| |
| EXPECT_THAT(ParseUsbIndex(kJsonUsbIndex), |
| Optional(UnorderedElementsAre(Pair(1, StrEq("d 541")), |
| Pair(10, StrEq("d 504"))))); |
| } |
| |
| // Verifies that ParseUsbIndex() can parse USB index metadata and return |
| // a partial ParsedUsbIndex even when it encounters garbage values. |
| TEST(PpdMetadataParserTest, CanPartiallyParseUsbIndex) { |
| constexpr base::StringPiece kJsonUsbIndex = R"({ |
| "usbIndex": { |
| "garbage key": { |
| "effectiveMakeAndModel": "garbage value" |
| }, |
| "1": { |
| "effectiveMakeAndModel": "d 541" |
| }, |
| "10": { |
| "effectiveMakeAndModel": "d 504" |
| } |
| } |
| })"; |
| |
| EXPECT_THAT(ParseUsbIndex(kJsonUsbIndex), |
| Optional(UnorderedElementsAre(Pair(1, StrEq("d 541")), |
| Pair(10, StrEq("d 504"))))); |
| } |
| |
| // Verifies that ParseUsbIndex() returns absl::nullopt rather than an |
| // empty container. |
| TEST(PpdMetadataParserTest, ParseUsbIndexDoesNotReturnEmptyContainer) { |
| constexpr base::StringPiece kEmptyJsonUsbIndex = R"({ |
| "usbIndex": { } |
| })"; |
| ASSERT_THAT(base::JSONReader::Read(kEmptyJsonUsbIndex), Ne(absl::nullopt)); |
| EXPECT_THAT(ParseUsbIndex(kEmptyJsonUsbIndex), Eq(absl::nullopt)); |
| |
| constexpr base::StringPiece kJsonUsbIndexWithBadStringKeys = R"({ |
| "usbIndex": { |
| "non-integral key": { } |
| } |
| })"; |
| ASSERT_THAT(base::JSONReader::Read(kJsonUsbIndexWithBadStringKeys), |
| Ne(absl::nullopt)); |
| EXPECT_THAT(ParseUsbIndex(kJsonUsbIndexWithBadStringKeys), Eq(absl::nullopt)); |
| |
| constexpr base::StringPiece kJsonUsbIndexWithoutEmmAtLeaf = R"({ |
| "usbIndex": { |
| "1": { |
| "some key that is not ``effectiveMakeAndModel''": "d 504" |
| } |
| } |
| })"; |
| ASSERT_THAT(base::JSONReader::Read(kJsonUsbIndexWithoutEmmAtLeaf), |
| Ne(absl::nullopt)); |
| EXPECT_THAT(ParseUsbIndex(kJsonUsbIndexWithoutEmmAtLeaf), Eq(absl::nullopt)); |
| |
| constexpr base::StringPiece kJsonUsbIndexWithEmptyEmmAtLeaf = R"({ |
| "usbIndex": { |
| "1": { |
| "effectiveMakeAndModel": "" |
| } |
| } |
| })"; |
| ASSERT_THAT(base::JSONReader::Read(kJsonUsbIndexWithEmptyEmmAtLeaf), |
| Ne(absl::nullopt)); |
| EXPECT_THAT(ParseUsbIndex(kJsonUsbIndexWithEmptyEmmAtLeaf), |
| Eq(absl::nullopt)); |
| } |
| |
| // Verifies that ParseUsbIndex() returns absl::nullopt on irrecoverable |
| // parse error. |
| TEST(PpdMetadataParserTest, ParseUsbIndexFailsGracefully) { |
| EXPECT_THAT(ParseUsbIndex(kInvalidJson), Eq(absl::nullopt)); |
| } |
| |
| // Verifies that ParseUsbvendorIdMap() can parse USB vendor ID maps. |
| TEST(PpdMetadataParserTest, CanParseUsbVendorIdMap) { |
| constexpr base::StringPiece kJsonUsbVendorIdMap = R"({ |
| "entries": [ { |
| "vendorId": 1111, |
| "vendorName": "One One One One" |
| }, { |
| "vendorId": 2222, |
| "vendorName": "Two Two Two Two" |
| }, { |
| "vendorId": 3333, |
| "vendorName": "Three Three Three Three" |
| } ] |
| })"; |
| |
| EXPECT_THAT(ParseUsbVendorIdMap(kJsonUsbVendorIdMap), |
| Optional(UnorderedElementsAre( |
| Pair(1111, StrEq("One One One One")), |
| Pair(2222, StrEq("Two Two Two Two")), |
| Pair(3333, StrEq("Three Three Three Three"))))); |
| } |
| |
| // Verifies that ParseUsbvendorIdMap() can parse USB vendor ID maps and |
| // return partial results even when it encounters garbage values. |
| TEST(PpdMetadataParserTest, CanPartiallyParseUsbVendorIdMap) { |
| // This USB vendor ID map has garbage values in it. |
| // ParseUsbVendorIdMap() shall ignore these. |
| constexpr base::StringPiece kJsonUsbVendorIdMap = R"({ |
| "garbage key": "garbage value", |
| "entries": [ |
| "garbage value", |
| { |
| "vendorId": 1111, |
| "garbage key": "garbage value", |
| "vendorName": "One One One One" |
| }, { |
| "vendorId": 2222, |
| "vendorName": "Two Two Two Two" |
| }, { |
| "vendorId": 3333, |
| "vendorName": "Three Three Three Three" |
| } ] |
| })"; |
| |
| EXPECT_THAT(ParseUsbVendorIdMap(kJsonUsbVendorIdMap), |
| Optional(UnorderedElementsAre( |
| Pair(1111, StrEq("One One One One")), |
| Pair(2222, StrEq("Two Two Two Two")), |
| Pair(3333, StrEq("Three Three Three Three"))))); |
| } |
| |
| // Verifies that ParseUsbvendorIdMap() returns absl::nullopt rather |
| // than an empty container. |
| TEST(PpdMetadataParserTest, ParseUsbVendorIdMapDoesNotReturnEmptyContainer) { |
| // Defines a USB vendor ID map that is empty; it's valid JSON, but |
| // has no values worth returning. |
| constexpr base::StringPiece kJsonUsbVendorIdMap = "{}"; |
| |
| EXPECT_THAT(ParseUsbVendorIdMap(kJsonUsbVendorIdMap), Eq(absl::nullopt)); |
| } |
| |
| // Verifies that ParseUsbvendorIdMap() returns absl::nullopt on |
| // irrecoverable parse error. |
| TEST(PpdMetadataParserTest, ParseUsbVendorIdMapFailsGracefully) { |
| EXPECT_THAT(ParseUsbVendorIdMap(kInvalidJson), Eq(absl::nullopt)); |
| } |
| |
| // Verifies that ParseReverseIndex() can parse reverse index metadata. |
| TEST(PpdMetadataParserTest, CanParseReverseIndex) { |
| constexpr base::StringPiece kReverseIndexJson = R"( |
| { |
| "reverseIndex": { |
| "Die Forelle D 550d": { |
| "manufacturer": "metsukabi", |
| "model": "kimebe" |
| }, |
| "Gruppe aus dem Tartarus D 583": { |
| "manufacturer": "teiga", |
| "model": "dahuho" |
| } |
| } |
| } |
| )"; |
| |
| const auto parsed = ParseReverseIndex(kReverseIndexJson); |
| ASSERT_TRUE(parsed.has_value()); |
| |
| EXPECT_THAT(*parsed, UnorderedElementsAre( |
| Pair(StrEq("Die Forelle D 550d"), |
| ReverseIndexLeafLike("metsukabi", "kimebe")), |
| Pair(StrEq("Gruppe aus dem Tartarus D 583"), |
| ReverseIndexLeafLike("teiga", "dahuho")))); |
| } |
| |
| // Verifies that ParseReverseIndex() can parse reverse index metadata |
| // and return a partial list even when it encounters unexpected values. |
| TEST(PpdMetadataParserTest, CanPartiallyParseReverseIndex) { |
| // Contains two unexpected values (keyed on "Dearie me" and "to go"). |
| // ParseReverseIndex() shall ignore these. |
| constexpr base::StringPiece kReverseIndexJson = R"( |
| { |
| "reverseIndex": { |
| "Dearie me": "one doesn't expect", |
| "to go": "any deeper", |
| "Elysium D 584": { |
| "manufacturer": "nahopenu", |
| "model": "sapudo" |
| }, |
| "An den Tod D 518": { |
| "manufacturer": "suwaka", |
| "model": "zogegi" |
| } |
| } |
| } |
| )"; |
| |
| const auto parsed = ParseReverseIndex(kReverseIndexJson); |
| ASSERT_TRUE(parsed.has_value()); |
| |
| EXPECT_THAT(*parsed, UnorderedElementsAre( |
| Pair(StrEq("Elysium D 584"), |
| ReverseIndexLeafLike("nahopenu", "sapudo")), |
| Pair(StrEq("An den Tod D 518"), |
| ReverseIndexLeafLike("suwaka", "zogegi")))); |
| } |
| |
| // Verifies that ParseReverseIndex() returns absl::nullopt rather than |
| // an empty container. |
| TEST(PpdMetadataParserTest, ParseReverseIndexDoesNotReturnEmptyContainer) { |
| // Contains two unexpected values (keyed on "Dearie me" and "to go"). |
| // ParseReverseIndex() shall ignore this, but in doing so shall make the |
| // returned ParsedReverseIndex empty. |
| constexpr base::StringPiece kReverseIndexJson = R"( |
| { |
| "reverseIndex": { |
| "Dearie me": "one doesn't expect", |
| "to go": "any deeper" |
| } |
| } |
| )"; |
| |
| EXPECT_FALSE(ParseReverseIndex(kReverseIndexJson).has_value()); |
| } |
| |
| // Verifies that ParseReverseIndex() returns absl::nullopt on |
| // irrecoverable parse error. |
| TEST(PpdMetadataParserTest, ParseReverseIndexFailsGracefully) { |
| EXPECT_FALSE(ParseReverseIndex(kInvalidJson).has_value()); |
| } |
| |
| } // namespace |
| } // namespace chromeos |