summaryrefslogtreecommitdiffstats
path: root/toolkit/components/downloads/test/unit/test_DownloadPaths.js
blob: 00fb070669861b4f95ed7d3e9cd4b770d30077e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

/**
 * Tests for the "DownloadPaths.sys.mjs" JavaScript module.
 */

function testSanitize(leafName, expectedLeafName, options = {}) {
  Assert.equal(DownloadPaths.sanitize(leafName, options), expectedLeafName);
}

function testSplitBaseNameAndExtension(aLeafName, [aBase, aExt]) {
  var [base, ext] = DownloadPaths.splitBaseNameAndExtension(aLeafName);
  Assert.equal(base, aBase);
  Assert.equal(ext, aExt);

  // If we modify the base name and concatenate it with the extension again,
  // another roundtrip through the function should give a consistent result.
  // The only exception is when we introduce an extension in a file name that
  // didn't have one or that ended with one of the special cases like ".gz". If
  // we avoid using a dot and we introduce at least another special character,
  // the results are always consistent.
  [base, ext] = DownloadPaths.splitBaseNameAndExtension("(" + base + ")" + ext);
  Assert.equal(base, "(" + aBase + ")");
  Assert.equal(ext, aExt);
}

function testCreateNiceUniqueFile(aTempFile, aExpectedLeafName) {
  var createdFile = DownloadPaths.createNiceUniqueFile(aTempFile);
  Assert.equal(createdFile.leafName, aExpectedLeafName);
}

add_task(async function test_sanitize() {
  // Platform-dependent conversion of special characters to spaces.
  const kSpecialChars = 'A:*?|""<<>>;,+=[]B][=+,;>><<""|?*:C';
  if (AppConstants.platform == "android") {
    testSanitize(kSpecialChars, "A B C");
    testSanitize(" :: Website :: ", "Website");
    testSanitize("* Website!", "Website!");
    testSanitize("Website | Page!", "Website Page!");
    testSanitize("Directory Listing: /a/b/", "Directory Listing _a_b_");
  } else if (AppConstants.platform == "win") {
    testSanitize(kSpecialChars, "A ;,+=[]B][=+,; C");
    testSanitize(" :: Website :: ", "Website");
    testSanitize("* Website!", "Website!");
    testSanitize("Website | Page!", "Website Page!");
    testSanitize("Directory Listing: /a/b/", "Directory Listing _a_b_");
  } else if (AppConstants.platform == "macosx") {
    testSanitize(kSpecialChars, "A ;,+=[]B][=+,; C");
    testSanitize(" :: Website :: ", "Website");
    testSanitize("* Website!", "Website!");
    testSanitize("Website | Page!", "Website Page!");
    testSanitize("Directory Listing: /a/b/", "Directory Listing _a_b_");
  } else {
    testSanitize(kSpecialChars, "A ;,+=[]B][=+,; C");
    testSanitize(" :: Website :: ", "Website");
    testSanitize("* Website!", "Website!");
    testSanitize("Website | Page!", "Website Page!");
    testSanitize("Directory Listing: /a/b/", "Directory Listing _a_b_");
  }

  // Conversion of consecutive runs of slashes and backslashes to underscores.
  testSanitize("\\ \\\\Website\\/Page// /", "_ __Website__Page__ _");

  // Removal of leading and trailing whitespace and dots after conversion.
  testSanitize("  Website  ", "Website");
  testSanitize(". . Website . Page . .", "Website . Page");
  testSanitize(" File . txt ", "File . txt");
  testSanitize("\f\n\r\t\v\x00\x1f\x7f\x80\x9f\xa0 . txt", "txt");
  testSanitize("\u1680\u180e\u2000\u2008\u200a . txt", "txt");
  testSanitize("\u2028\u2029\u202f\u205f\u3000\ufeff . txt", "txt");

  // Strings with whitespace and dots only.
  testSanitize(".", "");
  testSanitize("..", "");
  testSanitize(" ", "");
  testSanitize(" . ", "");

  // Stripping of BIDI formatting characters.
  testSanitize("\u200e \u202b\u202c\u202d\u202etest\x7f\u200f", "_ ____test _");
  testSanitize("AB\x7f\u202a\x7f\u202a\x7fCD", "AB _ _ CD");

  // Stripping of colons:
  testSanitize("foo:bar", "foo bar");

  // not compressing whitespaces.
  testSanitize("foo : bar", "foo   bar", { compressWhitespaces: false });

  testSanitize("thing.lnk", "thing.lnk.download");
  testSanitize("thing.lnk\n", "thing.lnk.download");
  testSanitize("thing.lnk", "thing.lnk", {
    allowInvalidFilenames: true,
  });
  testSanitize("thing.lnk\n", "thing.lnk", {
    allowInvalidFilenames: true,
  });
  testSanitize("thing.URl", "thing.URl.download");
  testSanitize("thing.URl  \n", "thing.URl", {
    allowInvalidFilenames: true,
  });

  testSanitize("thing  and more   .URl", "thing and more .URl", {
    allowInvalidFilenames: true,
  });
  testSanitize("thing  and more   .URl ", "thing  and more   .URl", {
    compressWhitespaces: false,
    allowInvalidFilenames: true,
  });

  testSanitize("thing.local|", "thing.local.download");
  testSanitize("thing.lo|cal", "thing.lo cal");
  testSanitize('thing.local/*"', "thing.local_");

  testSanitize("thing.desktoP", "thing.desktoP.download");
  testSanitize("thing.desktoP  \n", "thing.desktoP", {
    allowInvalidFilenames: true,
  });
});

