Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
9162dc5
Add project scaffolding, version 1.0.0-dev.1, build projects, and CI
AnthonySWest Oct 17, 2026
4f1d8c2
Add core types and string/line utilities
AnthonySWest Oct 17, 2026
969372a
Add TASWIniOptions with validation
AnthonySWest Oct 17, 2026
57f9cda
Add the file layer: atomic save, backups, and UTF-8 paths
AnthonySWest Oct 17, 2026
07f605f
Split TASWIniOptions into reading, writing, and saving options
AnthonySWest Oct 17, 2026
c91cab1
Coding standards
AnthonySWest Oct 17, 2026
32c1a12
Add TASWIniDocument, the lossless INI model
AnthonySWest Oct 17, 2026
9be6803
Parse INI text into TASWIniDocument
AnthonySWest Oct 17, 2026
e003d86
Update AGENTS.md for coding standards for function comment blocks
AnthonySWest Oct 17, 2026
698d312
Write TASWIniDocument back to text
AnthonySWest Oct 17, 2026
1ed4377
Add the round-trip corpus test for TASWIniDocument
AnthonySWest Oct 17, 2026
a45141f
Add fuzz tests for the INI parser and writer
AnthonySWest Oct 17, 2026
9946c74
Update the README status for the document model
AnthonySWest Oct 17, 2026
8b3514d
Fix a shadowed parameter in Parse() and add -Wshadow
AnthonySWest Oct 17, 2026
97c9f61
Keep the options in TASWIniDocument and add Reset()
AnthonySWest Oct 17, 2026
2d63d8e
Look up sections and keys by name in TASWIniDocument
AnthonySWest Oct 17, 2026
8082f60
Set values and add sections in TASWIniDocument
AnthonySWest Oct 17, 2026
f3f1b42
Remove and rename sections and keys in TASWIniDocument
AnthonySWest Oct 17, 2026
34b9919
Get and set file, section and key comments in TASWIniDocument
AnthonySWest Oct 17, 2026
ae95c75
Add an edit fuzz test and keep edited lines from merging
AnthonySWest Oct 17, 2026
60b9ea6
Allow doubled quotes inside quoted values
AnthonySWest Oct 17, 2026
15b491d
Add TASWIniValueOptions and the value result codes
AnthonySWest Oct 17, 2026
eeaa362
Add TASWIniConverter with the bool and string converters
AnthonySWest Oct 17, 2026
3d07038
Add the integer converters
AnthonySWest Oct 18, 2026
10c4008
Add the float and double converters
AnthonySWest Oct 18, 2026
d4cf618
Add the enum converters
AnthonySWest Oct 18, 2026
e323e4e
Add the list converter
AnthonySWest Oct 18, 2026
050f7f0
Add the path and u8string converters
AnthonySWest Oct 18, 2026
075252d
Add the converter fuzz test and update the README status
AnthonySWest Oct 18, 2026
e22703e
Add TASWIniDocument::SetOptions() to read the text again with new opt…
AnthonySWest Oct 18, 2026
2ecb9cb
Add IASWIni with the typed GetValue(), TryGetValue() and SetValue()
AnthonySWest Oct 20, 2026
7f04cbb
Add TASWBaseIni, the thread-safe INI object, without files yet
AnthonySWest Oct 20, 2026
44758f8
Add Load(), Save() and SaveAs() to TASWBaseIni
AnthonySWest Oct 20, 2026
794cd6a
Keep a replaced file's save from failing on Linux, and skip the out-o…
AnthonySWest Oct 20, 2026
1e0dfeb
Add thread stress tests for TASWBaseIni and update the README status
AnthonySWest Oct 20, 2026
2d53331
Add TASWIniSection and TASWIniSetting<T>, settings that read and writ…
AnthonySWest Oct 20, 2026
4975924
Apply the settings to the document Load() reads, with ECommentPolicy …
AnthonySWest Oct 21, 2026
4cfde13
Rename a setting's old keys when Load() finds them, with KeyRenamed
AnthonySWest Oct 21, 2026
e404f56
Add the settings' thread, out-of-memory and end-to-end tests, and kee…
AnthonySWest Oct 21, 2026
57bcde1
Update .uncrustify.cfg to indent the next line of a wrapped brace ini…
AnthonySWest Oct 21, 2026
b52ca06
Change layout of TAppSettings now that uncrustify is happy with indents
AnthonySWest Oct 21, 2026
cd8031b
Coding standards
AnthonySWest Oct 21, 2026
e57dda5
Make PathToUtf8() and Utf8ToPath() public
AnthonySWest Oct 21, 2026
bdc2d97
Add the example program, its CMake project, ASWINI_BUILD_EXAMPLE and …
AnthonySWest Oct 21, 2026
093aee6
Add the RAD Studio example project for Win32 and Win64x
AnthonySWest Oct 21, 2026
bac4c25
Check a settings class, a text and a file against the installed package
AnthonySWest Oct 21, 2026
666e12e
Add docs/Format.md, the INI format ASWIni reads and writes
AnthonySWest Oct 21, 2026
652d440
Add docs/Options.md, describing every option with its default
AnthonySWest Oct 21, 2026
9e65776
Let a setting hold a list of a type without operator<, and say that l…
AnthonySWest Oct 21, 2026
47f6d06
Add docs/Settings.md, a guide to settings classes, with an ASWLog ada…
AnthonySWest Oct 21, 2026
9a64ff8
Add docs/Guarantees.md: errors, threads, lifetimes, saving and the codes
AnthonySWest Oct 21, 2026
c8fb489
Make README a front page with quick start, an API overview and docs, …
AnthonySWest Oct 22, 2026
1d7211a
Bump version to 1.0.0
AnthonySWest Oct 22, 2026
fe0d29f
Roll up the CHANGELOG for 1.0.0 and update README
AnthonySWest Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
299 changes: 299 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
name: CI

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

