| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609 |
- {
- This file is part of the Free Component Library (FCL)
- Copyright (c) 2025 by Michael Van Canneyt
- JSON highlighter unit test
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- unit unittest.json;
- interface
- {$mode objfpc}{$H+}
- uses
- Classes, SysUtils, fpcunit, testregistry,
- syntax.highlighter, syntax.json;
- type
- TTestJsonHighlighter = class(TTestCase)
- protected
- procedure SetUp; override;
- procedure TearDown; override;
- private
- function DoJsonHighlighting(const source: string): TSyntaxTokenArray;
- published
- procedure TestJsonKeys;
- procedure TestJsonStrings;
- procedure TestJsonNumbers;
- procedure TestJsonKeywords;
- procedure TestJsonSymbols;
- procedure TestJsonEscapeSequences;
- procedure TestJsonUnicodeEscapes;
- procedure TestJsonInvalidStrings;
- procedure TestJsonInvalidNumbers;
- procedure TestComplexJsonObject;
- procedure TestJsonArray;
- procedure TestNestedJsonStructures;
- procedure TestJsonWhitespace;
- procedure TestJsonScientificNotation;
- procedure TestJsonEdgeCases;
- procedure TestJsonEmpty;
- end;
- implementation
- procedure TTestJsonHighlighter.SetUp;
- begin
- end;
- procedure TTestJsonHighlighter.TearDown;
- begin
- // Nothing to do
- end;
- function TTestJsonHighlighter.DoJsonHighlighting(const source: string): TSyntaxTokenArray;
- var
- highlighter: TJsonSyntaxHighlighter;
- begin
- highlighter := TJsonSyntaxHighlighter.Create;
- try
- Result := highlighter.Execute(source);
- finally
- highlighter.Free;
- end;
- end;
- procedure TTestJsonHighlighter.TestJsonKeys;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundKey: Boolean;
- begin
- // Test simple object with key
- tokens := DoJsonHighlighting('{"name": "value"}');
- foundKey := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '"name"') and (tokens[i].Kind = shKey) then
- begin
- foundKey := True;
- break;
- end;
- end;
- AssertTrue('Should find key token', foundKey);
- // Test multiple keys
- tokens := DoJsonHighlighting('{"firstName": "John", "lastName": "Doe"}');
- foundKey := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '"firstName"') and (tokens[i].Kind = shKey) then
- begin
- foundKey := True;
- break;
- end;
- end;
- AssertTrue('Should find firstName key', foundKey);
- end;
- procedure TTestJsonHighlighter.TestJsonStrings;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundString: Boolean;
- begin
- // Test string value (not key)
- tokens := DoJsonHighlighting('{"name": "John Doe"}');
- foundString := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '"John Doe"') and (tokens[i].Kind = shStrings) then
- begin
- foundString := True;
- break;
- end;
- end;
- AssertTrue('Should find string value token', foundString);
- // Test empty string
- tokens := DoJsonHighlighting('{"empty": ""}');
- foundString := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '""') and (tokens[i].Kind = shStrings) then
- begin
- foundString := True;
- break;
- end;
- end;
- AssertTrue('Should find empty string token', foundString);
- end;
- procedure TTestJsonHighlighter.TestJsonNumbers;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundNumber: Boolean;
- begin
- // Test integer
- tokens := DoJsonHighlighting('{"age": 25}');
- foundNumber := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '25') and (tokens[i].Kind = shNumbers) then
- begin
- foundNumber := True;
- break;
- end;
- end;
- AssertTrue('Should find integer number', foundNumber);
- // Test decimal
- tokens := DoJsonHighlighting('{"price": 19.99}');
- foundNumber := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '19.99') and (tokens[i].Kind = shNumbers) then
- begin
- foundNumber := True;
- break;
- end;
- end;
- AssertTrue('Should find decimal number', foundNumber);
- // Test negative number
- tokens := DoJsonHighlighting('{"temperature": -15}');
- foundNumber := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '-15') and (tokens[i].Kind = shNumbers) then
- begin
- foundNumber := True;
- break;
- end;
- end;
- AssertTrue('Should find negative number', foundNumber);
- // Test zero
- tokens := DoJsonHighlighting('{"zero": 0}');
- foundNumber := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '0') and (tokens[i].Kind = shNumbers) then
- begin
- foundNumber := True;
- break;
- end;
- end;
- AssertTrue('Should find zero', foundNumber);
- end;
- procedure TTestJsonHighlighter.TestJsonKeywords;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundKeyword: Boolean;
- begin
- // Test true
- tokens := DoJsonHighlighting('{"valid": true}');
- foundKeyword := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = 'true') and (tokens[i].Kind = shKeyword) then
- begin
- foundKeyword := True;
- break;
- end;
- end;
- AssertTrue('Should find true keyword', foundKeyword);
- // Test false
- tokens := DoJsonHighlighting('{"valid": false}');
- foundKeyword := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = 'false') and (tokens[i].Kind = shKeyword) then
- begin
- foundKeyword := True;
- break;
- end;
- end;
- AssertTrue('Should find false keyword', foundKeyword);
- // Test null
- tokens := DoJsonHighlighting('{"value": null}');
- foundKeyword := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = 'null') and (tokens[i].Kind = shKeyword) then
- begin
- foundKeyword := True;
- break;
- end;
- end;
- AssertTrue('Should find null keyword', foundKeyword);
- end;
- procedure TTestJsonHighlighter.TestJsonSymbols;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundSymbol: Boolean;
- begin
- tokens := DoJsonHighlighting('{"key": "value"}');
- // Test opening brace
- foundSymbol := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '{') and (tokens[i].Kind = shSymbol) then
- begin
- foundSymbol := True;
- break;
- end;
- end;
- AssertTrue('Should find opening brace', foundSymbol);
- // Test colon
- foundSymbol := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = ':') and (tokens[i].Kind = shSymbol) then
- begin
- foundSymbol := True;
- break;
- end;
- end;
- AssertTrue('Should find colon', foundSymbol);
- // Test closing brace
- foundSymbol := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '}') and (tokens[i].Kind = shSymbol) then
- begin
- foundSymbol := True;
- break;
- end;
- end;
- AssertTrue('Should find closing brace', foundSymbol);
- end;
- procedure TTestJsonHighlighter.TestJsonEscapeSequences;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundString: Boolean;
- begin
- // Test basic escape sequences
- tokens := DoJsonHighlighting('{"message": "Hello\nWorld"}');
- foundString := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '"Hello\nWorld"') and (tokens[i].Kind = shStrings) then
- begin
- foundString := True;
- break;
- end;
- end;
- AssertTrue('Should handle newline escape', foundString);
- // Test quote escape
- tokens := DoJsonHighlighting('{"quote": "Say \"Hello\""}');
- foundString := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '"Say \"Hello\""') and (tokens[i].Kind = shStrings) then
- begin
- foundString := True;
- break;
- end;
- end;
- AssertTrue('Should handle quote escape', foundString);
- // Test backslash escape
- tokens := DoJsonHighlighting('{"path": "C:\\Windows"}');
- foundString := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '"C:\\Windows"') and (tokens[i].Kind = shStrings) then
- begin
- foundString := True;
- break;
- end;
- end;
- AssertTrue('Should handle backslash escape', foundString);
- end;
- procedure TTestJsonHighlighter.TestJsonUnicodeEscapes;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundString: Boolean;
- begin
- // Test unicode escape
- tokens := DoJsonHighlighting('{"unicode": "\u0048\u0065\u006C\u006C\u006F"}');
- foundString := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '"\u0048\u0065\u006C\u006C\u006F"') and (tokens[i].Kind = shStrings) then
- begin
- foundString := True;
- break;
- end;
- end;
- AssertTrue('Should handle unicode escapes', foundString);
- end;
- procedure TTestJsonHighlighter.TestJsonInvalidStrings;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundInvalid: Boolean;
- begin
- // Test invalid escape sequence
- tokens := DoJsonHighlighting('{"invalid": "\x"}');
- foundInvalid := False;
- for i := 0 to High(tokens) do
- begin
- if tokens[i].Kind = shInvalid then
- begin
- foundInvalid := True;
- break;
- end;
- end;
- AssertTrue('Should find invalid escape sequence', foundInvalid);
- // Test unterminated string
- tokens := DoJsonHighlighting('{"unterminated": "hello');
- foundInvalid := False;
- for i := 0 to High(tokens) do
- begin
- if tokens[i].Kind = shInvalid then
- begin
- foundInvalid := True;
- break;
- end;
- end;
- AssertTrue('Should find unterminated string', foundInvalid);
- end;
- procedure TTestJsonHighlighter.TestJsonInvalidNumbers;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundInvalid: Boolean;
- begin
- // Test leading zero (invalid in JSON)
- tokens := DoJsonHighlighting('{"invalid": 01}');
- foundInvalid := False;
- for i := 0 to High(tokens) do
- begin
- if tokens[i].Kind = shInvalid then
- begin
- foundInvalid := True;
- break;
- end;
- end;
- AssertTrue('Should find invalid leading zero', foundInvalid);
- // Test incomplete decimal
- tokens := DoJsonHighlighting('{"incomplete": 12.}');
- foundInvalid := False;
- for i := 0 to High(tokens) do
- begin
- if tokens[i].Kind = shInvalid then
- begin
- foundInvalid := True;
- break;
- end;
- end;
- AssertTrue('Should find incomplete decimal', foundInvalid);
- end;
- procedure TTestJsonHighlighter.TestComplexJsonObject;
- var
- tokens: TSyntaxTokenArray;
- jsonText: string;
- i: Integer;
- hasKeys, hasStrings, hasNumbers, hasKeywords, hasSymbols: Boolean;
- begin
- jsonText := '{"name": "John", "age": 30, "married": true, "children": null}';
- tokens := DoJsonHighlighting(jsonText);
- AssertTrue('Should have multiple tokens', Length(tokens) > 10);
- // Check for different token types
- hasKeys := False;
- hasStrings := False;
- hasNumbers := False;
- hasKeywords := False;
- hasSymbols := False;
- for i := 0 to High(tokens) do
- begin
- case tokens[i].Kind of
- shKey: hasKeys := True;
- shStrings: hasStrings := True;
- shNumbers: hasNumbers := True;
- shKeyword: hasKeywords := True;
- shSymbol: hasSymbols := True;
- end;
- end;
- AssertTrue('Should contain key tokens', hasKeys);
- AssertTrue('Should contain string tokens', hasStrings);
- AssertTrue('Should contain number tokens', hasNumbers);
- AssertTrue('Should contain keyword tokens', hasKeywords);
- AssertTrue('Should contain symbol tokens', hasSymbols);
- end;
- procedure TTestJsonHighlighter.TestJsonArray;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundBrackets: Boolean;
- begin
- tokens := DoJsonHighlighting('[1, 2, 3]');
- foundBrackets := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '[') and (tokens[i].Kind = shSymbol) then
- begin
- foundBrackets := True;
- break;
- end;
- end;
- AssertTrue('Should find opening bracket', foundBrackets);
- foundBrackets := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = ']') and (tokens[i].Kind = shSymbol) then
- begin
- foundBrackets := True;
- break;
- end;
- end;
- AssertTrue('Should find closing bracket', foundBrackets);
- end;
- procedure TTestJsonHighlighter.TestNestedJsonStructures;
- var
- tokens: TSyntaxTokenArray;
- jsonText: string;
- i: Integer;
- hasKeys, hasNumbers: Boolean;
- begin
- jsonText := '{"person": {"name": "John", "address": {"city": "New York", "zip": 10001}}}';
- tokens := DoJsonHighlighting(jsonText);
- AssertTrue('Should handle nested structures', Length(tokens) > 15);
- // Verify we have both keys and numbers in nested structure
- hasKeys := False;
- hasNumbers := False;
- for i := 0 to High(tokens) do
- begin
- case tokens[i].Kind of
- shKey: hasKeys := True;
- shNumbers: hasNumbers := True;
- end;
- end;
- AssertTrue('Should contain keys in nested structure', hasKeys);
- AssertTrue('Should contain numbers in nested structure', hasNumbers);
- end;
- procedure TTestJsonHighlighter.TestJsonWhitespace;
- var
- tokens: TSyntaxTokenArray;
- jsonText: string;
- i: Integer;
- foundWhitespace: Boolean;
- begin
- jsonText := '{' + #10 + ' "name": "John"' + #10 + '}';
- tokens := DoJsonHighlighting(jsonText);
- foundWhitespace := False;
- for i := 0 to High(tokens) do
- begin
- if tokens[i].Kind = shDefault then
- begin
- foundWhitespace := True;
- break;
- end;
- end;
- AssertTrue('Should handle whitespace', foundWhitespace);
- end;
- procedure TTestJsonHighlighter.TestJsonScientificNotation;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundNumber: Boolean;
- begin
- // Test scientific notation
- tokens := DoJsonHighlighting('{"scientific": 1.23e-4}');
- foundNumber := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '1.23e-4') and (tokens[i].Kind = shNumbers) then
- begin
- foundNumber := True;
- break;
- end;
- end;
- AssertTrue('Should handle scientific notation', foundNumber);
- // Test with capital E
- tokens := DoJsonHighlighting('{"scientific": 2.5E+10}');
- foundNumber := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '2.5E+10') and (tokens[i].Kind = shNumbers) then
- begin
- foundNumber := True;
- break;
- end;
- end;
- AssertTrue('Should handle capital E notation', foundNumber);
- end;
- procedure TTestJsonHighlighter.TestJsonEdgeCases;
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- foundKey, foundString: Boolean;
- begin
- // Test single character key and value
- tokens := DoJsonHighlighting('{"a": "b"}');
- foundKey := False;
- foundString := False;
- for i := 0 to High(tokens) do
- begin
- if (tokens[i].Text = '"a"') and (tokens[i].Kind = shKey) then
- foundKey := True;
- if (tokens[i].Text = '"b"') and (tokens[i].Kind = shStrings) then
- foundString := True;
- end;
- AssertTrue('Should handle single character key', foundKey);
- AssertTrue('Should handle single character string', foundString);
- end;
- procedure TTestJsonHighlighter.TestJsonEmpty;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test empty object
- tokens := DoJsonHighlighting('{}');
- AssertTrue('Should handle empty object', Length(tokens) >= 2);
- // Test empty array
- tokens := DoJsonHighlighting('[]');
- AssertTrue('Should handle empty array', Length(tokens) >= 2);
- end;
- initialization
- RegisterTest(TTestJsonHighlighter);
- end.
|