add_task(async function test_splitBaseNameAndExtension() {
  // Usual file names.
  testSplitBaseNameAndExtension("base", ["base", ""]);
  testSplitBaseNameAndExtension("base.ext", ["base", ".ext"]);
  testSplitBaseNameAndExtension("base.application", ["base", ".application"]);
  testSplitBaseNameAndExtension("base.x.Z", ["base", ".x.Z"]);
  testSplitBaseNameAndExtension("base.ext.Z", ["base", ".ext.Z"]);
  testSplitBaseNameAndExtension("base.ext.gz", ["base", ".ext.gz"]);
  testSplitBaseNameAndExtension("base.ext.Bz2", ["base", ".ext.Bz2"]);
  testSplitBaseNameAndExtension("base..ext", ["base.", ".ext"]);
  testSplitBaseNameAndExtension("base..Z", ["base.", ".Z"]);
  testSplitBaseNameAndExtension("base. .Z", ["base. ", ".Z"]);
  testSplitBaseNameAndExtension("base.base.Bz2", ["base.base", ".Bz2"]);
  testSplitBaseNameAndExtension("base  .ext", ["base  ", ".ext"]);

  // Corner cases. A name ending with a dot technically has no extension, but
  // we consider the ending dot separately from the base name so that modifying
  // the latter never results in an extension being introduced accidentally.
  // Names beginning with a dot are hidden files on Unix-like platforms and if
  // their name doesn't contain another dot they should have no extension, but
  // on Windows the whole name is considered as an extension.
  testSplitBaseNameAndExtension("base.", ["base", "."]);
  testSplitBaseNameAndExtension(".ext", ["", ".ext"]);

  // Unusual file names (not recommended as input to the function).
  testSplitBaseNameAndExtension("base. ", ["base", ". "]);
  testSplitBaseNameAndExtension("base ", ["base ", ""]);
  testSplitBaseNameAndExtension("", ["", ""]);
  testSplitBaseNameAndExtension(" ", [" ", ""]);
  testSplitBaseNameAndExtension(" . ", [" ", ". "]);
  testSplitBaseNameAndExtension(" .. ", [" .", ". "]);
  testSplitBaseNameAndExtension(" .ext", [" ", ".ext"]);
  testSplitBaseNameAndExtension(" .ext. ", [" .ext", ". "]);
  testSplitBaseNameAndExtension(" .ext.gz ", [" .ext", ".gz "]);
});

add_task(async function test_createNiceUniqueFile() {
  var destDir = FileTestUtils.getTempFile("destdir");
  destDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);

  // Single extension.
  var tempFile = destDir.clone();
  tempFile.append("test.txt");
  testCreateNiceUniqueFile(tempFile, "test.txt");
  testCreateNiceUniqueFile(tempFile, "test(1).txt");
  testCreateNiceUniqueFile(tempFile, "test(2).txt");

  // Double extension.
  tempFile.leafName = "test.tar.gz";
  testCreateNiceUniqueFile(tempFile, "test.tar.gz");
  testCreateNiceUniqueFile(tempFile, "test(1).tar.gz");
  testCreateNiceUniqueFile(tempFile, "test(2).tar.gz");

  // Test automatic shortening of long file names. We don't know exactly how
  // many characters are removed, because it depends on the name of the folder
  // where the file is located.
  tempFile.leafName = new Array(256).join("T") + ".txt";
  var newFile = DownloadPaths.createNiceUniqueFile(tempFile);
  Assert.ok(newFile.leafName.length < tempFile.leafName.length);
  Assert.equal(newFile.leafName.slice(-4), ".txt");

  // Creating a valid file name from an invalid one is not always possible.
  tempFile.append("file-under-long-directory.txt");
  try {
    DownloadPaths.createNiceUniqueFile(tempFile);
    do_throw("Exception expected with a long parent directory name.");
  } catch (e) {
    // An exception is expected, but we don't know which one exactly.
  }
});