# Cancel a superseded run on the same branch/PR instead of letting it finish.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

# Read-only on purpose: a pull request from a fork gets a read-only token anyway, so nothing here needs to write
# (the test report goes to each job's summary page, which needs no permission).
permissions:
contents: read

jobs:
build-and-test:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
# Safety net: a hung/looping test should fail the job quickly instead of burning the runner's full time
# allotment. --test-timeout-seconds below covers a single hung test; this covers everything else. A sanitizer
# build compiles and runs several times slower, so it gets longer.
timeout-minutes: ${{ matrix.sanitize && 15 || 10 }}
env:
# cmake --build compiles up to 4 files at once (the runners have 4 cores). The Visual Studio generator only
# builds projects in parallel with it, so the MSVC job also compiles with -MP (its cxxflags).
CMAKE_BUILD_PARALLEL_LEVEL: 4
strategy:
fail-fast: false
matrix:
include:
- name: Windows (MSVC)
os: windows-latest
generator: ""
build_args: "--config Release"
exe_path: build/bin/Release/ASWIniTests.exe
# cl takes -MP as well as /MP; Git Bash would turn "/MP" into a path when it starts CMake.
cxxflags: -MP
# A multi-config generator, so the package check builds and installs both into the same prefix (the Debug
# library is ASWInid)
package_configs: Debug Release
sanitize: ""
- name: Windows (MinGW)
os: windows-latest
generator: "MinGW Makefiles"
build_args: ""
exe_path: build/bin/ASWIniTests.exe
sanitize: ""
- name: Linux (GCC)
os: ubuntu-latest
generator: ""
build_args: ""
exe_path: build/bin/ASWIniTests
cc: gcc
cxx: g++
sanitize: ""
- name: Linux (Clang)
os: ubuntu-latest
generator: ""
build_args: ""
exe_path: build/bin/ASWIniTests
cc: clang
cxx: clang++
sanitize: ""
# The other jobs build the tests at C++20 (tests/cmake's default). The library's minimum is C++17, the only
# standard RAD Studio's 32-bit compiler (bcc32c) supports, which CI can't run; this job catches C++20 code
# that the C++17 path must not use. (bcc32c's own standard library is checked with the RAD Win32 builds.)
- name: Linux (GCC, C++17)
os: ubuntu-latest
generator: ""
build_args: ""
exe_path: build/bin/ASWIniTests
cc: gcc
cxx: g++
cxx_standard: 17
sanitize: ""
# The sanitizer jobs build with ASWINI_SANITIZE (see the root CMakeLists.txt). A sanitizer report fails the
# run with a non-zero exit code, even when every test passed.
- name: Linux (Clang, ThreadSanitizer)
os: ubuntu-latest
generator: ""
build_args: ""
exe_path: build/bin/ASWIniTests
cc: clang
cxx: clang++
sanitize: thread
- name: Linux (Clang, AddressSanitizer + UBSan)
os: ubuntu-latest
generator: ""
build_args: ""
exe_path: build/bin/ASWIniTests
cc: clang
cxx: clang++
sanitize: address
steps:
- name: Check out repository
uses: actions/checkout@v7
with:
# The ASWUnitTests framework is the third_party/asw-unit-tests submodule. tests/cmake/CMakeLists.txt
# fails the configure step if it's missing, rather than silently building something else.
submodules: true

