fix build under newer KConfig by bump cmake min version

This commit is contained in:
2024-09-04 19:01:58 +08:00
parent 727a2ec214
commit 9fb3681e3a
1022 changed files with 4414 additions and 1375 deletions

View File

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,15 @@
The test/unit directory contains unit tests for Scintilla data structures.
The tests can be run on Windows, macOS, or Linux using g++ and GNU make.
The Catch test framework is used.
https://github.com/philsquared/Catch
The file catch.hpp is under the Boost Software License which is contained in LICENSE_1_0.txt
To run the tests on macOS or Linux:
make test
To run the tests on Windows:
mingw32-make test
Visual C++ (2010+) and nmake can also be used on Windows:
nmake -f test.mak test

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="Scintilla::Internal::SplitVector&lt;*&gt;">
<DisplayString>{{size = {lengthBody}}}</DisplayString>
<Expand>
<Item Name="[size]">lengthBody</Item>
<Item Name="[part1Length]">part1Length</Item>
<Item Name="[gap]">gapLength</Item>
<IndexListItems>
<Size>lengthBody</Size>
<ValueNode>body[($i&lt;part1Length)?$i:$i+gapLength]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="Scintilla::Internal::XSparseVector&lt;*&gt;">
<DisplayString>{{size = {values->lengthBody}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>values->lengthBody</Size>
<ValueNode>starts->body->body[($i&lt;starts->body->part1Length)?$i:$i+starts->body->gapLength]+($i&gt;starts->stepPartition?starts->stepLength:0)</ValueNode>
</IndexListItems>
<IndexListItems>
<Size>values->lengthBody</Size>
<ValueNode>values->body->body[($i&lt;values->body->part1Length)?$i:$i+values->body->gapLength]+($i&gt;values->stepPartition?values->stepLength:0)</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="Scintilla::Internal::Partitioning&lt;*&gt;">
<DisplayString>{{size = {body->lengthBody}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>body->lengthBody</Size>
<ValueNode>body->body[($i&lt;body->part1Length)?$i:$i+body->gapLength]+($i&gt;stepPartition?stepLength:0)</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="std::unique_ptr&lt;*&gt;">
<SmartPointer Usage="Minimal">_Mypair._Myval2</SmartPointer>
<DisplayString Condition="_Mypair._Myval2 == 0">empty</DisplayString>
<DisplayString Condition="_Mypair._Myval2 != 0">unique_ptr {*_Mypair._Myval2}</DisplayString>
<Expand>
<ExpandedItem Condition="_Mypair._Myval2 != 0">_Mypair._Myval2</ExpandedItem>
<ExpandedItem Condition="_Mypair._Myval2 != 0">_Mypair</ExpandedItem>
</Expand>
</Type>
</AutoVisualizer>

View File

@ -0,0 +1,5 @@
command.go.*.cxx=./unitTest
if PLAT_WIN
make.command=mingw32-make
command.go.*.cxx=unitTest
command.go.needs.$(file.patterns.cplusplus)=$(make.command)

View File

@ -0,0 +1,47 @@
/** @file UnitTester.cxx
** UnitTester.cpp : Defines the entry point for the console application.
**/
// Catch uses std::uncaught_exception which is deprecated in C++17.
// This define silences a warning from Visual C++.
#define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING
#include <cstdio>
#include <cstdarg>
#include <string_view>
#include <vector>
#include <optional>
#include <memory>
#include "Debugging.h"
#if defined(_WIN32)
#define CATCH_CONFIG_WINDOWS_CRTDBG
#endif
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"
using namespace Scintilla::Internal;
// Needed for PLATFORM_ASSERT in code being tested
void Platform::Assert(const char *c, const char *file, int line) noexcept {
fprintf(stderr, "Assertion [%s] failed at %s %d\n", c, file, line);
abort();
}
void Platform::DebugPrintf(const char *format, ...) noexcept {
char buffer[2000];
va_list pArguments;
va_start(pArguments, format);
vsnprintf(buffer, std::size(buffer), format, pArguments);
va_end(pArguments);
fprintf(stderr, "%s", buffer);
}
int main(int argc, char* argv[]) {
const int result = Catch::Session().run(argc, argv);
return result;
}

View File

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{35688A27-D91B-453A-8A05-65A7F28DEFBF}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>UnitTester</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<EnableClangTidyCodeAnalysis>true</EnableClangTidyCodeAnalysis>
<CodeAnalysisRuleSet>..\..\..\..\..\Users\Neil\SensibleRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>CHECK_CORRECTNESS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\include\;..\..\src\;..\..\lexlib\</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>CHECK_CORRECTNESS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\include\;..\..\src\;..\..\lexlib\</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>CHECK_CORRECTNESS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\include\;..\..\src\;..\..\lexlib\</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>CHECK_CORRECTNESS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\include\;..\..\src\;..\..\lexlib\</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\src\CaseConvert.cxx" />
<ClCompile Include="..\..\src\CaseFolder.cxx" />
<ClCompile Include="..\..\src\CellBuffer.cxx" />
<ClCompile Include="..\..\src\ChangeHistory.cxx" />
<ClCompile Include="..\..\src\CharacterCategoryMap.cxx" />
<ClCompile Include="..\..\src\CharClassify.cxx" />
<ClCompile Include="..\..\src\ContractionState.cxx" />
<ClCompile Include="..\..\src\Decoration.cxx" />
<ClCompile Include="..\..\src\Document.cxx" />
<ClCompile Include="..\..\src\Geometry.cxx" />
<ClCompile Include="..\..\src\PerLine.cxx" />
<ClCompile Include="..\..\src\RESearch.cxx" />
<ClCompile Include="..\..\src\RunStyles.cxx" />
<ClCompile Include="..\..\src\UndoHistory.cxx" />
<ClCompile Include="..\..\src\UniConversion.cxx" />
<ClCompile Include="..\..\src\UniqueString.cxx" />
<ClCompile Include="test*.cxx" />
<ClCompile Include="UnitTester.cxx" />
</ItemGroup>
<ItemGroup>
<Natvis Include="Sci.natvis" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,89 @@
# Build all the unit tests using GNU make and either g++ or clang
# Should be run using mingw32-make on Windows, not nmake
# On Windows g++ is used, on macOS clang, and on Linux G++ is used by default
# but clang can be used by defining CLANG when invoking make
# clang works only with libc++, not libstdc++
# Tested with clang 9 and g++ 9
CXXSTD=c++17
ifndef windir
ifeq ($(shell uname),Darwin)
# On macOS (detected with Darwin uname) always use clang as g++ is old version
CLANG = 1
USELIBCPP = 1
endif
endif
CXXFLAGS += $(OPTIMIZATION)
CXXFLAGS += --std=$(CXXSTD)
ifdef CLANG
CXX = clang++
ifdef USELIBCPP
# macOS, use libc++ but don't have sanitizers
CXXFLAGS += --stdlib=libc++
else
ifndef windir
# Linux, have sanitizers
SANITIZE = -fsanitize=address,undefined
CXXFLAGS += $(SANITIZE)
endif
endif
else
CXX = g++
endif
ifdef windir
DEL = del /q
EXE = unitTest.exe
else
DEL = rm -f
EXE = unitTest
endif
vpath %.cxx ../../src
INCLUDEDIRS = -I ../../include -I ../../src
CPPFLAGS += $(INCLUDEDIRS)
CXXFLAGS += -Wall -Wextra
# Files in this directory containing tests
TESTSRC=$(wildcard test*.cxx)
TESTOBJ=$(TESTSRC:.cxx=.o)
# Files being tested from scintilla/src directory
TESTEDOBJ=\
CaseConvert.o \
CaseFolder.o \
CellBuffer.o \
ChangeHistory.o \
CharacterCategoryMap.o \
CharClassify.o \
ContractionState.o \
Decoration.o \
Document.o \
Geometry.o \
PerLine.o \
RESearch.o \
RunStyles.o \
UndoHistory.o \
UniConversion.o \
UniqueString.o
TESTS=$(EXE)
all: $(TESTS)
test: $(TESTS)
./$(EXE)
clean:
$(DEL) $(TESTS) *.o *.obj *.exe
%.o: %.cxx
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
$(EXE): $(TESTOBJ) $(TESTEDOBJ) unitTest.o
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LINKFLAGS) $^ -o $@

View File

@ -0,0 +1,43 @@
# Build all the unit tests with Microsoft Visual C++ using nmake
# Tested with Visual C++ 2019
DEL = del /q
EXE = unitTest.exe
INCLUDEDIRS = /I../../include /I../../src
CXXFLAGS = /MP /EHsc /std:c++17 $(OPTIMIZATION) /nologo /D_HAS_AUTO_PTR_ETC=1 /wd 4805 $(INCLUDEDIRS)
# Files in this directory containing tests
TESTSRC=test*.cxx
# Files being tested from scintilla/src directory
TESTEDSRC=\
../../src/CaseConvert.cxx \
../../src/CaseFolder.cxx \
../../src/CellBuffer.cxx \
../../src/ChangeHistory.cxx \
../../src/CharacterCategoryMap.cxx \
../../src/CharClassify.cxx \
../../src/ContractionState.cxx \
../../src/Decoration.cxx \
../../src/Document.cxx \
../../src/Geometry.cxx \
../../src/PerLine.cxx \
../../src/RESearch.cxx \
../../src/RunStyles.cxx \
../../src/UndoHistory.cxx \
../../src/UniConversion.cxx \
../../src/UniqueString.cxx
TESTS=$(EXE)
all: $(TESTS)
test: $(TESTS)
$(EXE)
clean:
$(DEL) $(TESTS) *.o *.obj *.exe
$(EXE): $(TESTSRC) $(TESTEDSRC) $(@B).obj
$(CXX) $(CXXFLAGS) /Fe$@ $**

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
/** @file testCharClassify.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstring>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
#include <iostream>
#include "Debugging.h"
#include "CharClassify.h"
#include "catch.hpp"
using namespace Scintilla::Internal;
// Test CharClassify.
class CharClassifyTest {
protected:
CharClassifyTest() {
pcc = std::make_unique<CharClassify>();
for (int ch = 0; ch < 256; ch++) {
if (ch == '\r' || ch == '\n')
charClass[ch] = CharacterClass::newLine;
else if (ch < 0x20 || ch == ' ' || ch == '\x7f')
charClass[ch] = CharacterClass::space;
else if (ch >= 0x80 || isalnum(ch) || ch == '_')
charClass[ch] = CharacterClass::word;
else
charClass[ch] = CharacterClass::punctuation;
}
}
// Avoid warnings, deleted so never called.
CharClassifyTest(const CharClassifyTest &) = delete;
std::unique_ptr<CharClassify> pcc;
CharacterClass charClass[256] {};
static const char* GetClassName(CharacterClass charClass) noexcept {
switch(charClass) {
#define CASE(c) case CharacterClass::c: return #c
CASE(space);
CASE(newLine);
CASE(word);
CASE(punctuation);
#undef CASE
default:
return "<unknown>";
}
}
};
TEST_CASE_METHOD(CharClassifyTest, "Defaults") {
for (int i = 0; i < 256; i++) {
if (charClass[i] != pcc->GetClass(i))
std::cerr
<< "Character " << i
<< " should be class " << GetClassName(charClass[i])
<< ", but got " << GetClassName(pcc->GetClass(i)) << std::endl;
REQUIRE(charClass[i] == pcc->GetClass(i));
}
}
TEST_CASE_METHOD(CharClassifyTest, "Custom") {
unsigned char buf[2] = {0, 0};
for (int i = 0; i < 256; i++) {
const CharacterClass thisClass = static_cast<CharacterClass>(i % 4);
buf[0] = i;
pcc->SetCharClasses(buf, thisClass);
charClass[i] = thisClass;
}
for (int i = 0; i < 256; i++) {
if (charClass[i] != pcc->GetClass(i))
std::cerr
<< "Character " << i
<< " should be class " << GetClassName(charClass[i])
<< ", but got " << GetClassName(pcc->GetClass(i)) << std::endl;
REQUIRE(charClass[i] == pcc->GetClass(i));
}
}
TEST_CASE_METHOD(CharClassifyTest, "CharsOfClass") {
unsigned char buf[2] = {0, 0};
for (int i = 1; i < 256; i++) {
const CharacterClass thisClass = static_cast<CharacterClass>(i % 4);
buf[0] = i;
pcc->SetCharClasses(buf, thisClass);
charClass[i] = thisClass;
}
for (int classVal = 0; classVal < 4; ++classVal) {
const CharacterClass thisClass = static_cast<CharacterClass>(classVal % 4);
const int size = pcc->GetCharsOfClass(thisClass, nullptr);
std::vector<unsigned char> buffer(size+1);
const unsigned char *pBuffer = buffer.data();
pcc->GetCharsOfClass(thisClass, buffer.data());
for (int i = 1; i < 256; i++) {
if (charClass[i] == thisClass) {
if (!memchr(pBuffer, i, size))
std::cerr
<< "Character " << i
<< " should be class " << GetClassName(thisClass)
<< ", but was not in GetCharsOfClass;"
<< " it is reported to be "
<< GetClassName(pcc->GetClass(i)) << std::endl;
REQUIRE(memchr(pBuffer, i, size));
} else {
if (memchr(pBuffer, i, size))
std::cerr
<< "Character " << i
<< " should not be class " << GetClassName(thisClass)
<< ", but was in GetCharsOfClass"
<< " it is reported to be "
<< GetClassName(pcc->GetClass(i)) << std::endl;
REQUIRE_FALSE(memchr(pBuffer, i, size));
}
}
}
}

View File

@ -0,0 +1,73 @@
/** @file testCharacterCategoryMap.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstddef>
#include <cstring>
#include <stdexcept>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
#include "Debugging.h"
#include "CharacterCategoryMap.h"
#include "catch.hpp"
using namespace Scintilla;
using namespace Scintilla::Internal;
// Test CharacterCategoryMap.
TEST_CASE("CharacterCategoryMap") {
const CharacterCategoryMap ccm;
SECTION("LowerCaseLetter") {
const CharacterCategory cc = ccm.CategoryFor('a');
REQUIRE(cc == CharacterCategory::ccLl);
}
SECTION("All") {
REQUIRE(ccm.CategoryFor('A') == CharacterCategory::ccLu);
REQUIRE(ccm.CategoryFor('a') == CharacterCategory::ccLl);
REQUIRE(ccm.CategoryFor(0x01C5) == CharacterCategory::ccLt);
REQUIRE(ccm.CategoryFor(0x0E46) == CharacterCategory::ccLm);
REQUIRE(ccm.CategoryFor(0x4E00) == CharacterCategory::ccLo);
REQUIRE(ccm.CategoryFor(0x0300) == CharacterCategory::ccMn);
REQUIRE(ccm.CategoryFor(0x0903) == CharacterCategory::ccMc);
REQUIRE(ccm.CategoryFor(0x20E0) == CharacterCategory::ccMe);
REQUIRE(ccm.CategoryFor('7') == CharacterCategory::ccNd);
REQUIRE(ccm.CategoryFor(0x2160) == CharacterCategory::ccNl);
REQUIRE(ccm.CategoryFor(0x00BC) == CharacterCategory::ccNo);
REQUIRE(ccm.CategoryFor('_') == CharacterCategory::ccPc);
REQUIRE(ccm.CategoryFor('-') == CharacterCategory::ccPd);
REQUIRE(ccm.CategoryFor('(') == CharacterCategory::ccPs);
REQUIRE(ccm.CategoryFor('}') == CharacterCategory::ccPe);
REQUIRE(ccm.CategoryFor(0x00AB) == CharacterCategory::ccPi);
REQUIRE(ccm.CategoryFor(0x00BB) == CharacterCategory::ccPf);
REQUIRE(ccm.CategoryFor('"') == CharacterCategory::ccPo);
REQUIRE(ccm.CategoryFor('+') == CharacterCategory::ccSm);
REQUIRE(ccm.CategoryFor('$') == CharacterCategory::ccSc);
REQUIRE(ccm.CategoryFor(0x02C2) == CharacterCategory::ccSk);
REQUIRE(ccm.CategoryFor(0x00A6) == CharacterCategory::ccSo);
REQUIRE(ccm.CategoryFor(' ') == CharacterCategory::ccZs);
REQUIRE(ccm.CategoryFor(0x2028) == CharacterCategory::ccZl);
REQUIRE(ccm.CategoryFor(0x2029) == CharacterCategory::ccZp);
REQUIRE(ccm.CategoryFor('\n') == CharacterCategory::ccCc);
REQUIRE(ccm.CategoryFor(0x00AD) == CharacterCategory::ccCf);
REQUIRE(ccm.CategoryFor(0xD800) == CharacterCategory::ccCs);
REQUIRE(ccm.CategoryFor(0xE000) == CharacterCategory::ccCo);
REQUIRE(ccm.CategoryFor(0xFFFE) == CharacterCategory::ccCn);
}
}

View File

@ -0,0 +1,205 @@
/** @file testContractionState.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstddef>
#include <cstring>
#include <stdexcept>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
#include "Debugging.h"
#include "Position.h"
#include "UniqueString.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "ContractionState.h"
#include "catch.hpp"
using namespace Scintilla::Internal;
// Test ContractionState.
TEST_CASE("ContractionState") {
std::unique_ptr<IContractionState> pcs = ContractionStateCreate(false);
SECTION("IsEmptyInitially") {
REQUIRE(1 == pcs->LinesInDoc());
REQUIRE(1 == pcs->LinesDisplayed());
REQUIRE(0 == pcs->DisplayFromDoc(0));
REQUIRE(0 == pcs->DocFromDisplay(0));
}
SECTION("OneLine") {
pcs->InsertLines(0, 1);
REQUIRE(2 == pcs->LinesInDoc());
REQUIRE(2 == pcs->LinesDisplayed());
REQUIRE(0 == pcs->DisplayFromDoc(0));
REQUIRE(0 == pcs->DocFromDisplay(0));
REQUIRE(1 == pcs->DisplayFromDoc(1));
REQUIRE(1 == pcs->DocFromDisplay(1));
}
SECTION("InsertionThenDeletions") {
pcs->InsertLines(0,4);
pcs->DeleteLines(1, 1);
REQUIRE(4 == pcs->LinesInDoc());
REQUIRE(4 == pcs->LinesDisplayed());
for (int l=0;l<4;l++) {
REQUIRE(l == pcs->DisplayFromDoc(l));
REQUIRE(l == pcs->DocFromDisplay(l));
}
pcs->DeleteLines(0,2);
REQUIRE(2 == pcs->LinesInDoc());
REQUIRE(2 == pcs->LinesDisplayed());
for (int l=0;l<2;l++) {
REQUIRE(l == pcs->DisplayFromDoc(l));
REQUIRE(l == pcs->DocFromDisplay(l));
}
}
SECTION("ShowHide") {
pcs->InsertLines(0,4);
REQUIRE(true == pcs->GetVisible(0));
REQUIRE(true == pcs->GetVisible(1));
REQUIRE(true == pcs->GetVisible(2));
REQUIRE(5 == pcs->LinesDisplayed());
pcs->SetVisible(1, 1, false);
REQUIRE(true == pcs->GetVisible(0));
REQUIRE(false == pcs->GetVisible(1));
REQUIRE(true == pcs->GetVisible(2));
REQUIRE(4 == pcs->LinesDisplayed());
REQUIRE(true == pcs->HiddenLines());
pcs->SetVisible(1, 2, true);
for (int l=0;l<4;l++) {
REQUIRE(true == pcs->GetVisible(0));
}
pcs->SetVisible(1, 1, false);
REQUIRE(false == pcs->GetVisible(1));
pcs->ShowAll();
for (int l=0;l<4;l++) {
REQUIRE(true == pcs->GetVisible(0));
}
REQUIRE(false == pcs->HiddenLines());
}
SECTION("Hidden") {
pcs->InsertLines(0,1);
for (int l=0;l<2;l++) {
REQUIRE(true == pcs->GetVisible(0));
}
REQUIRE(false == pcs->HiddenLines());
pcs->SetVisible(1, 1, false);
REQUIRE(true == pcs->GetVisible(0));
REQUIRE(false == pcs->GetVisible(1));
REQUIRE(true == pcs->HiddenLines());
REQUIRE(1 == pcs->LinesDisplayed());
pcs->SetVisible(1, 1, true);
for (int l=0;l<2;l++) {
REQUIRE(true == pcs->GetVisible(0));
}
REQUIRE(false == pcs->HiddenLines());
}
SECTION("Hide All") {
pcs->InsertLines(0,1);
for (int l=0;l<2;l++) {
REQUIRE(true == pcs->GetVisible(0));
}
REQUIRE(false == pcs->HiddenLines());
pcs->SetVisible(0, 1, false);
REQUIRE(false == pcs->GetVisible(0));
REQUIRE(false == pcs->GetVisible(1));
REQUIRE(true == pcs->HiddenLines());
REQUIRE(0 == pcs->LinesDisplayed());
}
SECTION("Contracting") {
pcs->InsertLines(0,4);
for (int l=0;l<4;l++) {
REQUIRE(true == pcs->GetExpanded(l));
}
pcs->SetExpanded(2, false);
REQUIRE(true == pcs->GetExpanded(1));
REQUIRE(false == pcs->GetExpanded(2));
REQUIRE(true == pcs->GetExpanded(3));
REQUIRE(2 == pcs->ContractedNext(0));
REQUIRE(2 == pcs->ContractedNext(1));
REQUIRE(2 == pcs->ContractedNext(2));
REQUIRE(-1 == pcs->ContractedNext(3));
pcs->SetExpanded(2, true);
REQUIRE(true == pcs->GetExpanded(1));
REQUIRE(true == pcs->GetExpanded(2));
REQUIRE(true == pcs->GetExpanded(3));
}
SECTION("ExpandAll") {
pcs->InsertLines(0,4);
for (int l=0;l<4;l++) {
REQUIRE(true == pcs->GetExpanded(l));
}
pcs->SetExpanded(2, false);
REQUIRE(true == pcs->GetExpanded(1));
REQUIRE(false == pcs->GetExpanded(2));
REQUIRE(true == pcs->GetExpanded(3));
pcs->SetExpanded(1, false);
REQUIRE(false == pcs->GetExpanded(1));
REQUIRE(false == pcs->GetExpanded(2));
REQUIRE(true == pcs->GetExpanded(3));
REQUIRE(true == pcs->ExpandAll());
REQUIRE(true == pcs->GetExpanded(1));
REQUIRE(true == pcs->GetExpanded(2));
REQUIRE(true == pcs->GetExpanded(3));
}
SECTION("ChangeHeight") {
pcs->InsertLines(0,4);
for (int l=0;l<4;l++) {
REQUIRE(1 == pcs->GetHeight(l));
}
pcs->SetHeight(1, 2);
REQUIRE(1 == pcs->GetHeight(0));
REQUIRE(2 == pcs->GetHeight(1));
REQUIRE(1 == pcs->GetHeight(2));
}
SECTION("SetFoldDisplayText") {
pcs->InsertLines(0, 4);
REQUIRE(5 == pcs->LinesInDoc());
pcs->SetFoldDisplayText(1, "abc");
REQUIRE(strcmp(pcs->GetFoldDisplayText(1), "abc") == 0);
pcs->SetFoldDisplayText(1, "def");
REQUIRE(strcmp(pcs->GetFoldDisplayText(1), "def") == 0);
pcs->SetFoldDisplayText(1, nullptr);
REQUIRE(static_cast<const char *>(nullptr) == pcs->GetFoldDisplayText(1));
// At end
pcs->SetFoldDisplayText(5, "xyz");
REQUIRE(strcmp(pcs->GetFoldDisplayText(5), "xyz") == 0);
pcs->DeleteLines(4, 1);
REQUIRE(strcmp(pcs->GetFoldDisplayText(4), "xyz") == 0);
}
}

View File

@ -0,0 +1,98 @@
/** @file testDecoration.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstddef>
#include <cstring>
#include <stdexcept>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
#include "Debugging.h"
#include "Position.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "Decoration.h"
#include "catch.hpp"
constexpr int indicator=4;
using namespace Scintilla::Internal;
// Test Decoration.
TEST_CASE("Decoration") {
std::unique_ptr<IDecoration> deco = DecorationCreate(false, indicator);
SECTION("HasCorrectIndicator") {
REQUIRE(indicator == deco->Indicator());
}
SECTION("IsEmptyInitially") {
REQUIRE(0 == deco->Length());
REQUIRE(1 == deco->Runs());
REQUIRE(deco->Empty());
}
SECTION("SimpleSpace") {
deco->InsertSpace(0, 1);
REQUIRE(deco->Empty());
}
SECTION("SimpleRun") {
deco->InsertSpace(0, 1);
deco->SetValueAt(0, 2);
REQUIRE(!deco->Empty());
}
}
// Test DecorationList.
TEST_CASE("DecorationList") {
std::unique_ptr<IDecorationList> decol = DecorationListCreate(false);
SECTION("HasCorrectIndicator") {
decol->SetCurrentIndicator(indicator);
REQUIRE(indicator == decol->GetCurrentIndicator());
}
SECTION("HasCorrectCurrentValue") {
constexpr int value = 55;
decol->SetCurrentValue(value);
REQUIRE(value == decol->GetCurrentValue());
}
SECTION("ExpandSetValues") {
decol->SetCurrentIndicator(indicator);
decol->InsertSpace(0, 9);
constexpr int value = 59;
constexpr Sci::Position position = 4;
constexpr Sci::Position fillLength = 3;
auto fr = decol->FillRange(position, value, fillLength);
REQUIRE(fr.changed);
REQUIRE(fr.position == 4);
REQUIRE(fr.fillLength == 3);
REQUIRE(decol->ValueAt(indicator, 5) == value);
REQUIRE(decol->AllOnFor(5) == (1 << indicator));
REQUIRE(decol->Start(indicator, 5) == 4);
REQUIRE(decol->End(indicator, 5) == 7);
constexpr int indicatorB=6;
decol->SetCurrentIndicator(indicatorB);
fr = decol->FillRange(position, value, fillLength);
REQUIRE(fr.changed);
REQUIRE(decol->AllOnFor(5) == ((1 << indicator) | (1 << indicatorB)));
decol->DeleteRange(5, 1);
REQUIRE(decol->Start(indicatorB, 5) == 4);
REQUIRE(decol->End(indicatorB, 5) == 6);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,246 @@
/** @file testGeometry.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstdint>
#include "Geometry.h"
#include "catch.hpp"
using namespace Scintilla;
using namespace Scintilla::Internal;
// Test Geometry.
TEST_CASE("Point") {
SECTION("Point") {
constexpr Point pt(1.0, 2.0);
REQUIRE(pt.x == 1.0);
REQUIRE(pt.y == 2.0);
constexpr Point pti = Point::FromInts(1, 2);
REQUIRE(pt == pti);
constexpr Point pt2 = pt + pt;
REQUIRE(pt != pt2);
constexpr Point ptBack = pt2 - pt;
REQUIRE(pt == ptBack);
}
}
TEST_CASE("Interval") {
SECTION("Interval") {
constexpr Interval interval { 1.0, 2.0 };
REQUIRE(interval.left == 1.0);
REQUIRE(interval.right == 2.0);
REQUIRE(interval.Width() == 1.0);
REQUIRE(!interval.Empty());
constexpr Interval empty { 4.0, 4.0 };
REQUIRE(empty.Empty());
REQUIRE(empty.Width() == 0.0);
REQUIRE(!(interval == empty));
REQUIRE(!(interval.Intersects(empty)));
constexpr Interval inside { 1.7, 1.8 };
REQUIRE(interval.Intersects(inside));
constexpr Interval straddles { 1.7, 2.8 };
REQUIRE(interval.Intersects(straddles));
}
SECTION("Interval") {
constexpr Interval interval1{ 1.0, 3.0 };
constexpr Interval interval2{ 2.0, 4.0 };
REQUIRE(Intersection(interval1, interval2) == Interval{ 2.0, 3.0 });
}
}
TEST_CASE("PRectangle") {
SECTION("PRectangle") {
constexpr PRectangle rc(1.0, 2.0, 3.0, 4.0);
REQUIRE(rc.left == 1.0);
REQUIRE(rc.top == 2.0);
REQUIRE(rc.right == 3.0);
REQUIRE(rc.bottom == 4.0);
REQUIRE(rc.Width() == 2.0);
REQUIRE(rc.Height() == 2.0);
REQUIRE(!rc.Empty());
constexpr PRectangle rci = PRectangle::FromInts(1, 2, 3, 4);
REQUIRE(rc == rci);
constexpr PRectangle rcEmpty;
REQUIRE(rcEmpty.Empty());
}
SECTION("Contains") {
constexpr PRectangle rc(1.0, 2.0, 3.0, 4.0);
constexpr Point pt(1.5, 2.5);
REQUIRE(rc.Contains(pt));
REQUIRE(rc.ContainsWholePixel(pt));
constexpr Point ptNearEdge(2.9, 2.5);
REQUIRE(rc.Contains(ptNearEdge));
REQUIRE(!rc.ContainsWholePixel(ptNearEdge));
constexpr Point ptOutside(1.5, 20.5);
REQUIRE(!rc.Contains(ptOutside));
REQUIRE(!rc.ContainsWholePixel(ptOutside));
constexpr PRectangle rcInside(1.5, 2.0, 2.5, 4.0);
REQUIRE(rc.Contains(rcInside));
REQUIRE(rc.Intersects(rcInside));
constexpr PRectangle rcIntersects(1.5, 2.0, 3.5, 4.0);
REQUIRE(!rc.Contains(rcIntersects));
REQUIRE(rc.Intersects(rcIntersects));
constexpr PRectangle rcSeparate(11.0, 12.0, 13.0, 14.0);
REQUIRE(!rc.Contains(rcSeparate));
REQUIRE(!rc.Intersects(rcSeparate));
constexpr Point ptCentre = rc.Centre();
REQUIRE(ptCentre == Point(2.0, 3.0));
}
SECTION("Move") {
PRectangle rc(1.0, 2.0, 3.0, 4.0);
rc.Move(1.0, 10.0);
REQUIRE(rc == PRectangle(2.0, 12.0, 4.0, 14.0));
}
SECTION("Inset") {
constexpr PRectangle rc(1.0, 2.0, 3.0, 4.0);
constexpr PRectangle rcInset = rc.Inset(0.5);
REQUIRE(rcInset == PRectangle(1.5, 2.5, 2.5, 3.5));
constexpr PRectangle rcInsetPt = rc.Inset(Point(0.25, 0.5));
REQUIRE(rcInsetPt == PRectangle(1.25, 2.5, 2.75, 3.5));
}
SECTION("Clamp") {
constexpr PRectangle rc(1.0, 2.0, 3.0, 4.0);
const PRectangle cutBottom = Clamp(rc, Edge::bottom, 3.5);
REQUIRE(cutBottom == PRectangle(1.0, 2.0, 3.0, 3.5));
const PRectangle justBottom = Side(rc, Edge::bottom, 0.5);
REQUIRE(justBottom == PRectangle(1.0, 3.5, 3.0, 4.0));
constexpr PRectangle rcInset = rc.Inset(0.5);
REQUIRE(rcInset == PRectangle(1.5, 2.5, 2.5, 3.5));
constexpr PRectangle rcInsetPt = rc.Inset(Point(0.25, 0.5));
REQUIRE(rcInsetPt == PRectangle(1.25, 2.5, 2.75, 3.5));
const Interval bounds = HorizontalBounds(rcInsetPt);
REQUIRE(bounds == Interval{ 1.25, 2.75 });
const PRectangle applyBounds = Intersection(rc, bounds);
REQUIRE(applyBounds == PRectangle(1.25, 2.0, 2.75, 4.0));
}
SECTION("PixelAlign") {
// Whole pixels
REQUIRE(PixelAlign(1.0, 1) == 1.0);
REQUIRE(PixelAlignFloor(1.0, 1) == 1.0);
REQUIRE(PixelAlignCeil(1.0, 1) == 1.0);
REQUIRE(PixelAlign(1.25, 1) == 1.0);
REQUIRE(PixelAlignFloor(1.25, 1) == 1.0);
REQUIRE(PixelAlignCeil(1.25, 1) == 2.0);
REQUIRE(PixelAlign(1.5, 1) == 2.0);
REQUIRE(PixelAlignFloor(1.5, 1) == 1.0);
REQUIRE(PixelAlignCeil(1.5, 1) == 2.0);
REQUIRE(PixelAlign(1.75, 1) == 2.0);
REQUIRE(PixelAlignFloor(1.75, 1) == 1.0);
REQUIRE(PixelAlignCeil(1.75, 1) == 2.0);
REQUIRE(PixelAlign(Point(1.75, 1.25), 1) == Point(2.0, 1.0));
REQUIRE(PixelAlign(Point(1.5, 1.0), 1) == Point(2.0, 1.0));
// Half pixels
REQUIRE(PixelAlign(1.0, 2) == 1.0);
REQUIRE(PixelAlignFloor(1.0, 2) == 1.0);
REQUIRE(PixelAlignCeil(1.0, 2) == 1.0);
REQUIRE(PixelAlign(1.25, 2) == 1.5);
REQUIRE(PixelAlignFloor(1.25, 2) == 1.0);
REQUIRE(PixelAlignCeil(1.25, 2) == 1.5);
REQUIRE(PixelAlign(1.5, 2) == 1.5);
REQUIRE(PixelAlignFloor(1.5, 2) == 1.5);
REQUIRE(PixelAlignCeil(1.5, 2) == 1.5);
REQUIRE(PixelAlign(1.75, 2) == 2.0);
REQUIRE(PixelAlignFloor(1.75, 2) == 1.5);
REQUIRE(PixelAlignCeil(1.75, 2) == 2.0);
REQUIRE(PixelAlign(Point(1.75, 1.25), 2) == Point(2.0, 1.5));
REQUIRE(PixelAlign(Point(1.5, 1.0), 2) == Point(1.5, 1.0));
// x->round, y->floored
REQUIRE(PixelAlign(PRectangle(1.0, 1.25, 1.5, 1.75), 1) == PRectangle(1.0, 1.0, 2.0, 1.0));
REQUIRE(PixelAlign(PRectangle(1.0, 1.25, 1.5, 1.75), 2) == PRectangle(1.0, 1.0, 1.5, 1.5));
// x->outside(floor left, ceil right), y->floored
REQUIRE(PixelAlignOutside(PRectangle(1.1, 1.25, 1.6, 1.75), 1) == PRectangle(1.0, 1.0, 2.0, 1.0));
REQUIRE(PixelAlignOutside(PRectangle(1.1, 1.25, 1.6, 1.75), 2) == PRectangle(1.0, 1.0, 2.0, 1.5));
}
}
TEST_CASE("ColourRGBA") {
SECTION("ColourRGBA") {
constexpr ColourRGBA colour(0x10203040);
constexpr ColourRGBA colourFromRGB(0x40, 0x30, 0x20, 0x10);
REQUIRE(colour == colourFromRGB);
REQUIRE(colour.GetRed() == 0x40);
REQUIRE(colour.GetGreen() == 0x30);
REQUIRE(colour.GetBlue() == 0x20);
REQUIRE(colour.GetAlpha() == 0x10);
REQUIRE(!colour.IsOpaque());
REQUIRE(colour.AsInteger() == 0x10203040);
REQUIRE(ColourRGBA(colour, 0x80) == ColourRGBA(0x40, 0x30, 0x20, 0x80));
REQUIRE(ColourRGBA::FromRGB(0x203040) == ColourRGBA(0x40, 0x30, 0x20, 0xff));
REQUIRE(ColourRGBA::FromIpRGB(0x203040) == ColourRGBA(0x40, 0x30, 0x20, 0xff));
constexpr ColourRGBA colour01(0x00ff00ff);
REQUIRE(colour01.GetRedComponent() == 1.0);
REQUIRE(colour01.GetGreenComponent() == 0.0);
REQUIRE(colour01.GetBlueComponent() == 1.0);
REQUIRE(colour01.GetAlphaComponent() == 0.0);
// Opaque colours
constexpr ColourRGBA colourRGB(0xff203040);
REQUIRE(colourRGB.IsOpaque());
constexpr ColourRGBA colourOpaque(0x40, 0x30, 0x20, 0xff);
REQUIRE(colourRGB == colourOpaque);
REQUIRE(colourRGB.OpaqueRGB() == 0x203040);
REQUIRE(colourRGB.WithoutAlpha() == ColourRGBA(0x40, 0x30, 0x20, 0));
}
SECTION("Mixing") {
constexpr ColourRGBA colourMin(0x10, 0x20, 0x30, 0x40);
constexpr ColourRGBA colourMax(0x30, 0x60, 0x90, 0xC0);
constexpr ColourRGBA colourMid(0x20, 0x40, 0x60, 0x80);
REQUIRE(colourMin.MixedWith(colourMax) == colourMid);
REQUIRE(colourMin.MixedWith(colourMax, 0.5) == colourMid);
// 3/4 between min and max
constexpr ColourRGBA colour75(0x28, 0x50, 0x78, 0xA0);
REQUIRE(colourMin.MixedWith(colourMax, 0.75) == colour75);
}
}

View File

@ -0,0 +1,224 @@
/** @file testPartitioning.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstddef>
#include <cstring>
#include <stdexcept>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
#include "Debugging.h"
#include "Position.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "catch.hpp"
using namespace Scintilla::Internal;
// Test Partitioning.
TEST_CASE("CompileCopying Partitioning") {
// These are compile-time tests to check that basic copy and move
// operations are defined correctly.
SECTION("CopyingMoving") {
Partitioning<int> s;
Partitioning<int> s2;
// Copy constructor
const Partitioning<int> sa(s);
// Copy assignment
Partitioning<int> sb;
sb = s;
// Move constructor
const Partitioning<int> sc(std::move(s));
// Move assignment
Partitioning<int> sd;
sd = (std::move(s2));
}
}
TEST_CASE("Partitioning") {
Partitioning<Sci::Position> part;
SECTION("IsEmptyInitially") {
REQUIRE(1 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(part.Partitions()));
REQUIRE(0 == part.PartitionFromPosition(0));
}
SECTION("SimpleInsert") {
part.InsertText(0, 1);
REQUIRE(1 == part.Partitions());
REQUIRE(1 == part.PositionFromPartition(part.Partitions()));
}
SECTION("TwoPartitions") {
part.InsertText(0, 2);
part.InsertPartition(1, 1);
REQUIRE(2 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(0));
REQUIRE(1 == part.PositionFromPartition(1));
REQUIRE(2 == part.PositionFromPartition(2));
}
SECTION("MoveStart") {
part.InsertText(0, 3);
part.InsertPartition(1, 2);
part.SetPartitionStartPosition(1,1);
REQUIRE(2 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(0));
REQUIRE(1 == part.PositionFromPartition(1));
REQUIRE(3 == part.PositionFromPartition(2));
part.Check();
}
SECTION("InsertAgain") {
part.InsertText(0, 3);
part.InsertPartition(1, 2);
part.InsertText(0,3);
part.InsertText(1,2);
REQUIRE(2 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(0));
REQUIRE(5 == part.PositionFromPartition(1));
REQUIRE(8 == part.PositionFromPartition(2));
part.Check();
}
SECTION("InsertMultiple") {
part.InsertText(0, 10);
const Sci::Position positions[] { 2, 5, 7 };
part.InsertPartitions(1, positions, std::size(positions));
REQUIRE(4 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(0));
REQUIRE(2 == part.PositionFromPartition(1));
REQUIRE(5 == part.PositionFromPartition(2));
REQUIRE(7 == part.PositionFromPartition(3));
REQUIRE(10 == part.PositionFromPartition(4));
part.Check();
}
SECTION("InsertMultipleWithCast") {
part.InsertText(0, 9);
REQUIRE(1 == part.Partitions());
const ptrdiff_t positionsp[]{ 2, 4, 6, 8 };
part.InsertPartitionsWithCast(1, positionsp, std::size(positionsp));
REQUIRE(5 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(0));
REQUIRE(2 == part.PositionFromPartition(1));
REQUIRE(4 == part.PositionFromPartition(2));
REQUIRE(6 == part.PositionFromPartition(3));
REQUIRE(8 == part.PositionFromPartition(4));
REQUIRE(9 == part.PositionFromPartition(5));
part.Check();
}
SECTION("InsertReversed") {
part.InsertText(0, 3);
part.InsertPartition(1, 2);
part.InsertText(1,2);
part.InsertText(0,3);
REQUIRE(2 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(0));
REQUIRE(5 == part.PositionFromPartition(1));
REQUIRE(8 == part.PositionFromPartition(2));
part.Check();
}
SECTION("InverseSearch") {
part.InsertText(0, 3);
part.InsertPartition(1, 2);
part.SetPartitionStartPosition(1,1);
REQUIRE(2 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(0));
REQUIRE(1 == part.PositionFromPartition(1));
REQUIRE(3 == part.PositionFromPartition(2));
REQUIRE(0 == part.PartitionFromPosition(0));
REQUIRE(1 == part.PartitionFromPosition(1));
REQUIRE(1 == part.PartitionFromPosition(2));
REQUIRE(1 == part.PartitionFromPosition(3));
part.Check();
}
SECTION("DeletePartition") {
part.InsertText(0, 2);
part.InsertPartition(1, 1);
part.RemovePartition(1);
REQUIRE(1 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(0));
REQUIRE(2 == part.PositionFromPartition(1));
part.Check();
}
SECTION("DeleteAll") {
part.InsertText(0, 3);
part.InsertPartition(1, 2);
part.SetPartitionStartPosition(1,1);
part.DeleteAll();
// Back to initial state
REQUIRE(1 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(part.Partitions()));
}
SECTION("TestBackwards") {
part.InsertText(0, 10);
part.InsertPartition(1, 3);
part.InsertPartition(2, 6);
part.InsertPartition(3, 9);
part.InsertText(2,4);
part.InsertText(1,2);
part.InsertText(0,3);
REQUIRE(4 == part.Partitions());
REQUIRE(0 == part.PositionFromPartition(0));
REQUIRE(6 == part.PositionFromPartition(1));
REQUIRE(11 == part.PositionFromPartition(2));
REQUIRE(18 == part.PositionFromPartition(3));
REQUIRE(19 == part.PositionFromPartition(4));
part.Check();
}
SECTION("TestMany") {
// Provoke backstep call
part.InsertText(0, 42);
for (int i=0; i<20; i++) {
part.InsertPartition(i+1, (i+1) * 2);
}
for (int i=20; i>0; i--) {
part.InsertText(i,2);
}
REQUIRE(21 == part.Partitions());
for (int i=1; i<20; i++) {
REQUIRE((i*4 - 2) == part.PositionFromPartition(i));
REQUIRE(i == part.PartitionFromPosition(i*4 - 2));
}
part.InsertText(19,2);
REQUIRE(3 == part.PartitionFromPosition(10));
part.InsertText(0,2);
part.InsertText(0,-2);
part.RemovePartition(1);
REQUIRE(0 == part.PositionFromPartition(0));
REQUIRE(6 == part.PositionFromPartition(1));
REQUIRE(10 == part.PositionFromPartition(2));
part.RemovePartition(10);
REQUIRE(46 == part.PositionFromPartition(10));
REQUIRE(10 == part.PartitionFromPosition(46));
REQUIRE(50 == part.PositionFromPartition(11));
REQUIRE(11 == part.PartitionFromPosition(50));
part.Check();
}
}

View File

@ -0,0 +1,393 @@
/** @file testPerLine.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstddef>
#include <cstring>
#include <stdexcept>
#include <string_view>
#include <vector>
#include <forward_list>
#include <optional>
#include <algorithm>
#include <memory>
#include "ScintillaTypes.h"
#include "Debugging.h"
#include "Position.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "CellBuffer.h"
#include "PerLine.h"
#include "catch.hpp"
using namespace Scintilla::Internal;
constexpr int FoldBase = static_cast<int>(Scintilla::FoldLevel::Base);
// Test MarkerHandleSet.
TEST_CASE("CompileCopying MarkerHandleSet") {
// These are compile-time tests to check that basic copy and move
// operations are defined correctly.
SECTION("CopyingMoving") {
MarkerHandleSet s;
MarkerHandleSet s2;
// Copy constructor
const MarkerHandleSet sa(s);
// Copy assignment
MarkerHandleSet sb;
sb = s;
// Move constructor
const MarkerHandleSet sc(std::move(s));
// Move assignment
MarkerHandleSet sd;
sd = (std::move(s2));
}
}
TEST_CASE("MarkerHandleSet") {
MarkerHandleSet mhs;
SECTION("Initial") {
// Initial State
REQUIRE(mhs.Empty());
REQUIRE(0 == mhs.MarkValue());
REQUIRE(!mhs.Contains(1));
}
SECTION("InsertDelete") {
// Test knows that InsertHandle inserts at front (0)
// Insert 1 with handle 100
REQUIRE(mhs.InsertHandle(100,1));
REQUIRE(!mhs.Empty());
REQUIRE(2 == mhs.MarkValue());
REQUIRE(mhs.Contains(100));
// Insert 2 with handle 200
REQUIRE(mhs.InsertHandle(200,2));
REQUIRE(!mhs.Empty());
REQUIRE(mhs.Contains(100));
REQUIRE(mhs.Contains(200));
REQUIRE(6 == mhs.MarkValue());
const MarkerHandleNumber *mhn0 = mhs.GetMarkerHandleNumber(0);
REQUIRE(200 == mhn0->handle);
REQUIRE(2 == mhn0->number);
const MarkerHandleNumber *mhn1 = mhs.GetMarkerHandleNumber(1);
REQUIRE(100 == mhn1->handle);
REQUIRE(1 == mhn1->number);
const MarkerHandleNumber *mhn2 = mhs.GetMarkerHandleNumber(2);
REQUIRE(nullptr == mhn2);
// Remove first insertion
mhs.RemoveHandle(100);
REQUIRE(!mhs.Empty());
REQUIRE(mhs.Contains(200));
REQUIRE(4 == mhs.MarkValue());
// Remove remaining element
REQUIRE(mhs.RemoveNumber(2, true));
REQUIRE(mhs.Empty());
REQUIRE(!mhs.Contains(200));
REQUIRE(0 == mhs.MarkValue());
}
SECTION("Combine") {
mhs.InsertHandle(100, 1);
MarkerHandleSet mhsOther;
mhsOther.InsertHandle(200, 2);
mhs.CombineWith(&mhsOther);
REQUIRE(mhsOther.Empty());
mhs.RemoveHandle(100);
mhs.RemoveHandle(200);
REQUIRE(mhs.Empty());
}
}
TEST_CASE("LineMarkers") {
LineMarkers lm;
SECTION("Initial") {
// Initial State
REQUIRE(0 == lm.MarkValue(0));
}
SECTION("AddDelete") {
// Test knows the way handles are allocated, starting from 1
lm.InsertLines(0, 5);
const int handle1 = lm.AddMark(0, 1, 5);
REQUIRE(1 == handle1);
REQUIRE(2 == lm.MarkValue(0));
REQUIRE(1 == lm.HandleFromLine(0, 0));
REQUIRE(1 == lm.NumberFromLine(0, 0));
REQUIRE(-1 == lm.HandleFromLine(0, 1));
REQUIRE(-1 == lm.NumberFromLine(0, 1));
REQUIRE(0 == lm.LineFromHandle(handle1));
REQUIRE(lm.DeleteMark(0, 1, true));
REQUIRE(0 == lm.MarkValue(0));
const int handle2 = lm.AddMark(0, 2, 5);
REQUIRE(2 == handle2);
REQUIRE(4 == lm.MarkValue(0));
lm.DeleteMarkFromHandle(handle2);
REQUIRE(0 == lm.MarkValue(0));
}
SECTION("MarkerNext") {
lm.AddMark(1, 1, 5);
lm.AddMark(2, 2, 5);
const Sci::Line line1 = lm.MarkerNext(0, 6);
REQUIRE(1 == line1);
const Sci::Line line2 = lm.MarkerNext(line1+1, 6);
REQUIRE(2 == line2);
const Sci::Line line3 = lm.MarkerNext(line2+1, 6);
REQUIRE(-1 == line3);
}
SECTION("MergeMarkers") {
lm.AddMark(1, 1, 5);
lm.AddMark(2, 2, 5);
lm.MergeMarkers(1);
REQUIRE(6 == lm.MarkValue(1));
REQUIRE(0 == lm.MarkValue(2));
}
SECTION("InsertRemoveLine") {
const int handle1 = lm.AddMark(1, 1, 5);
const int handle2 = lm.AddMark(2, 2, 5);
lm.InsertLine(2);
REQUIRE(0 == lm.MarkValue(0));
REQUIRE(2 == lm.MarkValue(1));
REQUIRE(0 == lm.MarkValue(2));
REQUIRE(4 == lm.MarkValue(3));
REQUIRE(0 == lm.MarkValue(4));
lm.RemoveLine(2);
REQUIRE(0 == lm.MarkValue(0));
REQUIRE(2 == lm.MarkValue(1));
REQUIRE(4 == lm.MarkValue(2));
REQUIRE(0 == lm.MarkValue(3));
lm.InsertLines(2, 2);
REQUIRE(0 == lm.MarkValue(0));
REQUIRE(2 == lm.MarkValue(1));
REQUIRE(0 == lm.MarkValue(2));
REQUIRE(0 == lm.MarkValue(3));
REQUIRE(4 == lm.MarkValue(4));
REQUIRE(0 == lm.MarkValue(5));
REQUIRE(1 == lm.LineFromHandle(handle1));
REQUIRE(4 == lm.LineFromHandle(handle2));
}
}
TEST_CASE("LineLevels") {
LineLevels ll;
SECTION("Initial") {
// Initial State
REQUIRE(FoldBase == ll.GetLevel(0));
}
SECTION("SetLevel") {
REQUIRE(FoldBase == ll.SetLevel(1, 200, 5));
REQUIRE(FoldBase == ll.GetLevel(0));
REQUIRE(200 == ll.GetLevel(1));
REQUIRE(FoldBase == ll.GetLevel(2));
ll.ClearLevels();
REQUIRE(FoldBase == ll.GetLevel(1));
ll.ExpandLevels(5);
REQUIRE(FoldBase == ll.GetLevel(7));
}
SECTION("InsertRemoveLine") {
ll.SetLevel(1, 1, 5);
ll.SetLevel(2, 2, 5);
ll.InsertLine(2);
REQUIRE(FoldBase == ll.GetLevel(0));
REQUIRE(1 == ll.GetLevel(1));
REQUIRE(2 == ll.GetLevel(2));
REQUIRE(2 == ll.GetLevel(3));
REQUIRE(FoldBase == ll.GetLevel(4));
ll.RemoveLine(2);
REQUIRE(FoldBase == ll.GetLevel(0));
REQUIRE(1 == ll.GetLevel(1));
REQUIRE(2 == ll.GetLevel(2));
REQUIRE(FoldBase == ll.GetLevel(3));
ll.InsertLines(2, 2);
REQUIRE(FoldBase == ll.GetLevel(0));
REQUIRE(1 == ll.GetLevel(1));
REQUIRE(2 == ll.GetLevel(2));
REQUIRE(2 == ll.GetLevel(3));
REQUIRE(2 == ll.GetLevel(4));
REQUIRE(FoldBase == ll.GetLevel(5));
}
}
TEST_CASE("LineState") {
LineState ls;
SECTION("Initial") {
// Initial State
REQUIRE(0 == ls.GetMaxLineState());
REQUIRE(0 == ls.GetLineState(0));
REQUIRE(1 == ls.GetMaxLineState());
ls.Init();
REQUIRE(0 == ls.GetMaxLineState());
REQUIRE(0 == ls.GetLineState(0));
}
SECTION("SetLineState") {
REQUIRE(0 == ls.SetLineState(1, 200, 2));
REQUIRE(0 == ls.GetLineState(0));
REQUIRE(200 == ls.GetLineState(1));
REQUIRE(0 == ls.GetLineState(2));
REQUIRE(0 == ls.SetLineState(2, 400, 3));
REQUIRE(0 == ls.GetLineState(0));
REQUIRE(200 == ls.GetLineState(1));
REQUIRE(400 == ls.GetLineState(2));
REQUIRE(0 == ls.GetLineState(3));
// GetLineState(3) expands to 4 lines
REQUIRE(4 == ls.GetMaxLineState());
ls.Init();
REQUIRE(0 == ls.GetLineState(0));
REQUIRE(1 == ls.GetMaxLineState());
}
SECTION("InsertRemoveLine") {
REQUIRE(0 == ls.GetMaxLineState());
ls.SetLineState(1, 1, 3);
ls.SetLineState(2, 2, 3);
REQUIRE(4 == ls.GetMaxLineState());
ls.InsertLine(2);
REQUIRE(5 == ls.GetMaxLineState());
REQUIRE(0 == ls.GetLineState(0));
REQUIRE(1 == ls.GetLineState(1));
REQUIRE(2 == ls.GetLineState(2));
REQUIRE(2 == ls.GetLineState(3));
REQUIRE(0 == ls.GetLineState(4));
REQUIRE(5 == ls.GetMaxLineState());
ls.RemoveLine(2);
REQUIRE(4 == ls.GetMaxLineState());
REQUIRE(0 == ls.GetLineState(0));
REQUIRE(1 == ls.GetLineState(1));
REQUIRE(2 == ls.GetLineState(2));
REQUIRE(0 == ls.GetLineState(3));
ls.InsertLines(2, 2);
REQUIRE(6 == ls.GetMaxLineState());
REQUIRE(0 == ls.GetLineState(0));
REQUIRE(1 == ls.GetLineState(1));
REQUIRE(2 == ls.GetLineState(2));
REQUIRE(2 == ls.GetLineState(3));
REQUIRE(2 == ls.GetLineState(4));
REQUIRE(0 == ls.GetLineState(5));
}
}
TEST_CASE("LineAnnotation") {
LineAnnotation la;
SECTION("Initial") {
// Initial State
REQUIRE(0 == la.Length(0));
REQUIRE(0 == la.Lines(0));
REQUIRE(0 == la.Style(0));
REQUIRE(false == la.MultipleStyles(0));
}
SECTION("SetText") {
la.SetText(0, "Text");
REQUIRE(4 == la.Length(0));
REQUIRE(1 == la.Lines(0));
REQUIRE(memcmp(la.Text(0), "Text", 4) == 0);
REQUIRE(nullptr == la.Styles(0));
REQUIRE(0 == la.Style(0));
la.SetStyle(0, 9);
REQUIRE(9 == la.Style(0));
la.SetText(0, "Ant\nBird\nCat");
REQUIRE(3 == la.Lines(0));
la.ClearAll();
REQUIRE(nullptr == la.Text(0));
}
SECTION("SetStyles") {
la.SetText(0, "Text");
const unsigned char styles[] { 1,2,3,4 };
la.SetStyles(0, styles);
REQUIRE(memcmp(la.Text(0), "Text", 4) == 0);
REQUIRE(memcmp(la.Styles(0), styles, 4) == 0);
REQUIRE(la.MultipleStyles(0));
}
SECTION("InsertRemoveLine") {
la.SetText(0, "Ant");
la.SetText(1, "Bird");
REQUIRE(3 == la.Length(0));
REQUIRE(4 == la.Length(1));
REQUIRE(1 == la.Lines(0));
la.InsertLine(1);
REQUIRE(3 == la.Length(0));
REQUIRE(0 == la.Length(1));
REQUIRE(4 == la.Length(2));
la.RemoveLine(2);
REQUIRE(3 == la.Length(0));
REQUIRE(4 == la.Length(1));
la.InsertLines(1, 2);
REQUIRE(3 == la.Length(0));
REQUIRE(0 == la.Length(1));
REQUIRE(0 == la.Length(2));
REQUIRE(4 == la.Length(3));
}
}
TEST_CASE("LineTabstops") {
LineTabstops lt;
SECTION("Initial") {
// Initial State
REQUIRE(0 == lt.GetNextTabstop(0, 0));
}
SECTION("AddClearTabstops") {
lt.AddTabstop(0, 100);
REQUIRE(100 == lt.GetNextTabstop(0, 0));
REQUIRE(0 == lt.GetNextTabstop(0, 100));
lt.ClearTabstops(0);
REQUIRE(0 == lt.GetNextTabstop(0, 0));
}
SECTION("InsertRemoveLine") {
lt.AddTabstop(0, 100);
lt.AddTabstop(1, 200);
lt.InsertLine(1);
REQUIRE(100 == lt.GetNextTabstop(0, 0));
REQUIRE(0 == lt.GetNextTabstop(1, 0));
REQUIRE(200 == lt.GetNextTabstop(2, 0));
lt.RemoveLine(1);
REQUIRE(100 == lt.GetNextTabstop(0, 0));
REQUIRE(200 == lt.GetNextTabstop(1, 0));
lt.InsertLines(1, 2);
REQUIRE(100 == lt.GetNextTabstop(0, 0));
REQUIRE(0 == lt.GetNextTabstop(1, 0));
REQUIRE(0 == lt.GetNextTabstop(2, 0));
REQUIRE(200 == lt.GetNextTabstop(3, 0));
lt.Init();
REQUIRE(0 == lt.GetNextTabstop(0, 0));
}
}

View File

@ -0,0 +1,97 @@
/** @file testRESearch.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstddef>
#include <cstring>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
#include <array>
#include <optional>
#include <algorithm>
#include <memory>
#include "ScintillaTypes.h"
#include "Debugging.h"
#include "Position.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "CellBuffer.h"
#include "CharClassify.h"
#include "RESearch.h"
#include "catch.hpp"
using namespace Scintilla;
using namespace Scintilla::Internal;
class StringCI : public CharacterIndexer {
std::string s;
public:
explicit StringCI(std::string_view sv_) : s(sv_) {
}
virtual ~StringCI() = default;
[[nodiscard]] Sci::Position Length() const noexcept {
return s.length();
}
char CharAt(Sci::Position index) const override {
return s.at(index);
}
Sci::Position MovePositionOutsideChar(Sci::Position pos, [[maybe_unused]] Sci::Position moveDir) const noexcept override {
return pos;
}
[[nodiscard]] std::string GetCharRange(Sci::Position position, Sci::Position lengthRetrieve) const {
return s.substr(position, lengthRetrieve);
}
};
// Test RESearch.
TEST_CASE("RESearch") {
CharClassify cc;
constexpr std::string_view sTextSpace = "Scintilla ";
constexpr std::string_view pattern = "[a-z]+";
SECTION("Compile") {
RESearch re(&cc);
const char *msg = re.Compile(pattern.data(), pattern.length(), true, false);
REQUIRE(nullptr == msg);
}
SECTION("Bug2413") {
// Check for https://sourceforge.net/p/scintilla/bugs/2413/
RESearch re(&cc);
constexpr std::string_view BOW = "\\<";
constexpr std::string_view EOW = "\\>";
const char *msg = re.Compile(BOW.data(), BOW.length(), true, false);
REQUIRE(nullptr == msg);
msg = re.Compile(EOW.data(), EOW.length(), true, false);
REQUIRE(nullptr == msg);
}
SECTION("Execute") {
RESearch re(&cc);
re.Compile(pattern.data(), pattern.length(), true, false);
const StringCI sci(sTextSpace);
const int x = re.Execute(sci, 0, sci.Length());
REQUIRE(x == 1);
REQUIRE(re.bopat[0] == 1);
REQUIRE(re.eopat[0] == sci.Length() - 1);
}
SECTION("Grab") {
RESearch re(&cc);
re.Compile(pattern.data(), pattern.length(), true, false);
const StringCI sci(sTextSpace);
re.Execute(sci, 0, sci.Length());
const std::string pat = sci.GetCharRange(re.bopat[0], re.eopat[0] - re.bopat[0]);
REQUIRE(pat == "cintilla");
}
}

View File

@ -0,0 +1,381 @@
/** @file testRunStyles.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstddef>
#include <cstring>
#include <stdexcept>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
#include "Debugging.h"
#include "Position.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "catch.hpp"
using namespace Scintilla::Internal;
// Test RunStyles.
using UniqueInt = std::unique_ptr<int>;
TEST_CASE("CompileCopying RunStyles") {
// These are compile-time tests to check that basic copy and move
// operations are defined correctly.
SECTION("CopyingMoving") {
RunStyles<int, int> s;
RunStyles<int, int> s2;
// Copy constructor
const RunStyles<int, int> sa(s);
// Copy assignment fails
RunStyles<int, int> sb;
sb = s;
// Move constructor
const RunStyles<int, int> sc(std::move(s));
// Move assignment
RunStyles<int, int> sd;
sd = (std::move(s2));
}
#if defined(SHOW_COPY_BUILD_FAILURES)
// It should be reasonable to instantiate RunStyles where STYLE is move-only but fails
SECTION("MoveOnly") {
RunStyles<int, UniqueInt> s;
// Copy is not defined for std::unique_ptr
// Copy constructor fails
RunStyles<int, UniqueInt> sa(s);
// Copy assignment fails
RunStyles<int, UniqueInt> sb;
sb = s;
// Move constructor fails
RunStyles<int, UniqueInt> sc(std::move(s));
// Move assignment fails
RunStyles<int, UniqueInt> sd;
sd = (std::move(s));
}
#endif
}
namespace Scintilla::Internal { // Xcode clang 9.0 doesn't like this when in the unnamed namespace
bool operator==(const FillResult<int> &fra, const FillResult<int> &frb) noexcept {
return fra.changed == frb.changed &&
fra.position == frb.position &&
fra.fillLength == frb.fillLength;
}
}
TEST_CASE("RunStyles") {
RunStyles<int, int> rs;
SECTION("IsEmptyInitially") {
REQUIRE(0 == rs.Length());
REQUIRE(1 == rs.Runs());
}
SECTION("SimpleInsert") {
rs.InsertSpace(0, 1);
REQUIRE(1 == rs.Length());
REQUIRE(1 == rs.Runs());
REQUIRE(0 == rs.ValueAt(0));
REQUIRE(1 == rs.FindNextChange(0, rs.Length()));
REQUIRE(2 == rs.FindNextChange(1, rs.Length()));
}
SECTION("TwoRuns") {
rs.InsertSpace(0, 2);
REQUIRE(2 == rs.Length());
REQUIRE(1 == rs.Runs());
rs.SetValueAt(0, 2);
REQUIRE(2 == rs.Runs());
REQUIRE(2 == rs.ValueAt(0));
REQUIRE(0 == rs.ValueAt(1));
REQUIRE(1 == rs.FindNextChange(0, rs.Length()));
REQUIRE(2 == rs.FindNextChange(1, rs.Length()));
REQUIRE(3 == rs.FindNextChange(2, rs.Length()));
}
SECTION("LongerRuns") {
rs.InsertSpace(0, 5);
rs.SetValueAt(0, 3);
rs.SetValueAt(1, 3);
REQUIRE(3 == rs.ValueAt(0));
REQUIRE(3 == rs.ValueAt(1));
REQUIRE(0 == rs.ValueAt(2));
REQUIRE(2 == rs.Runs());
REQUIRE(0 == rs.StartRun(0));
REQUIRE(2 == rs.EndRun(0));
REQUIRE(0 == rs.StartRun(1));
REQUIRE(2 == rs.EndRun(1));
REQUIRE(2 == rs.StartRun(2));
REQUIRE(5 == rs.EndRun(2));
REQUIRE(2 == rs.StartRun(3));
REQUIRE(5 == rs.EndRun(3));
REQUIRE(2 == rs.StartRun(4));
REQUIRE(5 == rs.EndRun(4));
// At end
REQUIRE(2 == rs.StartRun(5));
REQUIRE(5 == rs.EndRun(5));
// After end is same as end
REQUIRE(2 == rs.StartRun(6));
REQUIRE(5 == rs.EndRun(6));
REQUIRE(2 == rs.FindNextChange(0, rs.Length()));
REQUIRE(5 == rs.FindNextChange(2, rs.Length()));
REQUIRE(6 == rs.FindNextChange(5, rs.Length()));
}
SECTION("FillRange") {
rs.InsertSpace(0, 5);
const int startFill = 1;
const int lengthFill = 3;
const auto fr = rs.FillRange(startFill, 99, lengthFill);
REQUIRE(FillResult<int>{true, 1, 3} == fr);
REQUIRE(0 == rs.ValueAt(0));
REQUIRE(99 == rs.ValueAt(1));
REQUIRE(99 == rs.ValueAt(2));
REQUIRE(99 == rs.ValueAt(3));
REQUIRE(0 == rs.ValueAt(4));
REQUIRE(0 == rs.StartRun(0));
REQUIRE(1 == rs.EndRun(0));
REQUIRE(1 == rs.StartRun(1));
REQUIRE(4 == rs.EndRun(1));
}
SECTION("FillRangeAlreadyFilled") {
rs.InsertSpace(0, 5);
const int startFill = 1;
const int lengthFill = 3;
const auto fr = rs.FillRange(startFill, 99, lengthFill);
REQUIRE(FillResult<int>{true, 1, 3} == fr);
const int startFill2 = 2;
const int lengthFill2 = 1;
// Compiler warnings if 'false' used instead of '0' as expected value:
const auto fr2 = rs.FillRange(startFill2, 99, lengthFill2);
REQUIRE(FillResult<int>{false, 2, 1} == fr2);
REQUIRE(0 == rs.ValueAt(0));
REQUIRE(99 == rs.ValueAt(1));
REQUIRE(99 == rs.ValueAt(2));
REQUIRE(99 == rs.ValueAt(3));
REQUIRE(0 == rs.ValueAt(4));
REQUIRE(3 == rs.Runs());
}
SECTION("FillRangeAlreadyPartFilled") {
rs.InsertSpace(0, 5);
const int startFill = 1;
const int lengthFill = 2;
const auto fr = rs.FillRange(startFill, 99, lengthFill);
REQUIRE(FillResult<int>{true, 1, 2} == fr);
const int startFill2 = 2;
const int lengthFill2 = 2;
const auto fr2 = rs.FillRange(startFill2, 99, lengthFill2);
REQUIRE(FillResult<int>{true, 3, 1} == fr2);
REQUIRE(3 == rs.Runs());
}
SECTION("DeleteRange") {
rs.InsertSpace(0, 5);
rs.SetValueAt(0, 3);
REQUIRE(2 == rs.Runs());
rs.SetValueAt(1, 3);
REQUIRE(2 == rs.Runs());
rs.DeleteRange(1, 1);
REQUIRE(4 == rs.Length());
REQUIRE(2 == rs.Runs());
REQUIRE(3 == rs.ValueAt(0));
REQUIRE(0 == rs.ValueAt(1));
REQUIRE(0 == rs.StartRun(0));
REQUIRE(1 == rs.EndRun(0));
REQUIRE(1 == rs.StartRun(1));
REQUIRE(4 == rs.EndRun(1));
REQUIRE(1 == rs.StartRun(2));
REQUIRE(4 == rs.EndRun(2));
}
SECTION("Find") {
rs.InsertSpace(0, 5);
const int startFill = 1;
const int lengthFill = 3;
const auto fr = rs.FillRange(startFill, 99, lengthFill);
REQUIRE(FillResult<int>{true, 1, 3} == fr);
REQUIRE(0 == rs.Find(0,0));
REQUIRE(1 == rs.Find(99,0));
REQUIRE(-1 == rs.Find(3,0));
REQUIRE(4 == rs.Find(0,1));
REQUIRE(1 == rs.Find(99,1));
REQUIRE(-1 == rs.Find(3,1));
REQUIRE(4 == rs.Find(0,2));
REQUIRE(2 == rs.Find(99,2));
REQUIRE(-1 == rs.Find(3, 2));
REQUIRE(4 == rs.Find(0,4));
REQUIRE(-1 == rs.Find(99,4));
REQUIRE(-1 == rs.Find(3,4));
REQUIRE(-1 == rs.Find(0,5));
REQUIRE(-1 == rs.Find(99,5));
REQUIRE(-1 == rs.Find(3,5));
REQUIRE(-1 == rs.Find(0,6));
REQUIRE(-1 == rs.Find(99,6));
REQUIRE(-1 == rs.Find(3,6));
}
SECTION("AllSame") {
REQUIRE(true == rs.AllSame());
rs.InsertSpace(0, 5);
REQUIRE(true == rs.AllSame());
REQUIRE(false == rs.AllSameAs(88));
REQUIRE(true == rs.AllSameAs(0));
const int startFill = 1;
const int lengthFill = 3;
const auto fr = rs.FillRange(startFill, 99, lengthFill);
REQUIRE(true == fr.changed);
REQUIRE(false == rs.AllSame());
REQUIRE(false == rs.AllSameAs(88));
REQUIRE(false == rs.AllSameAs(0));
const auto fr2 = rs.FillRange(startFill, 0, lengthFill);
REQUIRE(true == fr2.changed);
REQUIRE(true == rs.AllSame());
REQUIRE(false == rs.AllSameAs(88));
REQUIRE(true == rs.AllSameAs(0));
}
SECTION("FindWithReversion") {
rs.InsertSpace(0, 5);
REQUIRE(1 == rs.Runs());
int startFill = 1;
int lengthFill = 1;
const auto fr = rs.FillRange(startFill, 99, lengthFill);
REQUIRE(FillResult<int>{true, 1, 1} == fr);
REQUIRE(3 == rs.Runs());
startFill = 2;
lengthFill = 1;
const auto fr2 = rs.FillRange(startFill, 99, lengthFill);
REQUIRE(FillResult<int>{true, 2, 1} == fr2);
REQUIRE(3 == rs.Runs());
startFill = 1;
lengthFill = 1;
const auto fr3 = rs.FillRange(startFill, 0, lengthFill);
REQUIRE(FillResult<int>{true, 1, 1} == fr3);
REQUIRE(3 == rs.Runs());
startFill = 2;
lengthFill = 1;
const auto fr4 = rs.FillRange(startFill, 0, lengthFill);
REQUIRE(FillResult<int>{true, 2, 1} == fr4);
REQUIRE(1 == rs.Runs());
REQUIRE(-1 == rs.Find(0,6));
}
SECTION("FinalRunInversion") {
REQUIRE(1 == rs.Runs());
rs.InsertSpace(0, 1);
REQUIRE(1 == rs.Runs());
rs.SetValueAt(0, 1);
REQUIRE(1 == rs.Runs());
rs.InsertSpace(1, 1);
REQUIRE(1 == rs.Runs());
rs.SetValueAt(1, 1);
REQUIRE(1 == rs.Runs());
rs.SetValueAt(1, 0);
REQUIRE(2 == rs.Runs());
rs.SetValueAt(1, 1);
REQUIRE(1 == rs.Runs());
}
SECTION("DeleteAll") {
rs.InsertSpace(0, 5);
rs.SetValueAt(0, 3);
rs.SetValueAt(1, 3);
rs.DeleteAll();
REQUIRE(0 == rs.Length());
REQUIRE(0 == rs.ValueAt(0));
REQUIRE(1 == rs.Runs());
}
SECTION("DeleteSecond") {
rs.InsertSpace(0, 3);
const int startFill = 1;
const int lengthFill = 1;
const auto fr = rs.FillRange(startFill, 99, lengthFill);
REQUIRE(true == fr.changed);
REQUIRE(3 == rs.Length());
REQUIRE(3 == rs.Runs());
rs.DeleteRange(1, 1);
REQUIRE(2 == rs.Length());
REQUIRE(1 == rs.Runs());
}
SECTION("DeleteEndRun") {
rs.InsertSpace(0, 2);
const int startFill = 1;
const int lengthFill = 1;
const auto fr = rs.FillRange(startFill, 99, lengthFill);
REQUIRE(true == fr.changed);
REQUIRE(2 == rs.Length());
REQUIRE(2 == rs.Runs());
REQUIRE(0 == rs.StartRun(0));
REQUIRE(1 == rs.EndRun(0));
REQUIRE(1 == rs.StartRun(1));
REQUIRE(2 == rs.EndRun(1));
rs.DeleteRange(1, 1);
REQUIRE(1 == rs.Length());
REQUIRE(1 == rs.Runs());
REQUIRE(0 == rs.StartRun(0));
REQUIRE(1 == rs.EndRun(0));
REQUIRE(0 == rs.StartRun(1));
REQUIRE(1 == rs.EndRun(1));
rs.Check();
}
SECTION("OutsideBounds") {
rs.InsertSpace(0, 1);
const int startFill = 1;
const int lengthFill = 1;
rs.FillRange(startFill, 99, lengthFill);
REQUIRE(1 == rs.Length());
REQUIRE(1 == rs.Runs());
REQUIRE(0 == rs.StartRun(0));
REQUIRE(1 == rs.EndRun(0));
}
}

View File

@ -0,0 +1,493 @@
/** @file testSparseVector.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstddef>
#include <cassert>
#include <cstring>
#include <stdexcept>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
#include "Debugging.h"
#include "Position.h"
#include "UniqueString.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "SparseVector.h"
#include "catch.hpp"
using namespace Scintilla::Internal;
// Test SparseVector.
using UniqueInt = std::unique_ptr<int>;
TEST_CASE("CompileCopying SparseVector") {
// These are compile-time tests to check that basic copy and move
// operations are defined correctly.
SECTION("CopyingMoving") {
SparseVector<int> s;
SparseVector<int> s2;
// Copy constructor
const SparseVector<int> sa(s);
// Copy assignment
SparseVector<int> sb;
sb = s;
// Move constructor
const SparseVector<int> sc(std::move(s));
// Move assignment
SparseVector<int> sd;
sd = (std::move(s2));
}
SECTION("MoveOnly") {
SparseVector<UniqueInt> s;
#if defined(SHOW_COPY_BUILD_FAILURES)
// Copy is not defined for std::unique_ptr
// Copy constructor fails
SparseVector<UniqueInt> sa(s);
// Copy assignment fails
SparseVector<UniqueInt> sb;
sb = s;
#endif
// Move constructor
const SparseVector<UniqueInt> sc(std::move(s));
// Move assignment
SparseVector<UniqueInt> s2;
SparseVector<UniqueInt> sd;
sd = (std::move(s2));
}
}
namespace {
// Helper to produce a string representation of a SparseVector<const char *>
// to simplify checks.
std::string Representation(const SparseVector<UniqueString> &st) {
std::string ret;
for (int i = 0;i <= st.Length();i++) {
const char *value = st.ValueAt(i).get();
if (value && *value)
ret += value;
else
ret += "-";
}
return ret;
}
}
TEST_CASE("SparseVector") {
SparseVector<UniqueString> st;
SECTION("IsEmptyInitially") {
REQUIRE(1 == st.Elements());
REQUIRE(0 == st.Length());
REQUIRE("-" == Representation(st));
st.Check();
}
SECTION("InsertSpace") {
st.InsertSpace(0, 5);
REQUIRE(1 == st.Elements());
REQUIRE(static_cast<const char *>(nullptr) == st.ValueAt(0).get());
REQUIRE(static_cast<const char *>(nullptr) == st.ValueAt(1).get());
REQUIRE(static_cast<const char *>(nullptr) == st.ValueAt(4).get());
st.Check();
}
SECTION("InsertValue") {
st.InsertSpace(0, 5);
st.SetValueAt(3, UniqueStringCopy("3"));
REQUIRE(2 == st.Elements());
REQUIRE("---3--" == Representation(st));
st.Check();
}
SECTION("InsertAndChangeAndDeleteValue") {
st.InsertSpace(0, 5);
REQUIRE(5 == st.Length());
st.SetValueAt(3, UniqueStringCopy("3"));
REQUIRE(2 == st.Elements());
st.SetValueAt(3, UniqueStringCopy("4"));
REQUIRE(2 == st.Elements());
st.DeletePosition(3);
REQUIRE(1 == st.Elements());
REQUIRE(4 == st.Length());
REQUIRE("-----" == Representation(st));
st.Check();
}
SECTION("InsertAndDeleteAtStart") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 5);
st.SetValueAt(0, UniqueStringCopy("3"));
REQUIRE(1 == st.Elements());
REQUIRE("3-----" == Representation(st));
st.DeletePosition(0);
REQUIRE(1 == st.Elements());
REQUIRE("-----" == Representation(st));
st.SetValueAt(0, UniqueStringCopy("4"));
REQUIRE(1 == st.Elements());
REQUIRE("4----" == Representation(st));
st.DeletePosition(0);
REQUIRE(1 == st.Elements());
REQUIRE("----" == Representation(st));
st.SetValueAt(0, UniqueStringCopy("4"));
REQUIRE(1 == st.Elements());
REQUIRE("4---" == Representation(st));
st.DeletePosition(0);
REQUIRE(1 == st.Elements());
REQUIRE("---" == Representation(st));
st.Check();
}
SECTION("InsertStringAtStartThenInsertSpaceAtStart") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 5);
st.SetValueAt(0, UniqueStringCopy("3"));
REQUIRE(1 == st.Elements());
REQUIRE("3-----" == Representation(st));
st.InsertSpace(0, 1);
REQUIRE(2 == st.Elements());
REQUIRE("-3-----" == Representation(st));
st.Check();
}
SECTION("InsertSpaceAfterStart") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 5);
st.SetValueAt(1, UniqueStringCopy("1"));
REQUIRE(2 == st.Elements());
REQUIRE("-1----" == Representation(st));
st.InsertSpace(1, 1);
REQUIRE(2 == st.Elements());
REQUIRE("--1----" == Representation(st));
st.Check();
}
SECTION("InsertStringAt1ThenInsertLettersAt1") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 5);
st.SetValueAt(1, UniqueStringCopy("9"));
REQUIRE(2 == st.Elements());
REQUIRE("-9----" == Representation(st));
st.InsertSpace(0, 1);
REQUIRE(2 == st.Elements());
REQUIRE("--9----" == Representation(st));
// Initial st has allocation of 9 values so this should cause reallocation
const std::string letters("ABCDEFGHIJKLMNOP"); // 16 letters
for (const char letter : letters) {
const char sLetter[] = { letter, 0 };
st.InsertSpace(0, 1);
st.SetValueAt(1, UniqueStringCopy(sLetter));
}
REQUIRE("-PONMLKJIHGFEDCBA-9----" == Representation(st));
st.Check();
}
SECTION("InsertAndDeleteAtEnd") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 5);
st.SetValueAt(4, UniqueStringCopy("5"));
REQUIRE(2 == st.Elements());
REQUIRE("----5-" == Representation(st));
st.SetValueAt(5, UniqueStringCopy("6"));
REQUIRE(2 == st.Elements());
REQUIRE("----56" == Representation(st));
st.DeletePosition(4);
REQUIRE(1 == st.Elements());
REQUIRE("----6" == Representation(st));
st.SetValueAt(4, UniqueStringCopy("7"));
REQUIRE(1 == st.Elements());
REQUIRE("----7" == Representation(st));
st.Check();
}
SECTION("SetNULL") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 5);
st.SetValueAt(4, UniqueStringCopy("5"));
REQUIRE(2 == st.Elements());
REQUIRE("----5-" == Representation(st));
st.SetValueAt(4, nullptr);
REQUIRE(1 == st.Elements());
REQUIRE("------" == Representation(st));
st.Check();
st.SetValueAt(5, nullptr);
REQUIRE(1 == st.Elements());
REQUIRE("------" == Representation(st));
st.Check();
}
SECTION("CheckDeletionLeavesOrdered") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 1);
st.SetValueAt(0, UniqueStringCopy("1"));
REQUIRE("1-" == Representation(st));
REQUIRE(1 == st.Elements());
st.InsertSpace(1, 1);
st.SetValueAt(1, UniqueStringCopy("2"));
REQUIRE("12-" == Representation(st));
st.DeletePosition(0);
REQUIRE("2-" == Representation(st));
REQUIRE(1 == st.Elements());
st.DeletePosition(0);
REQUIRE("-" == Representation(st));
}
SECTION("DeleteAll") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 10);
st.SetValueAt(9, UniqueStringCopy("9"));
st.SetValueAt(7, UniqueStringCopy("7"));
st.SetValueAt(4, UniqueStringCopy("4"));
st.SetValueAt(3, UniqueStringCopy("3"));
REQUIRE(5 == st.Elements());
REQUIRE("---34--7-9-" == Representation(st));
st.DeleteAll();
REQUIRE(1 == st.Elements());
REQUIRE("-" == Representation(st));
st.Check();
}
SECTION("DeleteStarting") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 2);
st.SetValueAt(0, UniqueStringCopy("1"));
st.SetValueAt(1, UniqueStringCopy("2"));
REQUIRE("12-" == Representation(st));
st.DeletePosition(0);
REQUIRE("2-" == Representation(st));
st.DeletePosition(0);
REQUIRE("-" == Representation(st));
}
SECTION("DeleteRange") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 10);
st.SetValueAt(9, UniqueStringCopy("9"));
st.SetValueAt(7, UniqueStringCopy("7"));
st.SetValueAt(4, UniqueStringCopy("4"));
st.SetValueAt(3, UniqueStringCopy("3"));
REQUIRE(5 == st.Elements());
REQUIRE(10 == st.Length());
REQUIRE("---34--7-9-" == Representation(st));
// Delete in space
st.DeleteRange(1, 1);
REQUIRE(5 == st.Elements());
REQUIRE(9 == st.Length());
REQUIRE("--34--7-9-" == Representation(st));
// Delete 2 values
st.DeleteRange(3, 4);
REQUIRE(3 == st.Elements());
REQUIRE(5 == st.Length());
REQUIRE("--3-9-" == Representation(st));
// Deletion at start
st.DeleteRange(0, 1);
REQUIRE(3 == st.Elements());
REQUIRE(4 == st.Length());
REQUIRE("-3-9-" == Representation(st));
}
SECTION("DeleteRangeAtEnds") {
// There are always elements at start and end although they can be nulled
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 4);
REQUIRE(4 == st.Length());
st.SetValueAt(1, UniqueStringCopy("3"));
st.SetValueAt(4, UniqueStringCopy("9"));
REQUIRE("-3--9" == Representation(st));
REQUIRE(2 == st.Elements());
// Empty deletion at end -> no effect
st.DeleteRange(4, 0);
REQUIRE(2 == st.Elements());
REQUIRE(4 == st.Length());
REQUIRE("-3--9" == Representation(st));
// Delete value at start
st.InsertSpace(0, 1);
st.SetValueAt(0, UniqueStringCopy("0"));
REQUIRE(2 == st.Elements());
REQUIRE(5 == st.Length());
REQUIRE("0-3--9" == Representation(st));
st.DeleteRange(0, 1);
REQUIRE(2 == st.Elements());
REQUIRE(4 == st.Length());
REQUIRE("03--9" == Representation(st));
// Empty deletion at start -> no effect
st.InsertSpace(0, 1);
st.SetValueAt(0, UniqueStringCopy("1"));
REQUIRE(3 == st.Elements());
REQUIRE(5 == st.Length());
REQUIRE("103--9" == Representation(st));
st.DeleteRange(0, 0);
REQUIRE(3 == st.Elements());
REQUIRE(5 == st.Length());
REQUIRE("103--9" == Representation(st));
}
SECTION("DeleteStartingRange") {
REQUIRE(1 == st.Elements());
st.InsertSpace(0, 2);
st.SetValueAt(0, UniqueStringCopy("1"));
st.SetValueAt(1, UniqueStringCopy("2"));
REQUIRE(2 == st.Length());
REQUIRE("12-" == Representation(st));
st.DeleteRange(0,1);
REQUIRE(1 == st.Length());
REQUIRE("2-" == Representation(st));
st.DeleteRange(0,1);
REQUIRE(0 == st.Length());
REQUIRE("-" == Representation(st));
st.InsertSpace(0, 2);
st.SetValueAt(1, UniqueStringCopy("1"));
REQUIRE(2 == st.Length());
REQUIRE("-1-" == Representation(st));
st.DeleteRange(0, 2);
REQUIRE("-" == Representation(st));
st.InsertSpace(0, 4);
st.SetValueAt(1, UniqueStringCopy("1"));
st.SetValueAt(3, UniqueStringCopy("3"));
REQUIRE(4 == st.Length());
REQUIRE("-1-3-" == Representation(st));
st.DeleteRange(0, 3);
REQUIRE("3-" == Representation(st));
st.DeleteRange(0, 1);
REQUIRE("-" == Representation(st));
st.InsertSpace(0, 4);
st.SetValueAt(1, UniqueStringCopy("1"));
st.SetValueAt(4, UniqueStringCopy("4"));
st.SetValueAt(3, UniqueStringCopy("3"));
REQUIRE("-1-34" == Representation(st));
st.DeleteRange(1, 3);
REQUIRE("-4" == Representation(st));
st.InsertSpace(1, 3);
REQUIRE("----4" == Representation(st));
st.SetValueAt(4, UniqueStringCopy("4"));
st.SetValueAt(3, UniqueStringCopy("3"));
REQUIRE("---34" == Representation(st));
st.DeleteRange(1, 3);
REQUIRE("-4" == Representation(st));
}
}
TEST_CASE("SparseTextInt") {
SparseVector<int> st;
SECTION("InsertAndDeleteValue") {
st.InsertSpace(0, 5);
st.SetValueAt(3, 3);
REQUIRE(2 == st.Elements());
REQUIRE(0 == st.ValueAt(0));
REQUIRE(0 == st.ValueAt(1));
REQUIRE(0 == st.ValueAt(2));
REQUIRE(3 == st.ValueAt(3));
REQUIRE(0 == st.ValueAt(4));
st.SetValueAt(3, -3);
REQUIRE(2 == st.Elements());
REQUIRE(0 == st.ValueAt(0));
REQUIRE(0 == st.ValueAt(1));
REQUIRE(0 == st.ValueAt(2));
REQUIRE(-3 == st.ValueAt(3));
REQUIRE(0 == st.ValueAt(4));
st.SetValueAt(3, 0);
REQUIRE(1 == st.Elements());
REQUIRE(0 == st.ValueAt(0));
REQUIRE(0 == st.ValueAt(1));
REQUIRE(0 == st.ValueAt(2));
REQUIRE(0 == st.ValueAt(3));
REQUIRE(0 == st.ValueAt(4));
st.Check();
}
SECTION("IndexAfter") {
st.InsertSpace(0, 5);
REQUIRE(1 == st.Elements());
REQUIRE(0 == st.IndexAfter(-1));
REQUIRE(0 == st.PositionOfElement(0));
REQUIRE(1 == st.IndexAfter(0));
REQUIRE(5 == st.PositionOfElement(1));
st.SetValueAt(3, 3);
REQUIRE(2 == st.Elements());
REQUIRE(0 == st.IndexAfter(-1));
REQUIRE(0 == st.PositionOfElement(0));
REQUIRE(1 == st.IndexAfter(0));
REQUIRE(3 == st.PositionOfElement(1));
REQUIRE(2 == st.IndexAfter(3));
REQUIRE(5 == st.PositionOfElement(2));
REQUIRE(2 == st.IndexAfter(4));
}
SECTION("PositionNext") {
st.InsertSpace(0, 5);
REQUIRE(1 == st.Elements());
REQUIRE(5 == st.PositionNext(-1));
REQUIRE(5 == st.PositionNext(0));
REQUIRE(6 == st.PositionNext(5));
st.SetValueAt(3, 3);
REQUIRE(2 == st.Elements());
REQUIRE(3 == st.PositionNext(-1));
REQUIRE(3 == st.PositionNext(0));
REQUIRE(5 == st.PositionNext(3));
REQUIRE(6 == st.PositionNext(5));
}
}
TEST_CASE("SparseTextString") {
SparseVector<std::string> st;
SECTION("InsertAndDeleteValue") {
st.InsertSpace(0, 5);
REQUIRE(5 == st.Length());
st.SetValueAt(3, std::string("3"));
REQUIRE(2 == st.Elements());
REQUIRE("" == st.ValueAt(0));
REQUIRE("" == st.ValueAt(2));
REQUIRE("3" == st.ValueAt(3));
REQUIRE("" == st.ValueAt(4));
st.DeletePosition(0);
REQUIRE(4 == st.Length());
REQUIRE("3" == st.ValueAt(2));
st.DeletePosition(2);
REQUIRE(1 == st.Elements());
REQUIRE(3 == st.Length());
REQUIRE("" == st.ValueAt(0));
REQUIRE("" == st.ValueAt(1));
REQUIRE("" == st.ValueAt(2));
st.Check();
}
SECTION("SetAndMoveString") {
st.InsertSpace(0, 2);
REQUIRE(2u == st.Length());
const std::string s24("24");
st.SetValueAt(0, s24);
REQUIRE("24" == s24); // Not moved from
REQUIRE("" == st.ValueAt(-1));
REQUIRE("24" == st.ValueAt(0));
REQUIRE("" == st.ValueAt(1));
std::string s25("25");
st.SetValueAt(1, std::move(s25));
// Deliberate check of moved from: provokes warning from Visual C++ code analysis
REQUIRE("" == s25);
REQUIRE("25" == st.ValueAt(1));
}
}

View File

@ -0,0 +1,405 @@
/** @file testSplitVector.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstddef>
#include <cstring>
#include <stdexcept>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
#include "Debugging.h"
#include "Position.h"
#include "SplitVector.h"
#include "catch.hpp"
using namespace Scintilla::Internal;
// Test SplitVector.
using UniqueInt = std::unique_ptr<int>;
// Test SplitVector.
TEST_CASE("CompileCopying SplitVector") {
// These are compile-time tests to check that basic copy and move
// operations are defined correctly.
SECTION("CopyingMoving") {
SplitVector<int> s;
SplitVector<int> s2;
// Copy constructor fails
const SplitVector<int> sa(s);
// Copy assignment fails
SplitVector<int> sb;
sb = s;
// Move constructor fails
const SplitVector<int> sc(std::move(s));
// Move assignment fails
SplitVector<int> sd;
sd = (std::move(s2));
}
SECTION("MoveOnly") {
SplitVector<UniqueInt> s;
#if defined(SHOW_COPY_BUILD_FAILURES)
// Copy is not defined for std::unique_ptr
// Copy constructor fails
SplitVector<UniqueInt> sa(s);
// Copy assignment fails
SplitVector<UniqueInt> sb;
sb = s;
#endif
// Move constructor fails
const SplitVector<UniqueInt> sc(std::move(s));
// Move assignment fails
SplitVector<UniqueInt> sd;
sd = (std::move(s));
}
}
struct StringSetHolder {
SplitVector<std::string> sa;
[[nodiscard]] bool Check() const noexcept {
for (int i = 0; i < sa.Length(); i++) {
if (sa[i].empty()) {
return false;
}
}
return true;
}
};
constexpr int lengthTestArray = 4;
static const int testArray[4] = {3, 4, 5, 6};
TEST_CASE("SplitVector") {
SplitVector<int> sv;
SECTION("IsEmptyInitially") {
REQUIRE(0 == sv.Length());
}
SECTION("InsertOne") {
sv.InsertValue(0, 10, 0);
sv.Insert(5, 3);
REQUIRE(11 == sv.Length());
for (int i=0; i<sv.Length(); i++) {
REQUIRE(((i == 5) ? 3 : 0) == sv.ValueAt(i));
}
}
SECTION("Insertion") {
sv.InsertValue(0, 10, 0);
REQUIRE(10 == sv.Length());
for (int i=0; i<sv.Length(); i++) {
REQUIRE(0 == sv.ValueAt(i));
}
}
SECTION("InsertionString") {
// This test failed an earlier version of SplitVector that copied backwards incorrectly
StringSetHolder ssh;
ssh.sa.Insert(0, "Alpha");
REQUIRE(ssh.Check());
ssh.sa.Insert(0, "Beta");
REQUIRE(ssh.Check());
ssh.sa.Insert(0, "Cat");
REQUIRE(ssh.Check());
ssh.sa.Insert(1, "Dog");
REQUIRE(ssh.Check());
ssh.sa.Insert(0, "Elephant");
REQUIRE(ssh.Check());
ssh.sa.Insert(1, "Fox");
REQUIRE(ssh.Check());
ssh.sa.Insert(0, "Grass");
REQUIRE(ssh.Check());
ssh.sa.Insert(1, "Hat");
REQUIRE(ssh.Check());
ssh.sa.Delete(4);
REQUIRE(ssh.Check());
ssh.sa.Insert(0, "Indigo");
REQUIRE(ssh.Check());
ssh.sa.Insert(1, "Jackal");
REQUIRE(ssh.Check());
ssh.sa.Insert(0, "Kanga");
REQUIRE(ssh.Check());
ssh.sa.Insert(1, "Lion");
REQUIRE(ssh.Check());
ssh.sa.Insert(0, "Mango");
REQUIRE(ssh.Check());
ssh.sa.Insert(1, "Neon");
REQUIRE(ssh.Check());
}
SECTION("InsertionPattern") {
sv.Insert(0, 1); // 1
sv.Insert(0, 2); // 21
sv.Insert(0, 3); // 321
sv.Insert(1, 4); // 3421
sv.Insert(0, 5); // 53421
sv.Insert(1, 6); // 563421
sv.Insert(0, 7); // 7563421
sv.Insert(1, 8); // 78563421
REQUIRE(8 == sv.Length());
REQUIRE(7 == sv.ValueAt(0));
REQUIRE(8 == sv.ValueAt(1));
REQUIRE(5 == sv.ValueAt(2));
REQUIRE(6 == sv.ValueAt(3));
REQUIRE(3 == sv.ValueAt(4));
REQUIRE(4 == sv.ValueAt(5));
REQUIRE(2 == sv.ValueAt(6));
REQUIRE(1 == sv.ValueAt(7));
sv.Delete(4); // 7856421
REQUIRE(7 == sv.Length());
REQUIRE(7 == sv.ValueAt(0));
REQUIRE(8 == sv.ValueAt(1));
REQUIRE(5 == sv.ValueAt(2));
REQUIRE(6 == sv.ValueAt(3));
REQUIRE(4 == sv.ValueAt(4));
REQUIRE(2 == sv.ValueAt(5));
REQUIRE(1 == sv.ValueAt(6));
sv.Insert(0, 9); // 97856421
sv.Insert(1, 0xa); // 9a7856421
sv.Insert(0, 0xb); // b9a7856421
sv.Insert(1, 0xc); // bc9a7856421
sv.Insert(0, 0xd); // dbc9a7856421
sv.Insert(1, 0xe); // debc9a7856421
REQUIRE(13 == sv.Length());
REQUIRE(0xd == sv.ValueAt(0));
REQUIRE(0xe == sv.ValueAt(1));
REQUIRE(0xb == sv.ValueAt(2));
REQUIRE(0xc == sv.ValueAt(3));
REQUIRE(9 == sv.ValueAt(4));
REQUIRE(0xa == sv.ValueAt(5));
REQUIRE(7 == sv.ValueAt(6));
REQUIRE(8 == sv.ValueAt(7));
REQUIRE(5 == sv.ValueAt(8));
REQUIRE(6 == sv.ValueAt(9));
REQUIRE(4 == sv.ValueAt(10));
REQUIRE(2 == sv.ValueAt(11));
REQUIRE(1 == sv.ValueAt(12));
}
SECTION("EnsureLength") {
sv.EnsureLength(4);
REQUIRE(4 == sv.Length());
for (int i=0; i<sv.Length(); i++) {
REQUIRE(0 == sv.ValueAt(i));
}
}
SECTION("InsertFromArray") {
sv.InsertFromArray(0, testArray, 0, lengthTestArray);
REQUIRE(lengthTestArray == sv.Length());
for (int i=0; i<sv.Length(); i++) {
REQUIRE((i+3) == sv.ValueAt(i));
}
}
SECTION("InsertEmpty") {
sv.InsertEmpty(0, 0);
REQUIRE(0 == sv.Length());
int *pi = sv.InsertEmpty(0, 2);
REQUIRE(2 == sv.Length());
REQUIRE(0 == sv.ValueAt(0));
REQUIRE(0 == sv.ValueAt(1));
pi[0] = 4;
pi[1] = 5;
REQUIRE(4 == sv.ValueAt(0));
REQUIRE(5 == sv.ValueAt(1));
pi = sv.InsertEmpty(1, 2);
pi[0] = 6;
pi[1] = 7;
REQUIRE(4 == sv.Length());
REQUIRE(4 == sv.ValueAt(0));
REQUIRE(6 == sv.ValueAt(1));
REQUIRE(7 == sv.ValueAt(2));
REQUIRE(5 == sv.ValueAt(3));
}
SECTION("SetValue") {
sv.InsertValue(0, 10, 0);
sv.SetValueAt(5, 3);
REQUIRE(10 == sv.Length());
for (int i=0; i<sv.Length(); i++) {
REQUIRE(((i == 5) ? 3 : 0) == sv.ValueAt(i));
}
// Move the gap
sv.InsertValue(7, 1, 17);
REQUIRE(17 == sv.ValueAt(7));
REQUIRE(0 == sv.ValueAt(8));
// Set after the gap
sv.SetValueAt(8, 19);
REQUIRE(19 == sv.ValueAt(8));
}
SECTION("Indexing") {
sv.InsertValue(0, 10, 0);
sv.SetValueAt(5, 3);
REQUIRE(10 == sv.Length());
for (int i=0; i<sv.Length(); i++) {
REQUIRE(((i == 5) ? 3 : 0) == sv[i]);
}
}
SECTION("Fill") {
sv.InsertValue(0, 10, 0);
REQUIRE(10 == sv.Length());
sv.InsertValue(7, 1, 1);
REQUIRE(11 == sv.Length());
for (int i=0; i<sv.Length(); i++) {
REQUIRE(((i == 7) ? 1 : 0) == sv.ValueAt(i));
}
}
SECTION("DeleteOne") {
sv.InsertFromArray(0, testArray, 0, lengthTestArray);
sv.Delete(2);
REQUIRE((lengthTestArray-1) == sv.Length());
REQUIRE(3 == sv[0]);
REQUIRE(4 == sv[1]);
REQUIRE(6 == sv[2]);
}
SECTION("DeleteRange") {
sv.InsertValue(0, 10, 0);
REQUIRE(10 == sv.Length());
sv.InsertValue(7, 1, 1);
REQUIRE(11 == sv.Length());
sv.DeleteRange(2, 3);
REQUIRE(8 == sv.Length());
for (int i=0; i<sv.Length(); i++) {
REQUIRE(((i == 4) ? 1 : 0) == sv.ValueAt(i));
}
}
SECTION("DeleteAll") {
sv.InsertValue(0, 10, 0);
sv.InsertValue(7, 1, 1);
sv.DeleteRange(2, 3);
sv.DeleteAll();
REQUIRE(0 == sv.Length());
}
SECTION("GetRange") {
sv.InsertValue(0, 10, 0);
sv.InsertValue(7, 1, 1);
int retrieveArray[11] = {0};
sv.GetRange(retrieveArray, 0, 11);
for (int i=0; i<sv.Length(); i++) {
REQUIRE(((i==7) ? 1 : 0) == retrieveArray[i]);
}
}
SECTION("GetRangeOverGap") {
sv.InsertFromArray(0, testArray, 0, lengthTestArray);
REQUIRE(lengthTestArray == sv.Length());
int retrieveArray[lengthTestArray] = {0};
sv.GetRange(retrieveArray, 0, lengthTestArray);
for (int i=0; i<sv.Length(); i++) {
REQUIRE((i+3) == retrieveArray[i]);
}
}
SECTION("ReplaceUp") {
// Replace each element by inserting and then deleting the displaced element
// This should perform many moves
constexpr int testLength=105;
sv.EnsureLength(testLength);
for (int i=0; i<testLength; i++)
sv.SetValueAt(i, i+2);
REQUIRE(testLength == sv.Length());
for (int i=0; i<sv.Length(); i++) {
sv.InsertValue(i, 1, i+9);
sv.Delete(i+1);
}
for (int i=0; i<sv.Length(); i++)
REQUIRE((i+9) == sv.ValueAt(i));
}
SECTION("ReplaceDown") {
// From the end, replace each element by inserting and then deleting the displaced element
// This should perform many moves
constexpr int testLength=24;
sv.EnsureLength(testLength);
for (int i=0; i<testLength; i++)
sv.SetValueAt(i, i+12);
REQUIRE(testLength == sv.Length());
for (ptrdiff_t i=sv.Length()-1; i>=0; i--) {
sv.InsertValue(i, 1, static_cast<int>(i+5));
sv.Delete(i+1);
}
for (int i=0; i<sv.Length(); i++)
REQUIRE((i+5) == sv.ValueAt(i));
}
SECTION("BufferPointer") {
// Low-level access to the data
sv.InsertFromArray(0, testArray, 0, lengthTestArray);
sv.Insert(0, 99); // This moves the gap so that BufferPointer() must also move
REQUIRE(1 == sv.GapPosition());
constexpr int lengthAfterInsertion = 1 + lengthTestArray;
REQUIRE(lengthAfterInsertion == (sv.Length()));
const int *retrievePointer = sv.BufferPointer();
for (int i=1; i<sv.Length(); i++) {
REQUIRE((i+3-1) == retrievePointer[i]);
}
REQUIRE(lengthAfterInsertion == sv.Length());
// Gap was moved to end.
REQUIRE(lengthAfterInsertion == sv.GapPosition());
}
SECTION("DeleteBackAndForth") {
sv.InsertValue(0, 10, 87);
for (int i=0; i<10; i+=2) {
const int len = 10 - i;
REQUIRE(len == sv.Length());
for (int j=0; j<sv.Length(); j++) {
REQUIRE(87 == sv.ValueAt(j));
}
sv.Delete(len-1);
sv.Delete(0);
}
}
SECTION("GrowSize") {
sv.SetGrowSize(5);
REQUIRE(5 == sv.GetGrowSize());
}
SECTION("OutsideBounds") {
sv.InsertValue(0, 10, 87);
REQUIRE(0 == sv.ValueAt(-1));
REQUIRE(0 == sv.ValueAt(10));
/* Could be a death test as this asserts:
sv.SetValueAt(-1,98);
sv.SetValueAt(10,99);
REQUIRE(0 == sv.ValueAt(-1));
REQUIRE(0 == sv.ValueAt(10));
*/
}
}

View File

@ -0,0 +1,361 @@
/** @file testUniConversion.cxx
** Unit Tests for Scintilla internal data structures
**/
#include <cstring>
#include <string>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
#include "Debugging.h"
#include "UniConversion.h"
#include "catch.hpp"
using namespace Scintilla::Internal;
// Test UniConversion.
// Use examples from Wikipedia:
// https://en.wikipedia.org/wiki/UTF-8
TEST_CASE("UTF16Length") {
SECTION("UTF16Length ASCII") {
// Latin Small Letter A
const char *s = "a";
const size_t len = UTF16Length(s);
REQUIRE(len == 1U);
}
SECTION("UTF16Length Example1") {
// Dollar Sign
const char *s = "\x24";
const size_t len = UTF16Length(s);
REQUIRE(len == 1U);
}
SECTION("UTF16Length Example2") {
// Cent Sign
const char *s = "\xC2\xA2";
const size_t len = UTF16Length(s);
REQUIRE(len == 1U);
}
SECTION("UTF16Length Example3") {
// Euro Sign
const char *s = "\xE2\x82\xAC";
const size_t len = UTF16Length(s);
REQUIRE(len == 1U);
}
SECTION("UTF16Length Example4") {
// Gothic Letter Hwair
const char *s = "\xF0\x90\x8D\x88";
const size_t len = UTF16Length(s);
REQUIRE(len == 2U);
}
SECTION("UTF16Length Invalid Trail byte in lead position") {
const char *s = "a\xB5yz";
const size_t len = UTF16Length(s);
REQUIRE(len == 4U);
}
SECTION("UTF16Length Invalid Lead byte at end") {
const char *s = "a\xC2";
const size_t len = UTF16Length(s);
REQUIRE(len == 2U);
}
SECTION("UTF16Length Invalid Lead byte implies 3 trails but only 2") {
const char *s = "a\xF1yz";
const size_t len = UTF16Length(s);
REQUIRE(len == 2U);
}
}
TEST_CASE("UniConversion") {
// UnicodeFromUTF8
SECTION("UnicodeFromUTF8 ASCII") {
const unsigned char s[]={'a', 0, 0, 0};
REQUIRE(UnicodeFromUTF8(s) == 'a');
}
SECTION("UnicodeFromUTF8 Example1") {
const unsigned char s[]={0x24, 0, 0, 0};
REQUIRE(UnicodeFromUTF8(s) == 0x24);
}
SECTION("UnicodeFromUTF8 Example2") {
const unsigned char s[]={0xC2, 0xA2, 0, 0};
REQUIRE(UnicodeFromUTF8(s) == 0xA2);
}
SECTION("UnicodeFromUTF8 Example3") {
const unsigned char s[]={0xE2, 0x82, 0xAC, 0};
REQUIRE(UnicodeFromUTF8(s) == 0x20AC);
}
SECTION("UnicodeFromUTF8 Example4") {
const unsigned char s[]={0xF0, 0x90, 0x8D, 0x88, 0};
REQUIRE(UnicodeFromUTF8(s) == 0x10348);
}
// UTF16FromUTF8
SECTION("UTF16FromUTF8 ASCII") {
const char s[] = {'a', 0};
wchar_t tbuf[1] = {0};
const size_t tlen = UTF16FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 'a');
}
SECTION("UTF16FromUTF8 Example1") {
const char s[] = {'\x24', 0};
wchar_t tbuf[1] = {0};
const size_t tlen = UTF16FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x24);
}
SECTION("UTF16FromUTF8 Example2") {
const char s[] = {'\xC2', '\xA2', 0};
wchar_t tbuf[1] = {0};
const size_t tlen = UTF16FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0xA2);
}
SECTION("UTF16FromUTF8 Example3") {
const char s[] = {'\xE2', '\x82', '\xAC', 0};
wchar_t tbuf[1] = {0};
const size_t tlen = UTF16FromUTF8(s, tbuf, 1);;
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x20AC);
}
SECTION("UTF16FromUTF8 Example4") {
const char s[] = {'\xF0', '\x90', '\x8D', '\x88', 0};
wchar_t tbuf[2] = {0, 0};
const size_t tlen = UTF16FromUTF8(s, tbuf, 2);
REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == 0xD800);
REQUIRE(tbuf[1] == 0xDF48);
}
SECTION("UTF16FromUTF8 Invalid Trail byte in lead position") {
const char s[] = "a\xB5yz";
wchar_t tbuf[4] = {};
const size_t tlen = UTF16FromUTF8(s, tbuf, 4);
REQUIRE(tlen == 4U);
REQUIRE(tbuf[0] == 'a');
REQUIRE(tbuf[1] == 0xB5);
REQUIRE(tbuf[2] == 'y');
REQUIRE(tbuf[3] == 'z');
}
SECTION("UTF16FromUTF8 Invalid Lead byte at end") {
const char s[] = "a\xC2";
wchar_t tbuf[2] = {};
const size_t tlen = UTF16FromUTF8(s, tbuf, 2);
REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == 'a');
REQUIRE(tbuf[1] == 0xC2);
}
SECTION("UTF16FromUTF8 Invalid Lead byte implies 3 trails but only 2") {
const char *s = "a\xF1yz";
wchar_t tbuf[4] = {};
const size_t tlen = UTF16FromUTF8(s, tbuf, 4);
REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == 'a');
REQUIRE(tbuf[1] == 0xF1);
}
// UTF32FromUTF8
SECTION("UTF32FromUTF8 ASCII") {
const char s[] = {'a', 0};
unsigned int tbuf[1] = {0};
const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == static_cast<unsigned int>('a'));
}
SECTION("UTF32FromUTF8 Example1") {
const char s[] = {'\x24', 0};
unsigned int tbuf[1] = {0};
const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x24);
}
SECTION("UTF32FromUTF8 Example2") {
const char s[] = {'\xC2', '\xA2', 0};
unsigned int tbuf[1] = {0};
const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0xA2);
}
SECTION("UTF32FromUTF8 Example3") {
const char s[] = {'\xE2', '\x82', '\xAC', 0};
unsigned int tbuf[1] = {0};
const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x20AC);
}
SECTION("UTF32FromUTF8 Example4") {
const char s[] = {'\xF0', '\x90', '\x8D', '\x88', 0};
unsigned int tbuf[1] = {0};
const size_t tlen = UTF32FromUTF8(s, tbuf, 1);
REQUIRE(tlen == 1U);
REQUIRE(tbuf[0] == 0x10348);
}
SECTION("UTF32FromUTF8 Invalid Trail byte in lead position") {
const char s[] = "a\xB5yz";
unsigned int tbuf[4] = {};
const size_t tlen = UTF32FromUTF8(s, tbuf, 4);
REQUIRE(tlen == 4U);
REQUIRE(tbuf[0] == static_cast<unsigned int>('a'));
REQUIRE(tbuf[1] == 0xB5);
REQUIRE(tbuf[2] == static_cast<unsigned int>('y'));
REQUIRE(tbuf[3] == static_cast<unsigned int>('z'));
}
SECTION("UTF32FromUTF8 Invalid Lead byte at end") {
const char s[] = "a\xC2";
unsigned int tbuf[2] = {};
const size_t tlen = UTF32FromUTF8(s, tbuf, 2);
REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == static_cast<unsigned int>('a'));
REQUIRE(tbuf[1] == 0xC2);
}
SECTION("UTF32FromUTF8 Invalid Lead byte implies 3 trails but only 2") {
const char *s = "a\xF1yz";
unsigned int tbuf[4] = {};
const size_t tlen = UTF32FromUTF8(s, tbuf, 4);
REQUIRE(tlen == 2U);
REQUIRE(tbuf[0] == static_cast<unsigned int>('a'));
REQUIRE(tbuf[1] == 0xF1);
}
}
namespace {
// Simple adapter to avoid casting
int UTFClass(std::string_view sv) noexcept {
return UTF8Classify(sv);
}
}
TEST_CASE("UTF8Classify") {
// These tests are supposed to hit every return statement in UTF8Classify in order
// with some hit multiple times.
// Single byte
SECTION("UTF8Classify Simple ASCII") {
REQUIRE(UTFClass("a") == 1);
}
SECTION("UTF8Classify Invalid Too large lead") {
REQUIRE(UTFClass("\xF5") == (1|UTF8MaskInvalid));
}
SECTION("UTF8Classify Overlong") {
REQUIRE(UTFClass("\xC0\x80") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify single trail byte") {
REQUIRE(UTFClass("\x80") == (1 | UTF8MaskInvalid));
}
// Invalid length tests
SECTION("UTF8Classify 2 byte lead, string less than 2 long") {
REQUIRE(UTFClass("\xD0") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 3 byte lead, string less than 3 long") {
REQUIRE(UTFClass("\xEF") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 4 byte lead, string less than 4 long") {
REQUIRE(UTFClass("\xF0") == (1 | UTF8MaskInvalid));
}
// Invalid first trail byte tests
SECTION("UTF8Classify 2 byte lead trail is invalid") {
REQUIRE(UTFClass("\xD0q") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 3 byte lead invalid trails") {
REQUIRE(UTFClass("\xE2qq") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 4 byte bad trails") {
REQUIRE(UTFClass("\xF0xyz") == (1 | UTF8MaskInvalid));
}
// 2 byte lead
SECTION("UTF8Classify 2 byte valid character") {
REQUIRE(UTFClass("\xD0\x80") == 2);
}
// 3 byte lead
SECTION("UTF8Classify 3 byte lead, overlong") {
REQUIRE(UTFClass("\xE0\x80\xAF") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 3 byte lead, surrogate") {
REQUIRE(UTFClass("\xED\xA0\x80") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify FFFE non-character") {
REQUIRE(UTFClass("\xEF\xBF\xBE") == (3 | UTF8MaskInvalid));
}
SECTION("UTF8Classify FFFF non-character") {
REQUIRE(UTFClass("\xEF\xBF\xBF") == (3 | UTF8MaskInvalid));
}
SECTION("UTF8Classify FDD0 non-character") {
REQUIRE(UTFClass("\xEF\xB7\x90") == (3 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 3 byte valid character") {
REQUIRE(UTFClass("\xE2\x82\xAC") == 3);
}
// 4 byte lead
SECTION("UTF8Classify 1FFFF non-character") {
REQUIRE(UTFClass("\xF0\x9F\xBF\xBF") == (4 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 1 Greater than max Unicode 110000") {
// Maximum Unicode value is 10FFFF so 110000 is out of range
REQUIRE(UTFClass("\xF4\x90\x80\x80") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 4 byte overlong") {
REQUIRE(UTFClass("\xF0\x80\x80\x80") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 4 byte valid character") {
REQUIRE(UTFClass("\xF0\x9F\x8C\x90") == 4);
}
// Invalid 2nd or 3rd continuation bytes
SECTION("UTF8Classify 3 byte lead invalid 2nd trail") {
REQUIRE(UTFClass("\xE2\x82q") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 4 byte lead invalid 2nd trail") {
REQUIRE(UTFClass("\xF0\x9Fq\x9F") == (1 | UTF8MaskInvalid));
}
SECTION("UTF8Classify 4 byte lead invalid 3rd trail") {
REQUIRE(UTFClass("\xF0\x9F\x9Fq") == (1 | UTF8MaskInvalid));
}
}

View File

@ -0,0 +1,75 @@
/** @file unitTest.cxx
** Unit Tests for Scintilla internal data structures
**/
/*
Currently tested:
SplitVector
Partitioning
RunStyles
ContractionState
CharClassify
Decoration
DecorationList
CellBuffer
UniConversion
To do:
PerLine *
Range
StyledText
CaseFolder ...
Document
RESearch
Selection
Style
lexlib:
Accessor
LexAccessor
CharacterSet
OptionSet
PropSetSimple
StyleContext
*/
#include <cstdio>
#include <cstdarg>
#include <string_view>
#include <vector>
#include <optional>
#include <memory>
#include "Debugging.h"
#if defined(__GNUC__)
// Want to avoid misleading indentation warnings in catch.hpp but the pragma
// may not be available so protect by turning off pragma warnings
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wpragmas"
#if !defined(__clang__)
#pragma GCC diagnostic ignored "-Wmisleading-indentation"
#endif
#endif
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"
using namespace Scintilla::Internal;
// Needed for PLATFORM_ASSERT in code being tested
void Platform::Assert(const char *c, const char *file, int line) noexcept {
fprintf(stderr, "Assertion [%s] failed at %s %d\n", c, file, line);
abort();
}
void Platform::DebugPrintf(const char *format, ...) noexcept {
char buffer[2000];
va_list pArguments;
va_start(pArguments, format);
vsnprintf(buffer, std::size(buffer), format, pArguments);
va_end(pArguments);
fprintf(stderr, "%s", buffer);
}