unit PathFuncTest;

{
  Inno Setup
  Copyright (C) 1997-2010 Jordan Russell
  Portions by Martijn Laan
  For conditions of distribution and use, see LICENSE.TXT.

  Test unit for PathFunc

  $jrsoftware: issrc/Components/PathFuncTest.pas,v 1.10 2010/04/19 21:43:01 jr Exp $
}

interface

procedure PathFuncRunTests(const AlsoTestJapaneseDBCS: Boolean);

implementation

uses
  Windows, SysUtils, PathFunc;

procedure PathFuncRunTests(const AlsoTestJapaneseDBCS: Boolean);

  procedure Test(const Filename: String;
    const DrivePartFalse, DrivePartTrue, PathPartFalse, PathPartTrue: Integer);
  begin
    if PathDrivePartLengthEx(Filename, False) <> DrivePartFalse then
      raise Exception.CreateFmt('"%s" drive part(False) test failed', [Filename]);
    if PathDrivePartLengthEx(Filename, True) <> DrivePartTrue then
      raise Exception.CreateFmt('"%s" drive part(True) test failed', [Filename]);
    if PathPathPartLength(Filename, False) <> PathPartFalse then
      raise Exception.CreateFmt('"%s" path part(False) test failed', [Filename]);
    if PathPathPartLength(Filename, True) <> PathPartTrue then
      raise Exception.CreateFmt('"%s" path part(True) test failed', [Filename]);

    if PathIsRooted(Filename) <> (PathDrivePartLengthEx(Filename, True) <> 0) then
      raise Exception.CreateFmt('"%s" PathIsRooted test failed', [Filename]);
  end;

  procedure TestRemoveBackslash(const Filename, ExpectedResult: String);
  begin
    if RemoveBackslash(Filename) <> ExpectedResult then
      raise Exception.Create('RemoveBackslash test failed');
  end;

  procedure TestRemoveBackslashUnlessRoot(const Filename, ExpectedResult: String);
  begin
    if RemoveBackslashUnlessRoot(Filename) <> ExpectedResult then
      raise Exception.Create('RemoveBackslashUnlessRoot test failed');
  end;

  procedure TestPathChangeExt(const Filename, Extension, ExpectedResult: String);
  begin
    if PathChangeExt(Filename, Extension) <> ExpectedResult then
      raise Exception.Create('PathChangeExt test failed');
  end;

  procedure TestPathExtractExt(const Filename, ExpectedResult: String);
  begin
    if PathExtractExt(Filename) <> ExpectedResult then
      raise Exception.Create('PathExtractExt test failed');
  end;

  procedure TestPathCombine(const Dir, Filename, ExpectedResult: String);
  begin
    if PathCombine(Dir, Filename) <> ExpectedResult then
      raise Exception.Create('PathCombine test failed');
  end;

  procedure TestPathStartsWith(const S, AStartsWith: String; const ExpectedResult: Boolean);
  begin
    if PathStartsWith(S, AStartsWith) <> ExpectedResult then
      raise Exception.Create('PathStartsWith test failed');
  end;

const
  DBChar = #131'\'; { a double-byte character whose 2nd byte happens to be a backslash }