- name: Set up MinGW
if: matrix.generator == 'MinGW Makefiles'
uses: egor-tensin/setup-mingw@v3
with:
platform: x64

- name: Limit address space randomization for the sanitizers
# Ubuntu runners use more mmap randomization bits than some sanitizer runtimes support, which makes them
# fail at startup (e.g. "ThreadSanitizer: unexpected memory mapping"). 28 bits is the usual workaround.
if: matrix.sanitize
run: sudo sysctl vm.mmap_rnd_bits=28

- name: Configure
shell: bash
run: |
if [ -n "${{ matrix.cc }}" ]; then
export CC="${{ matrix.cc }}"
export CXX="${{ matrix.cxx }}"
fi
# CMake adds CXXFLAGS to its default flags (setting CMAKE_CXX_FLAGS instead would replace them).
if [ -n "${{ matrix.cxxflags }}" ]; then
export CXXFLAGS="${{ matrix.cxxflags }}"
fi
if [ -n "${{ matrix.generator }}" ]; then
cmake -S tests/cmake -B build -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release \
-DASWINI_SANITIZE="${{ matrix.sanitize }}" -DASWINI_CXX_STANDARD="${{ matrix.cxx_standard || 20 }}"
else
cmake -S tests/cmake -B build -DCMAKE_BUILD_TYPE=Release -DASWINI_SANITIZE="${{ matrix.sanitize }}" \
-DASWINI_CXX_STANDARD="${{ matrix.cxx_standard || 20 }}"
fi

- name: Build
shell: bash
run: cmake --build build ${{ matrix.build_args }}

- name: Verify executable exists
shell: bash
run: |
if [ ! -f "${{ matrix.exe_path }}" ]; then
echo "ERROR: Executable not found at ${{ matrix.exe_path }}"
echo "Contents of build directory:"
find build -type f \( -name "ASWIniTests" -o -name "*.exe" \) 2>/dev/null || echo "No executables found"
exit 1
fi

- name: Run unit tests
id: tests
shell: bash
# --test-timeout-seconds and --catch-crashes turn a hung or natively crashing test into a clean, reported
# failure instead of relying on the job's timeout-minutes or an opaque crashed job. A sanitizer job leaves out
# --catch-crashes, whose signal handlers would replace the sanitizer's own crash report (with its call stack),
# and allows a slower test.
env:
TSAN_OPTIONS: second_deadlock_stack=1
UBSAN_OPTIONS: print_stacktrace=1
run: >-
./${{ matrix.exe_path }} --project-name "ASWIni-CI" --report-junit junit-results.xml
${{ matrix.sanitize && '--test-timeout-seconds 300' || '--test-timeout-seconds 60 --catch-crashes' }}

- name: Test results summary
# Runs whenever the tests ran, pass or fail, so failures show up in the report too. In this mode the reporter
# only writes this job's summary page and makes no GitHub API calls, so it also works for pull requests from
# forks, whose token is read-only. It also fails if the tests ran but the report is missing or unreadable.
if: ${{ !cancelled() && steps.tests.outcome != 'skipped' }}
uses: dorny/test-reporter@v3
with:
name: Test Results - ${{ matrix.name }}
path: junit-results.xml
reporter: java-junit
use-actions-summary: true

- name: Upload test results
# Keeps the JUnit report as a downloadable artifact of the run.
if: ${{ !cancelled() && steps.tests.outcome != 'skipped' }}
uses: actions/upload-artifact@v7
with:
name: test-results-${{ matrix.name }}
path: junit-results.xml
if-no-files-found: error

- name: Build and run the example
# Builds example/cmake (with the library from the source tree) at the job's C++ standard and with its
# sanitizer, and runs it: it returns 0 only when every step did what it shows, and a sanitizer report fails it
# too. Its output (the messages and the INI file of each step) is in this step's log.
shell: bash
env:
TSAN_OPTIONS: second_deadlock_stack=1
UBSAN_OPTIONS: print_stacktrace=1
run: |
if [ -n "${{ matrix.cc }}" ]; then
export CC="${{ matrix.cc }}"
export CXX="${{ matrix.cxx }}"
fi
if [ -n "${{ matrix.cxxflags }}" ]; then
export CXXFLAGS="${{ matrix.cxxflags }}"
fi
generatorArgs=()
if [ -n "${{ matrix.generator }}" ]; then
generatorArgs=(-G "${{ matrix.generator }}")
fi
cmake -S example/cmake -B build-example "${generatorArgs[@]}" -DCMAKE_BUILD_TYPE=Release \
-DASWINI_SANITIZE="${{ matrix.sanitize }}" -DASWINI_CXX_STANDARD="${{ matrix.cxx_standard || 20 }}"
cmake --build build-example ${{ matrix.build_args }}
# The tests' path with the example's folder and name, e.g. build-example/bin/Release/ASWIniExample.exe
exe="${{ matrix.exe_path }}"
exe="${exe/#build\//build-example/}"
exe="${exe/ASWIniTests/ASWIniExample}"
"./$exe"

