PathFuncTest.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. unit PathFuncTest;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Test unit for PathFunc
  8. }
  9. interface
  10. procedure PathFuncRunTests(const AlsoTestJapaneseDBCS: Boolean);
  11. implementation
  12. uses
  13. Windows, SysUtils, PathFunc;
  14. procedure PathFuncRunTests(const AlsoTestJapaneseDBCS: Boolean);
  15. procedure Test(const Filename: String;
  16. const DrivePartFalse, DrivePartTrue, PathPartFalse, PathPartTrue: Integer);
  17. begin
  18. if PathDrivePartLengthEx(Filename, False) <> DrivePartFalse then
  19. raise Exception.CreateFmt('"%s" drive part(False) test failed', [Filename]);
  20. if PathDrivePartLengthEx(Filename, True) <> DrivePartTrue then
  21. raise Exception.CreateFmt('"%s" drive part(True) test failed', [Filename]);
  22. if PathPathPartLength(Filename, False) <> PathPartFalse then
  23. raise Exception.CreateFmt('"%s" path part(False) test failed', [Filename]);
  24. if PathPathPartLength(Filename, True) <> PathPartTrue then
  25. raise Exception.CreateFmt('"%s" path part(True) test failed', [Filename]);
  26. if PathIsRooted(Filename) <> (PathDrivePartLengthEx(Filename, True) <> 0) then
  27. raise Exception.CreateFmt('"%s" PathIsRooted test failed', [Filename]);
  28. end;
  29. procedure TestRemoveBackslash(const Filename, ExpectedResult: String);
  30. begin
  31. if RemoveBackslash(Filename) <> ExpectedResult then
  32. raise Exception.Create('RemoveBackslash test failed');
  33. end;
  34. procedure TestRemoveBackslashUnlessRoot(const Filename, ExpectedResult: String);
  35. begin
  36. if RemoveBackslashUnlessRoot(Filename) <> ExpectedResult then
  37. raise Exception.Create('RemoveBackslashUnlessRoot test failed');
  38. end;
  39. procedure TestPathChangeExt(const Filename, Extension, ExpectedResult: String);
  40. begin
  41. if PathChangeExt(Filename, Extension) <> ExpectedResult then
  42. raise Exception.Create('PathChangeExt test failed');
  43. end;
  44. procedure TestPathExtractExt(const Filename, ExpectedResult: String);
  45. begin
  46. if PathExtractExt(Filename) <> ExpectedResult then
  47. raise Exception.Create('PathExtractExt test failed');
  48. end;
  49. procedure TestPathCombine(const Dir, Filename, ExpectedResult: String);
  50. begin
  51. if PathCombine(Dir, Filename) <> ExpectedResult then
  52. raise Exception.Create('PathCombine test failed');
  53. end;
  54. procedure TestPathStartsWith(const S, AStartsWith: String; const ExpectedResult: Boolean);
  55. begin
  56. if PathStartsWith(S, AStartsWith) <> ExpectedResult then
  57. raise Exception.Create('PathStartsWith test failed');
  58. end;
  59. procedure TestPathEndsWith(const IgnoreCase: Boolean;
  60. const S, AEndsWith: String; const ExpectedResult: Boolean);
  61. begin
  62. if PathEndsWith(S, AEndsWith, IgnoreCase) <> ExpectedResult then
  63. raise Exception.Create('PathEndsWith test failed');
  64. end;
  65. procedure TestPathExpandAndNormalizeSlashes(const S, ExpectedResult: String);
  66. begin
  67. { PathExpand's work is done by Windows' GetFullPathName, while
  68. PathNormalizeSlashes uses our own code. They should produce the same
  69. result when the path is fully qualified and has no '.' or '..'
  70. components. }
  71. var PathExpandResult: String;
  72. if (PathExpand(S) <> ExpectedResult) or
  73. not PathExpand(S, PathExpandResult) or
  74. (PathExpandResult <> ExpectedResult) then
  75. raise Exception.Create('PathExpand test failed');
  76. if PathNormalizeSlashes(S) <> ExpectedResult then
  77. raise Exception.Create('PathNormalizeSlashes test failed');
  78. end;
  79. function CompareResultSign(const Value: Integer): Integer;
  80. begin
  81. if Value < 0 then
  82. Result := -1
  83. else if Value > 0 then
  84. Result := 1
  85. else
  86. Result := 0;
  87. end;
  88. procedure TestPathStrCompare(const S1, S2: String;
  89. const IgnoreCase: Boolean; const ExpectedSign: Integer;
  90. const UseNullTerminatedLengths: Boolean = False);
  91. begin
  92. var S1Length, S2Length: Integer;
  93. if UseNullTerminatedLengths then begin
  94. S1Length := -1;
  95. S2Length := -1;
  96. end else begin
  97. S1Length := Length(S1);
  98. S2Length := Length(S2);
  99. end;
  100. if CompareResultSign(PathStrCompare(PChar(S1), S1Length, PChar(S2), S2Length, IgnoreCase)) <> ExpectedSign then
  101. raise Exception.Create('PathStrCompare test failed');
  102. end;
  103. procedure TestPathStrFind(const Source, Value: String;
  104. const IgnoreCase: Boolean; const ExpectedIndex: Integer;
  105. const UseNullTerminatedLengths: Boolean = False);
  106. begin
  107. var SourceLength, ValueLength: Integer;
  108. if UseNullTerminatedLengths then begin
  109. SourceLength := -1;
  110. ValueLength := -1;
  111. end else begin
  112. SourceLength := Length(Source);
  113. ValueLength := Length(Value);
  114. end;
  115. if PathStrFind(PChar(Source), SourceLength, PChar(Value), ValueLength, IgnoreCase) <> ExpectedIndex then
  116. raise Exception.Create('PathStrFind test failed');
  117. end;
  118. const
  119. DBChar = #131'\'; { a double-byte character whose 2nd byte happens to be a backslash }
  120. begin
  121. {$IFDEF UNICODE}
  122. if AlsoTestJapaneseDBCS then
  123. raise Exception.Create('DBCS tests not supported in Unicode build');
  124. {$ENDIF}
  125. if AlsoTestJapaneseDBCS and (GetACP <> 932) then
  126. raise Exception.Create('Must be running in Japanese code page to run these tests');
  127. { * = Bogus path case. What the "correct" result should be is debatable. }
  128. { ** = Possible access to NTFS alternate data stream. The characters before
  129. and after the colon must be kept together as a single component. }
  130. Test('', 0, 0, 0, 0);
  131. Test('\', 0, 1, 1, 1);
  132. Test('\a', 0, 1, 1, 1);
  133. Test('\a\', 0, 1, 2, 3);
  134. Test('\a\b', 0, 1, 2, 3);
  135. Test('a', 0, 0, 0, 0);
  136. Test('a\', 0, 0, 1, 2);
  137. Test('a\\', 0, 0, 1, 3);
  138. Test('a\\\', 0, 0, 1, 4);
  139. Test('a\b', 0, 0, 1, 2);
  140. Test('a\b:c', 0, 0, 1, 2); {**}
  141. Test('1:', 2, 2, 2, 2); {*}
  142. Test('\:', 0, 1, 1, 1); {*}
  143. { Yes, the following is a valid path -- it specifies a stream named 'stream'
  144. on the root directory of the current drive. (Yes, directories can have
  145. named streams.) }
  146. Test('\:stream', 0, 1, 1, 1); {**}
  147. Test('c:', 2, 2, 2, 2);
  148. Test('c:a', 2, 2, 2, 2);
  149. Test('c:\', 2, 3, 3, 3);
  150. Test('c:\\', 2, 3, 3, 4);
  151. Test('c:\\\', 2, 3, 3, 5);
  152. Test('c:\a', 2, 3, 3, 3);
  153. Test('c:\a\', 2, 3, 4, 5);
  154. Test('c:\a\\', 2, 3, 4, 6);
  155. Test('c:\a\\\', 2, 3, 4, 7);
  156. Test('c:\a\b', 2, 3, 4, 5);
  157. Test('c:\a\b:c', 2, 3, 4, 5); {**}
  158. Test('\\', 2, 2, 2, 2); {*}
  159. { Odd cases follow: The extra slashes are considered to be in the drive part
  160. since PathDrivePartLength keeps slurping slashes looking for a share name
  161. that doesn't exist. }
  162. Test('\\\', 3, 3, 3, 3); {*}
  163. Test('\\\\', 4, 4, 4, 4); {*}
  164. Test('\\\\\', 5, 5, 5, 5); {*}
  165. Test('\\a', 3, 3, 3, 3); {*}
  166. Test('\\a\', 4, 4, 4, 4); {*}
  167. Test('\\a\b', 5, 5, 5, 5);
  168. Test('\\a\b\', 5, 5, 5, 6);
  169. Test('\\a\b\c', 5, 5, 5, 6);
  170. Test('\\a\b\c\', 5, 5, 7, 8);
  171. Test('\\a\b\c\d', 5, 5, 7, 8);
  172. Test('\\a\b\c\d:e', 5, 5, 7, 8); {**}
  173. Test('\\a\\\b', 7, 7, 7, 7);
  174. Test('\\a\\\b\\\', 7, 7, 7, 10);
  175. Test('\\a\\\b\\\c', 7, 7, 7, 10);
  176. Test('\\a\\\b\\\c\\\', 7, 7, 11, 14);
  177. if AlsoTestJapaneseDBCS then begin
  178. Test('\\'+DBChar+DBChar+'\b', 8, 8, 8, 8);
  179. Test('\\'+DBChar+DBChar+'\b\c', 8, 8, 8, 9);
  180. Test('\\'+DBChar+DBChar+'\b\c\', 8, 8, 10, 11);
  181. Test('c:\'+DBChar+'\b', 2, 3, 5, 6);
  182. Test(DBChar+':', 3, 3, 3, 3); {*} { double-byte drive letter? bogus, but be like Windows... }
  183. end;
  184. TestRemoveBackslash('', '');
  185. TestRemoveBackslash('\', '');
  186. TestRemoveBackslash('\\', '');
  187. TestRemoveBackslash('\\\', '');
  188. TestRemoveBackslash('c:', 'c:');
  189. TestRemoveBackslash('c:\', 'c:');
  190. TestRemoveBackslash('c:\\', 'c:');
  191. TestRemoveBackslash('c:\\\', 'c:');
  192. if AlsoTestJapaneseDBCS then begin
  193. TestRemoveBackslash(DBChar, DBChar);
  194. TestRemoveBackslash(DBChar+'\', DBChar);
  195. TestRemoveBackslash(DBChar+'\\', DBChar);
  196. end;
  197. TestRemoveBackslashUnlessRoot('', '');
  198. TestRemoveBackslashUnlessRoot('\', '\');
  199. TestRemoveBackslashUnlessRoot('\\', '\\'); {*}
  200. TestRemoveBackslashUnlessRoot('\\\', '\\\'); {*}
  201. TestRemoveBackslashUnlessRoot('\\\\', '\\\\'); {*}
  202. TestRemoveBackslashUnlessRoot('a', 'a');
  203. TestRemoveBackslashUnlessRoot('a\', 'a');
  204. TestRemoveBackslashUnlessRoot('a\\', 'a');
  205. TestRemoveBackslashUnlessRoot('c:', 'c:');
  206. TestRemoveBackslashUnlessRoot('c:\', 'c:\');
  207. TestRemoveBackslashUnlessRoot('c:\a', 'c:\a');
  208. TestRemoveBackslashUnlessRoot('c:\a\', 'c:\a');
  209. TestRemoveBackslashUnlessRoot('c:\a\\', 'c:\a');
  210. TestRemoveBackslashUnlessRoot('\\a\b', '\\a\b');
  211. TestRemoveBackslashUnlessRoot('\\a\b\', '\\a\b');
  212. TestRemoveBackslashUnlessRoot('\\a\b\\', '\\a\b');
  213. TestPathChangeExt('c:', '.txt', 'c:.txt'); {*} { weird, but same as Delphi's ChangeFileExt }
  214. TestPathChangeExt('c:\', '.txt', 'c:\.txt'); {*}
  215. TestPathChangeExt('c:\a', '.txt', 'c:\a.txt');
  216. TestPathChangeExt('c:\a.', '.txt', 'c:\a.txt');
  217. TestPathChangeExt('c:\a.tar', '.txt', 'c:\a.txt');
  218. TestPathChangeExt('c:\a.tar.gz', '.txt', 'c:\a.tar.txt');
  219. TestPathChangeExt('c:\x.y\a', '.txt', 'c:\x.y\a.txt');
  220. TestPathChangeExt('\\x.y\a', '.txt', '\\x.y\a.txt'); {*} { ditto above }
  221. TestPathChangeExt('\\x.y\a\', '.txt', '\\x.y\a\.txt'); {*}
  222. TestPathExtractExt('c:', '');
  223. TestPathExtractExt('c:\', '');
  224. TestPathExtractExt('c:\a', '');
  225. TestPathExtractExt('c:\a.', '.');
  226. TestPathExtractExt('c:\a.txt', '.txt');
  227. TestPathExtractExt('c:\a.txt.gz', '.gz');
  228. TestPathExtractExt('c:\x.y\a', '');
  229. TestPathExtractExt('\\x.y\a', '');
  230. TestPathExtractExt('\\x.y\a.b', '');
  231. TestPathExtractExt('\\x.y\a.b\c', '');
  232. TestPathExtractExt('\\x.y\a.b\c.txt', '.txt');
  233. TestPathCombine('', 'x', 'x');
  234. TestPathCombine('a', 'x', 'a\x');
  235. TestPathCombine('a\', 'x', 'a\x');
  236. TestPathCombine('a\\', 'x', 'a\\x');
  237. TestPathCombine('c:', 'x', 'c:x');
  238. TestPathCombine('c:\', 'x', 'c:\x');
  239. TestPathCombine('c:\\', 'x', 'c:\\x');
  240. TestPathCombine('c:\a', 'x', 'c:\a\x');
  241. TestPathCombine('\', 'x', '\x');
  242. if AlsoTestJapaneseDBCS then begin
  243. TestPathCombine(DBChar+':', 'x', DBChar+':x'); {*} { double-byte drive letter? bogus, but be like Windows... }
  244. TestPathCombine('c:\'+DBChar, 'x', 'c:\'+DBChar+'\x');
  245. end;
  246. TestPathCombine('c:\', '', '');
  247. TestPathCombine('c:\', 'e:x', 'e:x');
  248. TestPathCombine('c:\', 'e:\x', 'e:\x');
  249. TestPathCombine('c:\', '\x', '\x');
  250. TestPathCombine('c:\', '\\a\b\c', '\\a\b\c');
  251. TestPathCombine('c:\', 'ee:x', 'c:\ee:x'); {**}
  252. TestPathStartsWith('', '', True);
  253. TestPathStartsWith('TestingAbc', '', True);
  254. TestPathStartsWith('C:', 'c:\', False);
  255. TestPathStartsWith('C:\', 'c:\', True);
  256. TestPathStartsWith('C:\test', 'c:\', True);
  257. if AlsoTestJapaneseDBCS then begin
  258. { Test PathStartsWith's PathCharIsTrailByte call; it shouldn't chop a
  259. double-byte character in half }
  260. TestPathStartsWith('C:'+DBChar, 'c:\', False);
  261. TestPathStartsWith('C:'+DBChar, 'c:'+DBChar[1], False);
  262. end;
  263. TestPathEndsWith(False, '', '', True);
  264. TestPathEndsWith(True, '', '', True);
  265. TestPathEndsWith(True, 'TestingAbc', '', True);
  266. TestPathEndsWith(True, 'TestingAbc', 'gabc', True);
  267. TestPathEndsWith(False, 'TestingAbc', 'gabc', False);
  268. TestPathEndsWith(True, 'TestingAbc', 'zabc', False);
  269. TestPathEndsWith(True, 'TestingAbc', 'testingABC', True);
  270. TestPathEndsWith(True, 'TestingAbc', 'xTestingAbc', False);
  271. TestPathExpandAndNormalizeSlashes('C:\abc\def', 'C:\abc\def');
  272. TestPathExpandAndNormalizeSlashes('C:\abc\def\', 'C:\abc\def\');
  273. TestPathExpandAndNormalizeSlashes('C:\abc\def\\', 'C:\abc\def\');
  274. TestPathExpandAndNormalizeSlashes('C:/abc\def', 'C:\abc\def');
  275. TestPathExpandAndNormalizeSlashes('C:\\\abc////def', 'C:\abc\def');
  276. { Windows' GetFullPathName doesn't collapse 3+ leading slashes down to 2;
  277. instead, it collapses 4+ leading slashes down to 3. (The resulting path
  278. doesn't actually work with that extra 3rd slash.) }
  279. TestPathExpandAndNormalizeSlashes('\\?\C:\Windows', '\\?\C:\Windows');
  280. TestPathExpandAndNormalizeSlashes('\\\?\C:\Windows', '\\\?\C:\Windows');
  281. TestPathExpandAndNormalizeSlashes('\\\\?\C:\Windows', '\\\?\C:\Windows');
  282. TestPathExpandAndNormalizeSlashes('\\\\\?\C:\Windows', '\\\?\C:\Windows');
  283. TestPathExpandAndNormalizeSlashes('\\?\\C:\\Windows', '\\?\C:\Windows');
  284. TestPathExpandAndNormalizeSlashes('\\\?\\C:\\Windows', '\\\?\C:\Windows');
  285. TestPathStrCompare('Test', 'test', True, 0);
  286. TestPathStrCompare('Test', 'test', False, -1);
  287. TestPathStrCompare('Test', 'Te', False, 1);
  288. TestPathStrCompare('Test', 'Tex', False, -1);
  289. TestPathStrCompare('Hello'+#0+'World', 'Hello', False, 0, True);
  290. TestPathStrFind('abcABC', 'ABC', True, 0);
  291. TestPathStrFind('abcABC', 'ABC', False, 3);
  292. TestPathStrFind('abcABC', 'AbC', False, -1);
  293. TestPathStrFind('abcABC', 'AbC', True, 0);
  294. TestPathStrFind('abcABC', 'xyz', True, -1);
  295. TestPathStrFind('abc'+#0+'ABC', 'ABC', False, -1, True);
  296. TestPathStrFind('abc'+#0+'ABC', 'ABC', False, 4);
  297. end;
  298. end.