begin
  {$IFDEF UNICODE}
  if AlsoTestJapaneseDBCS then
    raise Exception.Create('DBCS tests not supported in Unicode build');
  {$ENDIF}
  if AlsoTestJapaneseDBCS and (GetACP <> 932) then
    raise Exception.Create('Must be running in Japanese code page to run these tests');

  { * = Bogus path case. What the "correct" result should be is debatable. }
  { ** = Possible access to NTFS alternate data stream. The characters before
         and after the colon must be kept together as a single component. }

  Test('', 0, 0, 0, 0);
  Test('\', 0, 1, 1, 1);
  Test('\a', 0, 1, 1, 1);
  Test('\a\', 0, 1, 2, 3);
  Test('\a\b', 0, 1, 2, 3);
  Test('a', 0, 0, 0, 0);
  Test('a\', 0, 0, 1, 2);
  Test('a\\', 0, 0, 1, 3);
  Test('a\\\', 0, 0, 1, 4);
  Test('a\b', 0, 0, 1, 2);
  Test('a\b:c', 0, 0, 1, 2); {**}

  Test('1:', 2, 2, 2, 2); {*}

  Test('\:', 0, 1, 1, 1); {*}
  { Yes, the following is a valid path -- it specifies a stream named 'stream'
    on the root directory of the current drive. (Yes, directories can have
    named streams.) }
  Test('\:stream', 0, 1, 1, 1); {**}

  Test('c:', 2, 2, 2, 2);
  Test('c:a', 2, 2, 2, 2);
  Test('c:\', 2, 3, 3, 3);
  Test('c:\\', 2, 3, 3, 4);
  Test('c:\\\', 2, 3, 3, 5);
  Test('c:\a', 2, 3, 3, 3);
  Test('c:\a\', 2, 3, 4, 5);
  Test('c:\a\\', 2, 3, 4, 6);
  Test('c:\a\\\', 2, 3, 4, 7);
  Test('c:\a\b', 2, 3, 4, 5);
  Test('c:\a\b:c', 2, 3, 4, 5); {**}

  Test('\\', 2, 2, 2, 2); {*}
  { Odd cases follow: The extra slashes are considered to be in the drive part
    since PathDrivePartLength keeps slurping slashes looking for a share name
    that doesn't exist. }
  Test('\\\', 3, 3, 3, 3); {*}
  Test('\\\\', 4, 4, 4, 4); {*}
  Test('\\\\\', 5, 5, 5, 5); {*}
  Test('\\a', 3, 3, 3, 3); {*}
  Test('\\a\', 4, 4, 4, 4); {*}
  Test('\\a\b', 5, 5, 5, 5);
  Test('\\a\b\', 5, 5, 5, 6);
  Test('\\a\b\c', 5, 5, 5, 6);
  Test('\\a\b\c\', 5, 5, 7, 8);
  Test('\\a\b\c\d', 5, 5, 7, 8);
  Test('\\a\b\c\d:e', 5, 5, 7, 8); {**}
  Test('\\a\\\b', 7, 7, 7, 7);
  Test('\\a\\\b\\\', 7, 7, 7, 10);
  Test('\\a\\\b\\\c', 7, 7, 7, 10);
  Test('\\a\\\b\\\c\\\', 7, 7, 11, 14);

  if AlsoTestJapaneseDBCS then begin
    Test('\\'+DBChar+DBChar+'\b', 8, 8, 8, 8);
    Test('\\'+DBChar+DBChar+'\b\c', 8, 8, 8, 9);
    Test('\\'+DBChar+DBChar+'\b\c\', 8, 8, 10, 11);
    Test('c:\'+DBChar+'\b', 2, 3, 5, 6);
    Test(DBChar+':', 3, 3, 3, 3); {*}  { double-byte drive letter? bogus, but be like Windows... }
  end;

  TestRemoveBackslash('', '');
  TestRemoveBackslash('\', '');
  TestRemoveBackslash('\\', '');
  TestRemoveBackslash('\\\', '');
  TestRemoveBackslash('c:', 'c:');
  TestRemoveBackslash('c:\', 'c:');
  TestRemoveBackslash('c:\\', 'c:');
  TestRemoveBackslash('c:\\\', 'c:');

  if AlsoTestJapaneseDBCS then begin
    TestRemoveBackslash(DBChar, DBChar);
    TestRemoveBackslash(DBChar+'\', DBChar);
    TestRemoveBackslash(DBChar+'\\', DBChar);
  end;

  TestRemoveBackslashUnlessRoot('', '');
  TestRemoveBackslashUnlessRoot('\', '\');
  TestRemoveBackslashUnlessRoot('\\', '\\'); {*}
  TestRemoveBackslashUnlessRoot('\\\', '\\\'); {*}
  TestRemoveBackslashUnlessRoot('\\\\', '\\\\'); {*}
  TestRemoveBackslashUnlessRoot('a', 'a');
  TestRemoveBackslashUnlessRoot('a\', 'a');
  TestRemoveBackslashUnlessRoot('a\\', 'a');
  TestRemoveBackslashUnlessRoot('c:', 'c:');
  TestRemoveBackslashUnlessRoot('c:\', 'c:\');
  TestRemoveBackslashUnlessRoot('c:\a', 'c:\a');
  TestRemoveBackslashUnlessRoot('c:\a\', 'c:\a');
  TestRemoveBackslashUnlessRoot('c:\a\\', 'c:\a');
  TestRemoveBackslashUnlessRoot('\\a\b', '\\a\b');
  TestRemoveBackslashUnlessRoot('\\a\b\', '\\a\b');
  TestRemoveBackslashUnlessRoot('\\a\b\\', '\\a\b');

  TestPathChangeExt('c:', '.txt', 'c:.txt'); {*}  { weird, but same as Delphi's ChangeFileExt }
  TestPathChangeExt('c:\', '.txt', 'c:\.txt'); {*}
  TestPathChangeExt('c:\a', '.txt', 'c:\a.txt');
  TestPathChangeExt('c:\a.', '.txt', 'c:\a.txt');
  TestPathChangeExt('c:\a.tar', '.txt', 'c:\a.txt');
  TestPathChangeExt('c:\a.tar.gz', '.txt', 'c:\a.tar.txt');
  TestPathChangeExt('c:\x.y\a', '.txt', 'c:\x.y\a.txt');
  TestPathChangeExt('\\x.y\a', '.txt', '\\x.y\a.txt'); {*}  { ditto above }
  TestPathChangeExt('\\x.y\a\', '.txt', '\\x.y\a\.txt'); {*}

  TestPathExtractExt('c:', '');
  TestPathExtractExt('c:\', '');
  TestPathExtractExt('c:\a', '');
  TestPathExtractExt('c:\a.', '.');
  TestPathExtractExt('c:\a.txt', '.txt');
  TestPathExtractExt('c:\a.txt.gz', '.gz');
  TestPathExtractExt('c:\x.y\a', '');
  TestPathExtractExt('\\x.y\a', '');
  TestPathExtractExt('\\x.y\a.b', '');
  TestPathExtractExt('\\x.y\a.b\c', '');
  TestPathExtractExt('\\x.y\a.b\c.txt', '.txt');

  TestPathCombine('', 'x', 'x');
  TestPathCombine('a', 'x', 'a\x');
  TestPathCombine('a\', 'x', 'a\x');
  TestPathCombine('a\\', 'x', 'a\\x');
  TestPathCombine('c:', 'x', 'c:x');
  TestPathCombine('c:\', 'x', 'c:\x');
  TestPathCombine('c:\\', 'x', 'c:\\x');
  TestPathCombine('c:\a', 'x', 'c:\a\x');
  TestPathCombine('\', 'x', '\x');
  if AlsoTestJapaneseDBCS then begin
    TestPathCombine(DBChar+':', 'x', DBChar+':x'); {*}  { double-byte drive letter? bogus, but be like Windows... }
    TestPathCombine('c:\'+DBChar, 'x', 'c:\'+DBChar+'\x');
  end;
  TestPathCombine('c:\', '', '');
  TestPathCombine('c:\', 'e:x', 'e:x');
  TestPathCombine('c:\', 'e:\x', 'e:\x');
  TestPathCombine('c:\', '\x', '\x');
  TestPathCombine('c:\', '\\a\b\c', '\\a\b\c');
  TestPathCombine('c:\', 'ee:x', 'c:\ee:x'); {**}

  TestPathStartsWith('C:', 'c:\', False);
  TestPathStartsWith('C:\', 'c:\', True);
  TestPathStartsWith('C:\test', 'c:\', True);
  if AlsoTestJapaneseDBCS then begin
    { Test PathStartsWith's PathCharIsTrailByte call; it shouldn't chop a
      double-byte character in half }
    TestPathStartsWith('C:'+DBChar, 'c:\', False);
    TestPathStartsWith('C:'+DBChar, 'c:'+DBChar[1], False);
  end;
end;

end.