- name: Check the installed CMake package
# Builds and installs the library (the root project) to a temporary prefix, then builds tests/package against
# it with find_package() and runs it, which checks the install rules and the package files. Not in the
# sanitizer jobs: they build the same CMake files, and the package would pass their flags on to the consumer.
if: ${{ !matrix.sanitize }}
shell: bash
run: |
if [ -n "${{ matrix.cc }}" ]; then
export CC="${{ matrix.cc }}"
export CXX="${{ matrix.cxx }}"
fi
if [ -n "${{ matrix.cxxflags }}" ]; then
export CXXFLAGS="${{ matrix.cxxflags }}"
fi
generatorArgs=()
if [ -n "${{ matrix.generator }}" ]; then
generatorArgs=(-G "${{ matrix.generator }}")
fi
# Forward slashes, which CMake takes on Windows too (RUNNER_TEMP has backslashes there)
prefix="${RUNNER_TEMP//\\//}/aswini"
configs="${{ matrix.package_configs || 'Release' }}"
cmake -S . -B build-install "${generatorArgs[@]}" -DCMAKE_BUILD_TYPE=Release
for config in $configs; do
cmake --build build-install --config "$config"
cmake --install build-install --config "$config" --prefix "$prefix"
done
cmake -S tests/package -B build-package "${generatorArgs[@]}" -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH="$prefix"
for config in $configs; do
cmake --build build-package --config "$config"
ctest --test-dir build-package -C "$config" --output-on-failure
done

coverage:
# Which lines of the library (ASWIni/) the tests run, on Linux: a table on this job's summary page, and an HTML
# report (each source line marked) as an artifact. Informational: continue-on-error keeps it from failing the run;
# the jobs above are the ones that must pass.
name: Linux (GCC, coverage)
runs-on: ubuntu-latest
timeout-minutes: 15
continue-on-error: true
env:
CMAKE_BUILD_PARALLEL_LEVEL: 4
steps:
- name: Check out repository
uses: actions/checkout@v7
with:
submodules: true

- name: Configure
# Debug (no optimization), so each line is counted as written. -fprofile-update=atomic keeps the counts right
# in the tests that use one object from several threads at once; without it the threads' updates race and get
# lost.
env:
CC: gcc
CXX: g++
CXXFLAGS: "--coverage -fprofile-update=atomic"
run: cmake -S tests/cmake -B build -DCMAKE_BUILD_TYPE=Debug

- name: Build
run: cmake --build build

- name: Run unit tests
# A failing test fails the jobs above; the coverage of this run is still worth reporting
continue-on-error: true
run: ./build/bin/ASWIniTests --test-timeout-seconds 120 --catch-crashes

- name: Install gcovr
# 8.4 added the Markdown report. pipx installs it in an environment of its own (the runner's system Python
# refuses pip installs).
run: pipx install "gcovr>=8.4"

- name: Coverage report
run: |
mkdir -p coverage-html
gcovr --root . --filter 'ASWIni/' --print-summary --markdown coverage.md \
--html-details coverage-html/index.html
cat coverage.md >> "$GITHUB_STEP_SUMMARY"

- name: Upload coverage report
uses: actions/upload-artifact@v7
with:
name: coverage-report
path: coverage-html/
if-no-files-found: error
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ Obj/
cmake-build-*/
out/
build/
# Extra CMake build folders, e.g. tests/cmake/build-cpp17 or tests/cmake/build-tsan
build-*/
.Xil/
MSVC.Suo/
*.Visual studio/
Expand Down
2 changes: 1 addition & 1 deletion .uncrustify.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -1590,7 +1590,7 @@ indent_vbrace_open_on_tabstop = false # true/false
# false: indent all contained lines to match the brace
#
# Default: true
indent_token_after_brace = true # true/false
indent_token_after_brace = false # true/false

# Whether to indent the body of a C++11 lambda.
indent_cpp_lambda_body = false # true/false
Expand Down
Loading
Loading