Merge pull request #21657 from totten/master-wfvn
[civicrm-core.git] / tests / phpunit / CRM / Utils / FileTest.php
1 <?php
2
3 /**
4 * Class CRM_Utils_FileTest
5 * @group headless
6 */
7 class CRM_Utils_FileTest extends CiviUnitTestCase {
8
9 /**
10 * Test is child path.
11 */
12 public function testIsChildPath() {
13 $testCases = [];
14 $testCases[] = ['/ab/cd/ef', '/ab/cd', FALSE];
15 $testCases[] = ['/ab/cd', '/ab/cd/ef', TRUE];
16 $testCases[] = ['/ab/cde', '/ab/cd/ef', FALSE];
17 $testCases[] = ['/ab/cde', '/ab/cd', FALSE];
18 $testCases[] = ['/ab/cd', 'ab/cd/ef', FALSE];
19 foreach ($testCases as $testCase) {
20 $actual = CRM_Utils_File::isChildPath($testCase[0], $testCase[1], FALSE);
21 $this->assertEquals($testCase[2], $actual, sprintf("parent=[%s] child=[%s] expected=[%s] actual=[%s]",
22 $testCase[0], $testCase[1], $testCase[2], $actual
23 ));
24 }
25 }
26
27 public function testStripComment() {
28 $strings = [
29 "\nab\n-- cd\nef" => "\nab\nef",
30 "ab\n-- cd\nef" => "ab\nef",
31 "ab\n-- cd\nef\ngh" => "ab\nef\ngh",
32 "ab\n--cd\nef" => "ab\nef",
33 "ab\n--cd\nef\n" => "ab\nef\n",
34 "ab\n#cd\nef\n" => "ab\nef\n",
35 "ab\n--cd\nef" => "ab\nef",
36 "ab\n#cd\nef" => "ab\nef",
37 "ab\nfoo#cd\nef" => "ab\nfoo#cd\nef",
38 "ab\r\n--cd\r\nef" => "ab\r\nef",
39 "ab\r\n#cd\r\nef" => "ab\r\nef",
40 "ab\r\nfoo#cd\r\nef" => "ab\r\nfoo#cd\r\nef",
41 ];
42 foreach ($strings as $string => $check) {
43 $test = CRM_Utils_File::stripComments($string);
44 $this->assertEquals($test,
45 $check,
46 sprintf("original=[%s]\nstripped=[%s]\nexpected=[%s]",
47 json_encode($string),
48 json_encode($test),
49 json_encode($check)
50 )
51 );
52 }
53 }
54
55 public function fileExtensions() {
56 return [
57 ['txt'],
58 ['danger'],
59 ];
60 }
61
62 /**
63 * @dataProvider fileExtensions
64 * @param string $ext
65 */
66 public function testDuplicate($ext) {
67 $fileName = CRM_Utils_File::makeFileName('test' . rand(100, 999) . ".$ext");
68 CRM_Utils_File::createFakeFile('/tmp', 'test file content', $fileName);
69 $newFile = CRM_Utils_File::duplicate("/tmp/$fileName");
70 $this->assertNotEquals("/tmp/$fileName", $newFile);
71 $contents = file_get_contents($newFile);
72 $this->assertEquals('test file content', $contents);
73 unlink("/tmp/$fileName");
74 unlink($newFile);
75 }
76
77 public function fileNames() {
78 $cases = [];
79 $cases[] = ['helloworld.txt', TRUE];
80 $cases[] = ['../helloworld.txt', FALSE];
81 // Test case seems to be failing for a strange reason
82 // $cases[] = ['\helloworld.txt', FALSE];
83 $cases[] = ['.helloworld', FALSE];
84 $cases[] = ['smartwatch_1736683_1280_9af3657015e8660cc234eb1601da871.jpg', TRUE];
85 return $cases;
86 }
87
88 /**
89 * Test if the fileName is valid or not
90 * @dataProvider fileNames
91 * @param string $fileName
92 * @param bool $expectedResult
93 */
94 public function testFileNameValid($fileName, $expectedResult) {
95 $this->assertEquals($expectedResult, CRM_Utils_File::isValidFileName($fileName));
96 }
97
98 public function pathToFileExtension() {
99 $cases = [];
100 $cases[] = ['/evil.pdf', 'pdf'];
101 $cases[] = ['/helloworld.jpg', 'jpg'];
102 $cases[] = ['/smartwatch_1736683_1280_9af3657015e8660cc234eb1601da871.jpg', 'jpg'];
103 return $cases;
104 }
105
106 /**
107 * Test returning appropriate file extension
108 * @dataProvider pathToFileExtension
109 * @param string $path
110 * @param string $expectedExtension
111 */
112 public function testPathToExtension($path, $expectedExtension) {
113 $this->assertEquals($expectedExtension, CRM_Utils_File::getExtensionFromPath($path));
114 }
115
116 public function mimeTypeToExtension() {
117 $cases = [];
118 $cases[] = ['text/plain', ['txt', 'text', 'conf', 'def', 'list', 'log', 'in', 'ini']];
119 $cases[] = ['image/jpeg', ['jpeg', 'jpg', 'jpe']];
120 $cases[] = ['image/png', ['png']];
121 return $cases;
122 }
123
124 /**
125 * @dataProvider mimeTypeToExtension
126 * @param stirng $mimeType
127 * @param array $expectedExtensions
128 */
129 public function testMimeTypeToExtension($mimeType, $expectedExtensions) {
130 $this->assertEquals($expectedExtensions, CRM_Utils_File::getAcceptableExtensionsForMimeType($mimeType));
131 }
132
133 /**
134 * Check a few variations of isIncludable
135 */
136 public function testIsIncludable() {
137 $path = \Civi::paths()->getPath('[civicrm.private]/');
138 $bare_filename = 'afile' . time() . '.php';
139 $file = "$path/$bare_filename";
140 file_put_contents($file, '<?php');
141
142 // A file that doesn't exist shouldn't be includable.
143 $this->assertFalse(CRM_Utils_File::isIncludable('invisiblefile.php'));
144
145 // Shouldn't be includable by default in civicrm.private
146 $this->assertFalse(CRM_Utils_File::isIncludable($bare_filename));
147
148 // Add civicrm.private to the include_path, then it should be includable.
149 $old_include_path = ini_get('include_path');
150 ini_set('include_path', $old_include_path . PATH_SEPARATOR . $path);
151 $this->assertTrue(CRM_Utils_File::isIncludable($bare_filename));
152
153 // Set permissions to 0, then it shouldn't be includable even if in path.
154 if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
155 chmod($file, 0);
156 $this->assertFalse(CRM_Utils_File::isIncludable($bare_filename));
157 chmod($file, 0644);
158 }
159
160 ini_set('include_path', $old_include_path);
161 unlink($file);
162 }
163
164 /**
165 * dataprovider for testMakeFilenameWithUnicode
166 * @return array
167 */
168 public function makeFilenameWithUnicodeProvider(): array {
169 return [
170 // explicit indices to make it easier to see which one failed
171 0 => [
172 'string' => '',
173 'replacementCharacter' => NULL,
174 'cutoffLength' => NULL,
175 'expected' => '',
176 ],
177 1 => [
178 'string' => 'a',
179 'replacementCharacter' => NULL,
180 'cutoffLength' => NULL,
181 'expected' => 'a',
182 ],
183 2 => [
184 'string' => 'a b',
185 'replacementCharacter' => NULL,
186 'cutoffLength' => NULL,
187 'expected' => 'a_b',
188 ],
189 3 => [
190 'string' => 'a4b',
191 'replacementCharacter' => NULL,
192 'cutoffLength' => NULL,
193 'expected' => 'a4b',
194 ],
195 4 => [
196 'string' => '_a!@#$%^&*()[]+-=."\'{}<>?/\\|;:b',
197 'replacementCharacter' => NULL,
198 'cutoffLength' => NULL,
199 'expected' => '_a____________________________b',
200 ],
201 5 => [
202 'string' => '_a!@#$%^&*()[]+-=."\'{}<>?/\\|;:b',
203 'replacementCharacter' => '',
204 'cutoffLength' => NULL,
205 'expected' => '_ab',
206 ],
207 // emojis get replaced, but alphabetic letters in non-english are kept
208 6 => [
209 'string' => 'açbяc😀d',
210 'replacementCharacter' => NULL,
211 'cutoffLength' => NULL,
212 'expected' => 'açbяc_d',
213 ],
214 7 => [
215 'string' => 'çя😀',
216 'replacementCharacter' => NULL,
217 'cutoffLength' => NULL,
218 'expected' => 'çя_',
219 ],
220 // test default cutoff
221 8 => [
222 'string' => 'abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890123456789',
223 'replacementCharacter' => NULL,
224 'cutoffLength' => NULL,
225 'expected' => 'abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890123456',
226 ],
227 9 => [
228 'string' => 'abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890123456789',
229 'replacementCharacter' => '_',
230 'cutoffLength' => 30,
231 'expected' => 'abcdefghijklmnopqrstuvwxyz0123',
232 ],
233 // test cutoff truncates multibyte properly
234 10 => [
235 'string' => 'ДДДДДДДДДДДДДДД',
236 'replacementCharacter' => '',
237 'cutoffLength' => 10,
238 'expected' => 'ДДДДДДДДДД',
239 ],
240 ];
241 }
242
243 /**
244 * test makeFilenameWithUnicode
245 * @dataProvider makeFilenameWithUnicodeProvider
246 * @param string $input
247 * @param ?string $replacementCharacter
248 * @param ?int $cutoffLength
249 * @param string $expected
250 */
251 public function testMakeFilenameWithUnicode(string $input, ?string $replacementCharacter, ?int $cutoffLength, string $expected) {
252 if (is_null($replacementCharacter) && is_null($cutoffLength)) {
253 $this->assertSame($expected, CRM_Utils_File::makeFilenameWithUnicode($input));
254 }
255 elseif (is_null($cutoffLength)) {
256 $this->assertSame($expected, CRM_Utils_File::makeFilenameWithUnicode($input, $replacementCharacter));
257 }
258 else {
259 $this->assertSame($expected, CRM_Utils_File::makeFilenameWithUnicode($input, $replacementCharacter, $cutoffLength));
260 }
261 }
262
263 }