unittest.bash.pp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2025 by Michael Van Canneyt
  4. Bash highlighter unit test
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit unittest.bash;
  12. interface
  13. {$mode objfpc}{$H+}
  14. uses
  15. Classes, SysUtils, fpcunit, testregistry,
  16. syntax.highlighter, syntax.bash;
  17. type
  18. TTestBashHighlighter = class(TTestCase)
  19. protected
  20. procedure SetUp; override;
  21. procedure TearDown; override;
  22. private
  23. function DoBashHighlighting(const source: string): TSyntaxTokenArray;
  24. published
  25. procedure TestBashKeywords;
  26. procedure TestNonKeywordCommands;
  27. procedure TestBashStrings;
  28. procedure TestBashComments;
  29. procedure TestBashVariables;
  30. procedure TestBashNumbers;
  31. procedure TestBashOperators;
  32. procedure TestBashCommandSubstitution;
  33. procedure TestBashSymbols;
  34. procedure TestComplexBashScript;
  35. procedure TestBashStringTypes;
  36. end;
  37. implementation
  38. procedure TTestBashHighlighter.SetUp;
  39. begin
  40. end;
  41. procedure TTestBashHighlighter.TearDown;
  42. begin
  43. // Nothing to do
  44. end;
  45. function TTestBashHighlighter.DoBashHighlighting(const source: string): TSyntaxTokenArray;
  46. var
  47. highlighter: TBashSyntaxHighlighter;
  48. begin
  49. highlighter := TBashSyntaxHighlighter.Create;
  50. try
  51. Result := highlighter.Execute(source);
  52. finally
  53. highlighter.Free;
  54. end;
  55. end;
  56. procedure TTestBashHighlighter.TestBashKeywords;
  57. const
  58. Keywords: array[0..18] of string = (
  59. 'if', 'then', 'else', 'fi', 'case', 'esac', 'for', 'do', 'done', 'while',
  60. 'function', 'return', 'break', 'continue', 'declare', 'local', 'export', 'set', 'eval'
  61. );
  62. var
  63. tokens: TSyntaxTokenArray;
  64. i: Integer;
  65. begin
  66. for i := 0 to High(Keywords) do
  67. begin
  68. tokens := DoBashHighlighting(Keywords[i]);
  69. AssertEquals('Should have 1 token for ' + Keywords[i], 1, Length(tokens));
  70. AssertEquals('Token should be ' + Keywords[i], Keywords[i], tokens[0].Text);
  71. AssertEquals(Keywords[i] + ' should be keyword', Ord(shKeyword), Ord(tokens[0].Kind));
  72. end;
  73. end;
  74. procedure TTestBashHighlighter.TestNonKeywordCommands;
  75. var
  76. tokens: TSyntaxTokenArray;
  77. begin
  78. // 'test' is not recognized as a keyword in the bash highlighter
  79. tokens := DoBashHighlighting('test');
  80. AssertEquals('Should have 1 token', 1, Length(tokens));
  81. AssertEquals('Token should be test', 'test', tokens[0].Text);
  82. AssertEquals('test should be default (not keyword)', Ord(shDefault), Ord(tokens[0].Kind));
  83. end;
  84. procedure TTestBashHighlighter.TestBashStrings;
  85. var
  86. tokens: TSyntaxTokenArray;
  87. begin
  88. // Test single-quoted string
  89. tokens := DoBashHighlighting('''hello world''');
  90. AssertEquals('Should have 1 token', 1, Length(tokens));
  91. AssertEquals('Token should be single-quoted string', '''hello world''', tokens[0].Text);
  92. AssertEquals('Token should be string', Ord(shStrings), Ord(tokens[0].Kind));
  93. // Test double-quoted string (may have variable expansion)
  94. tokens := DoBashHighlighting('"hello $USER"');
  95. AssertTrue('Should have at least 1 token', Length(tokens) >= 1);
  96. AssertEquals('First token should be string type', Ord(shStrings), Ord(tokens[0].Kind));
  97. // Test simple double-quoted string
  98. tokens := DoBashHighlighting('"hello world"');
  99. AssertEquals('Should have 1 token', 1, Length(tokens));
  100. AssertEquals('Token should be double-quoted string', '"hello world"', tokens[0].Text);
  101. AssertEquals('Token should be string', Ord(shStrings), Ord(tokens[0].Kind));
  102. end;
  103. procedure TTestBashHighlighter.TestBashComments;
  104. var
  105. tokens: TSyntaxTokenArray;
  106. begin
  107. tokens := DoBashHighlighting('# This is a comment');
  108. AssertEquals('Should have 1 token', 1, Length(tokens));
  109. AssertEquals('Token should be comment', '# This is a comment', tokens[0].Text);
  110. AssertEquals('Token should be comment type', Ord(shComment), Ord(tokens[0].Kind));
  111. end;
  112. procedure TTestBashHighlighter.TestBashVariables;
  113. var
  114. tokens: TSyntaxTokenArray;
  115. begin
  116. tokens := DoBashHighlighting('$USER');
  117. AssertEquals('Should have 1 token', 1, Length(tokens));
  118. AssertEquals('Token should be variable', '$USER', tokens[0].Text);
  119. AssertEquals('Token should be default type', Ord(shDefault), Ord(tokens[0].Kind));
  120. // Test variable in braces
  121. tokens := DoBashHighlighting('${HOME}');
  122. AssertEquals('Should have 1 token', 1, Length(tokens));
  123. AssertEquals('Token should be braced variable', '${HOME}', tokens[0].Text);
  124. AssertEquals('Token should be default type', Ord(shDefault), Ord(tokens[0].Kind));
  125. end;
  126. procedure TTestBashHighlighter.TestBashNumbers;
  127. var
  128. tokens: TSyntaxTokenArray;
  129. begin
  130. tokens := DoBashHighlighting('123');
  131. AssertEquals('Should have 1 token', 1, Length(tokens));
  132. AssertEquals('Token should be number', '123', tokens[0].Text);
  133. AssertEquals('Token should be number type', Ord(shNumbers), Ord(tokens[0].Kind));
  134. // Test floating point
  135. tokens := DoBashHighlighting('3.14');
  136. AssertTrue('Should have at least 1 token', Length(tokens) >= 1);
  137. // First token should be the integer part
  138. AssertEquals('First token should be number type', Ord(shNumbers), Ord(tokens[0].Kind));
  139. end;
  140. procedure TTestBashHighlighter.TestBashOperators;
  141. var
  142. tokens: TSyntaxTokenArray;
  143. begin
  144. tokens := DoBashHighlighting('==');
  145. AssertEquals('Should have 1 token', 1, Length(tokens));
  146. AssertEquals('Token should be equality operator', '==', tokens[0].Text);
  147. AssertEquals('Token should be operator type', Ord(shOperator), Ord(tokens[0].Kind));
  148. tokens := DoBashHighlighting('!=');
  149. AssertEquals('Should have 1 token', 1, Length(tokens));
  150. AssertEquals('Token should be inequality operator', '!=', tokens[0].Text);
  151. AssertEquals('Token should be operator type', Ord(shOperator), Ord(tokens[0].Kind));
  152. tokens := DoBashHighlighting('&&');
  153. AssertEquals('Should have 1 token', 1, Length(tokens));
  154. AssertEquals('Token should be logical AND', '&&', tokens[0].Text);
  155. AssertEquals('Token should be operator type', Ord(shOperator), Ord(tokens[0].Kind));
  156. end;
  157. procedure TTestBashHighlighter.TestBashCommandSubstitution;
  158. var
  159. tokens: TSyntaxTokenArray;
  160. begin
  161. tokens := DoBashHighlighting('`date`');
  162. AssertEquals('Should have 1 token', 1, Length(tokens));
  163. AssertEquals('Token should be command substitution', '`date`', tokens[0].Text);
  164. AssertEquals('Token should be interpolation type', Ord(shInterpolation), Ord(tokens[0].Kind));
  165. // Test $(command) syntax
  166. tokens := DoBashHighlighting('$(whoami)');
  167. AssertTrue('Should have at least 1 token', Length(tokens) >= 1);
  168. // Should contain interpolation tokens
  169. end;
  170. procedure TTestBashHighlighter.TestBashSymbols;
  171. var
  172. tokens: TSyntaxTokenArray;
  173. begin
  174. tokens := DoBashHighlighting('[');
  175. AssertEquals('Should have 1 token', 1, Length(tokens));
  176. AssertEquals('Token should be opening bracket', '[', tokens[0].Text);
  177. AssertEquals('Token should be symbol type', Ord(shSymbol), Ord(tokens[0].Kind));
  178. tokens := DoBashHighlighting(';');
  179. AssertEquals('Should have 1 token', 1, Length(tokens));
  180. AssertEquals('Token should be semicolon', ';', tokens[0].Text);
  181. AssertEquals('Token should be symbol type', Ord(shSymbol), Ord(tokens[0].Kind));
  182. tokens := DoBashHighlighting('|');
  183. AssertEquals('Should have 1 token', 1, Length(tokens));
  184. AssertEquals('Token should be pipe', '|', tokens[0].Text);
  185. AssertEquals('Token should be operator type', Ord(shOperator), Ord(tokens[0].Kind));
  186. end;
  187. procedure TTestBashHighlighter.TestComplexBashScript;
  188. var
  189. tokens: TSyntaxTokenArray;
  190. script: string;
  191. i: Integer;
  192. hasKeywords, hasStrings, hasOperators, hasSymbols: Boolean;
  193. begin
  194. script := 'if [ "$USER" == "root" ]; then echo "Admin user"; fi';
  195. tokens := DoBashHighlighting(script);
  196. AssertTrue('Should have multiple tokens', Length(tokens) > 10);
  197. // Check that we have different token types
  198. hasKeywords := False;
  199. hasStrings := False;
  200. hasOperators := False;
  201. hasSymbols := False;
  202. for i := 0 to High(tokens) do
  203. begin
  204. case tokens[i].Kind of
  205. shKeyword: hasKeywords := True;
  206. shStrings: hasStrings := True;
  207. shOperator: hasOperators := True;
  208. shSymbol: hasSymbols := True;
  209. end;
  210. end;
  211. AssertTrue('Should contain keyword tokens', hasKeywords);
  212. AssertTrue('Should contain string tokens', hasStrings);
  213. AssertTrue('Should contain operator tokens', hasOperators);
  214. AssertTrue('Should contain symbol tokens', hasSymbols);
  215. // First token should be 'if' keyword
  216. AssertEquals('First token should be if', 'if', tokens[0].Text);
  217. AssertEquals('First token should be keyword', Ord(shKeyword), Ord(tokens[0].Kind));
  218. // Last token should be 'fi' keyword
  219. AssertEquals('Last token should be fi', 'fi', tokens[High(tokens)].Text);
  220. AssertEquals('Last token should be keyword', Ord(shKeyword), Ord(tokens[High(tokens)].Kind));
  221. end;
  222. procedure TTestBashHighlighter.TestBashStringTypes;
  223. var
  224. tokens: TSyntaxTokenArray;
  225. begin
  226. // Test heredoc-style strings or other bash string features
  227. tokens := DoBashHighlighting('echo "test"');
  228. AssertTrue('Should have multiple tokens', Length(tokens) >= 3);
  229. // Should have echo as default (command), space as default, and "test" as string
  230. AssertEquals('First token should be echo', 'echo', tokens[0].Text);
  231. AssertEquals('First token should be default', Ord(shDefault), Ord(tokens[0].Kind));
  232. AssertEquals('Second token should be space', ' ', tokens[1].Text);
  233. AssertEquals('Second token should be default', Ord(shDefault), Ord(tokens[1].Kind));
  234. AssertEquals('Third token should be quoted string', '"test"', tokens[2].Text);
  235. AssertEquals('Third token should be string', Ord(shStrings), Ord(tokens[2].Kind));
  236. end;
  237. initialization
  238. RegisterTest(TTestBashHighlighter);
  239. end.