xref: /aosp_15_r20/external/cronet/base/nix/mime_util_xdg_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/nix/mime_util_xdg.h"
6 
7 #include <map>
8 #include <string>
9 #include <vector>
10 
11 #include "base/base64.h"
12 #include "base/check.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 namespace base::nix {
19 namespace {
20 
21 // Test mime.cache files are generated using a process such as:
22 // mkdir -p /tmp/mimetest/packages
23 // cat <<EOF >> /tmp/mimetest/packages/application-x-foobar.xml
24 // <?xml version="1.0" encoding="UTF-8"?>
25 // <mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
26 //   <mime-type type="x/no-dot"><glob pattern="~"/></mime-type>
27 //   <mime-type type="application/pdf"><glob pattern="*.pdf"/></mime-type>
28 //   <mime-type type="text/plain"><glob pattern="*.txt"/></mime-type>
29 //   <mime-type type="text/plain"><glob pattern="*.doc"/></mime-type>
30 //   <mime-type type="x/ignore"><glob pattern="*.foo" weight="60"/></mime-type>
31 //   <mime-type type="x/foo"><glob pattern="*.foo" weight="80"/></mime-type>
32 //   <mime-type type="text/plain"><glob pattern="*.foo"/></mime-type>
33 //   <mime-type type="x/smile"><glob pattern="*.����"/></mime-type>
34 // </mime-info>
35 // EOF
36 // update-mime-database /tmp/mimetest
37 // base64 -w72 /tmp/mimetest/mime.cache
38 // See https://wiki.archlinux.org/title/XDG_MIME_Applications
39 
40 constexpr char kTestMimeCacheB64[] =
41     "AAEAAgAAAHQAAAB4AAAAfAAAAIwAAAHMAAAB0AAAAdwAAAHgAAAB5AAAAehhcHBsaWNhdGlv"
42     "bi9wZGYAeC9zbWlsZQB4L2lnbm9yZQAAAAB0ZXh0L3BsYWluAAB4L2ZvbwAAAH4AAAB4L25v"
43     "LWRvdAAAAAAAAAAAAAAAAAAAAAEAAABkAAAAaAAAADIAAAAFAAAAlAAAAGMAAAABAAAA0AAA"
44     "AGYAAAABAAAA3AAAAG8AAAABAAAA6AAAAHQAAAABAAAA9AAB+SkAAAABAAABAAAAAG8AAAAB"
45     "AAABDAAAAGQAAAABAAABGAAAAG8AAAABAAABJAAAAHgAAAABAAABMAAB9kIAAAABAAABPAAA"
46     "AGQAAAABAAABSAAAAHAAAAABAAABVAAAAGYAAAABAAABYAAAAHQAAAABAAABbAAAAC4AAAAB"
47     "AAABeAAAAC4AAAABAAABhAAAAC4AAAABAAABkAAAAC4AAAADAAABnAAAAC4AAAABAAABwAAA"
48     "AAAAAAA8AAAAMgAAAAAAAABQAAAAMgAAAAAAAAAsAAAAMgAAAAAAAABEAAAAPAAAAAAAAABc"
49     "AAAAUAAAAAAAAABQAAAAMgAAAAAAAABQAAAAMgAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAA"
50     "AAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
51 
52 class ParseMimeTypesTest : public ::testing::Test {
53  public:
ParseMimeTypesTest()54   ParseMimeTypesTest() {
55     CHECK(temp_dir_.CreateUniqueTempDir());
56     mime_types_path_ = temp_dir_.GetPath().Append("mime.types");
57   }
58   ParseMimeTypesTest(const ParseMimeTypesTest&) = delete;
59   ParseMimeTypesTest& operator=(const ParseMimeTypesTest&) = delete;
60 
61   ~ParseMimeTypesTest() override = default;
62 
63   // Ensures that parsing fails when mime.cache file is modified such that
64   // `buf[pos] = c`.
InvalidIf(std::vector<uint8_t> & buf,size_t pos,uint8_t c)65   void InvalidIf(std::vector<uint8_t>& buf, size_t pos, uint8_t c) {
66     ASSERT_LT(pos, buf.size());
67     uint8_t old_c = buf[pos];
68     buf[pos] = c;
69     ASSERT_TRUE(base::WriteFile(TempFile(), buf));
70     MimeTypeMap map;
71     EXPECT_FALSE(ParseMimeTypes(TempFile(), map));
72     buf[pos] = old_c;
73   }
74 
TempFile() const75   const FilePath& TempFile() const { return mime_types_path_; }
76 
77  private:
78   ScopedTempDir temp_dir_;
79   FilePath mime_types_path_;
80 };
81 
82 }  // namespace
83 
operator ==(const WeightedMime & lhs,const WeightedMime & rhs)84 bool operator==(const WeightedMime& lhs, const WeightedMime& rhs) {
85   return lhs.mime_type == rhs.mime_type && lhs.weight == rhs.weight;
86 }
87 
TEST_F(ParseMimeTypesTest,NonExistentFileFails)88 TEST_F(ParseMimeTypesTest, NonExistentFileFails) {
89   MimeTypeMap map;
90   EXPECT_FALSE(ParseMimeTypes(FilePath("/invalid/filepath/foo"), map));
91 }
92 
TEST_F(ParseMimeTypesTest,ValidResult)93 TEST_F(ParseMimeTypesTest, ValidResult) {
94   MimeTypeMap map;
95   auto buf = Base64Decode(kTestMimeCacheB64);
96   ASSERT_TRUE(buf.has_value());
97   ASSERT_TRUE(WriteFile(TempFile(), *buf));
98   EXPECT_TRUE(ParseMimeTypes(TempFile(), map));
99   const MimeTypeMap kExpected = {
100       {"pdf", {"application/pdf", 50}}, {"txt", {"text/plain", 50}},
101       {"doc", {"text/plain", 50}},      {"foo", {"x/foo", 80}},
102       {"����", {"x/smile", 50}},
103   };
104   EXPECT_EQ(map, kExpected);
105 }
106 
TEST_F(ParseMimeTypesTest,Empty)107 TEST_F(ParseMimeTypesTest, Empty) {
108   MimeTypeMap map;
109   ASSERT_TRUE(WriteFile(TempFile(), ""));
110   EXPECT_FALSE(ParseMimeTypes(TempFile(), map));
111 }
112 
113 // xxd /tmp/mimetest/mime.cache
114 // 00000000: 0001 0002 0000 0074 0000 0078 0000 007c  .......t...x...|
115 // 00000010: 0000 008c 0000 01cc 0000 01d0 0000 01dc  ................
116 // 00000020: 0000 01e0 0000 01e4 0000 01e8 6170 706c  ............appl
117 // 00000030: 6963 6174 696f 6e2f 7064 6600 782f 736d  ication/pdf.x/sm
118 // 00000040: 696c 6500 782f 6967 6e6f 7265 0000 0000  ile.x/ignore....
119 // 00000050: 7465 7874 2f70 6c61 696e 0000 782f 666f  text/plain..x/fo
120 // 00000060: 6f00 0000 7e00 0000 782f 6e6f 2d64 6f74  o...~...x/no-dot
121 // 00000070: 0000 0000 0000 0000 0000 0000 0000 0001  ................
122 // 00000080: 0000 0064 0000 0068 0000 0032 0000 0005  ...d...h...2....
123 // 00000090: 0000 0094 0000 0063 0000 0001 0000 00d0  .......c........
124 // 000000a0: 0000 0066 0000 0001 0000 00dc 0000 006f  ...f...........o
125 // 000000b0: 0000 0001 0000 00e8 0000 0074 0000 0001  ...........t....
126 // 000000c0: 0000 00f4 0001 f929 0000 0001 0000 0100  .......)........
127 // 000000d0: 0000 006f 0000 0001 0000 010c 0000 0064  ...o...........d
128 // 000000e0: 0000 0001 0000 0118 0000 006f 0000 0001  ...........o....
129 // 000000f0: 0000 0124 0000 0078 0000 0001 0000 0130  ...$...x.......0
130 // 00000100: 0001 f642 0000 0001 0000 013c 0000 0064  ...B.......<...d
131 // 00000110: 0000 0001 0000 0148 0000 0070 0000 0001  .......H...p....
132 // 00000120: 0000 0154 0000 0066 0000 0001 0000 0160  ...T...f.......`
133 // 00000130: 0000 0074 0000 0001 0000 016c 0000 002e  ...t.......l....
134 // 00000140: 0000 0001 0000 0178 0000 002e 0000 0001  .......x........
135 // 00000150: 0000 0184 0000 002e 0000 0001 0000 0190  ................
136 // 00000160: 0000 002e 0000 0003 0000 019c 0000 002e  ................
137 // 00000170: 0000 0001 0000 01c0 0000 0000 0000 003c  ...............<
138 // 00000180: 0000 0032 0000 0000 0000 0050 0000 0032  ...2.......P...2
139 // 00000190: 0000 0000 0000 002c 0000 0032 0000 0000  .......,...2....
140 // 000001a0: 0000 0044 0000 003c 0000 0000 0000 005c  ...D...<.......\
141 // 000001b0: 0000 0050 0000 0000 0000 0050 0000 0032  ...P.......P...2
142 // 000001c0: 0000 0000 0000 0050 0000 0032 0000 0000  .......P...2....
143 // 000001d0: 0000 0000 0000 0000 0000 01dc 0000 0000  ................
144 // 000001e0: 0000 0000 0000 0000 0000 0006 0000 0000  ................
145 // 000001f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
146 // 00000200: 0000 0000
TEST_F(ParseMimeTypesTest,Invalid)147 TEST_F(ParseMimeTypesTest, Invalid) {
148   auto buf = Base64Decode(kTestMimeCacheB64);
149   ASSERT_TRUE(buf.has_value());
150   // ALIAS_LIST_OFFSET is uint32 at byte 4 = 0x74.
151   // Alias list offset inside header.
152   InvalidIf(*buf, 7, 0xa);
153   // Alias list offset larger than file size.
154   InvalidIf(*buf, 6, 0xff);
155   // Not null beore alias list.
156   InvalidIf(*buf, 0x74 - 1, 'X');
157   // Misaligned offset for REVERSE_SUFFIX_TREE_OFFSET.
158   InvalidIf(*buf, 0x13, 0x7a);
159   // N_ROOTS > kMaxUnicode (0x10ffff).
160   InvalidIf(*buf, 0x8d, 0x20);
161   InvalidIf(*buf, 0xd5, 0x20);
162   // Node C > kMaxUnicode (0x10ffff).
163   InvalidIf(*buf, 0x95, 0x20);
164   // Node N_CHILDREN > kMaxUnicode (0x10ffff).
165   InvalidIf(*buf, 0x99, 0x20);
166   // Node FIRST_CHILD_OFFSET below tree offset.
167   InvalidIf(*buf, 0x9f, 0x10);
168   InvalidIf(*buf, 0xdb, 0x20);
169   // Node FIRST_CHILD_OFFSET beyond file size.
170   InvalidIf(*buf, 0x9e, 0x20);
171   InvalidIf(*buf, 0xda, 0x20);
172   // Mime type offset below header.
173   InvalidIf(*buf, 0x18b, 0x10);
174   // Mime type offset above alias list.
175   InvalidIf(*buf, 0x18b, 0x74);
176 }
177 
178 }  // namespace base::nix
179