Commit Graph
7029 Commits
Author SHA1 Message Date
Mike-Solar 1a7029fd8f engine: add the footage family to the C ABI facade; oak-cli probe
- oakengine_footage_probe inspects media without a project (decoder
  probe): stream counts, per-stream video info (dimensions, rate,
  duration, color tags, interlacing), audio info, duration, decoder
  name, source start time; thread_local last_error for the NULL-handle
  failure paths
- oakengine_project_import_footage adds probed footage to a project
  through the same undoable command path as the app
- oak-cli probe prints decoder/duration/per-stream details and runs as
  a ctest everywhere (no GL)
- dual ownership documented: probe handles are owned, imported footage
  is borrowed from its project
2026-07-20 06:03:16 +08:00
Mike-Solar 6118260e01 engine: expose sequence video dimensions in the timeline facade
- oakengine_sequence_get_video_params (width/height/pixel aspect) with
  assertions for the default 1920x1080 square-pixel sequence and the
  invalid-handle path
- oak-cli now renders at the sequence's real dimensions instead of the
  hardcoded 1920x1080
2026-07-20 05:51:53 +08:00
Mike-Solar 009af0ff3f cli: add oak-cli, the first pure C ABI consumer of liboakengine
- oak-cli info prints project/sequence/footage details; oak-cli render
  writes PPM frames and a PCM WAV through the facade only - no engine
  C++ headers, no Qt headers, links just liboakengine
- exit code 2 marks rendering-unavailable so ctest can skip cleanly on
  machines without a GL render backend; info test runs everywhere
- facade fix uncovered by the CLI: project load now absolutizes the
  path, so relative footage paths can't be mistaken for a moved
  project; the fixture project now carries probed footage wired into
  its sequence so it renders real content
- packaged like the other binaries (Linux bin install, macOS bundle,
  Windows DLL copies); DESTDIR-verified
2026-07-20 05:48:07 +08:00
Mike-Solar b26ffb7d64 engine: add the renderer family to the C ABI facade
- oakengine_renderer_create/set_mode/last_error, render_frame (sync,
  60s timeout, CPU frames via the existing worker pool),
  render_audio (planar float), cancel; frames and audio buffers are
  owned handles with borrowed data pointers
- output colorspace names map to OCIO display transforms, with
  graceful fallback to reference-space output
- oakengine_renderer_test: parameter validation and error paths need
  no GL and always run; render assertions gate on
  DynamicRenderer backend availability and SKIP cleanly otherwise
- fix a facade bug found by its own test: wait_for_ticket leaked a
  connected lambda capturing a stack reference, which a subsequent
  cancelled ticket could fire into reused stack memory
2026-07-20 05:26:42 +08:00
Mike-Solar 5db7aac058 engine: add init/project/timeline families to the C ABI facade
- oakengine_init/shutdown with HEADLESS/RENDER flags: headless boots
  Config, NodeFactory, ColorManager, task/conform/proxy/frame/disk
  managers and the serializer (plus an offscreen QGuiApplication that
  Qt requires for QAction); RENDER adds RenderManager. Idempotent and
  upgradable, no UI anywhere
- oakengine_project_* (17): create/load/save, modified state, name,
  footage enumeration with online check, undo/redo, sequence access
- oakengine_sequence_* (13): name, length (seconds and rational),
  frame rate, per-type track counts, playhead (timestamp and seconds),
  work area, markers; sequences are borrowed handles owned by their
  project
- pure-C oakengine_init_test covers init idempotency, save/load
  round-trip through a real fixture project, footage online checks,
  timeline parameters, and NULL/bounds safety - no GL required
2026-07-20 04:54:45 +08:00
Mike-Solar 37845302f9 engine: begin the liboakengine C ABI facade with the IPC subsystem
- oakengine/export.h establishes the OAKENGINE_API visibility macros;
  include/oakengine/ipc.h is the first pure-C surface (41 functions:
  shm, frame slot pool, and the worker IPC messages as POD<->JSON
  build/parse), implemented in engine/src/capi/
- the IPC implementations move to engine/src/oliveimpl (namespace
  olive::engine::internal::ipc); engine/render/ipc/*.h are rebuilt as
  same-name/same-API wrapper classes forwarding across the C boundary
- FrameSlotMeta is shared with the C header verbatim so the app/worker
  wire format (v1) is bit-identical; static_asserts pin sizeof and
  field offsets
- spscringbuffer.h moves to include/oakengine/ as an inline-only
  header (no symbols, not ABI)
- new pure-C test oakengine_ipc_test (make_oakengine_test, no GL)
  covers shm, frame pool, message round-trips and the layout asserts;
  full gtest suite stays green (1986 tests)
2026-07-20 04:12:58 +08:00
Mike-Solar 28c4426236 build: split the engine into liboakengine.so; worker drops the UI entirely
Physical split: app/{audio,cli,codec,common,config,node,pluginSupport,
render,task,timeline,undo,tool,shaders} plus coreengine, version and
ui/icons+colorcoding move to a new top-level engine/ tree, built as
liboakengine.so (shared). The render backends (oakgl/oakvulkan) move
with it and link the engine library instead of embedding a static
render-core subset (libolive-rendercore is gone).

- oak-render-worker now links liboakengine instead of the whole
  libolive-editor object set: 336MB -> 2.9MB, no Qt Widgets UI
- the editor links liboakengine for the engine and keeps only UI
  objects in libolive-editor
- install/packaging: GNUInstallDirs libdir on Linux, bundle copy on
  macOS, oakengine.dll staged for NSIS, AppImage validation entry
- fix backend lookup for the new layout: DynamicRenderer searched
  ../app but backends now live in engine/; a stale pre-split liboakgl
  in the build tree got dlopened instead, re-initialized and later
  destroyed the interposed engine statics (full-suite segfault at
  DialogSequenceParameterTab, found via gdb watchpoint)
2026-07-20 03:23:28 +08:00
Mike-Solar 026ff94b5e refactor: invert the last engine-to-UI dependencies
- NodeFactory's menu creation moves to UI-side widget/menu/factorymenu
  (the factory only exposes its node library read-only now)
- DiskManager's cache-settings dialog is created through a registered
  std::function handler (registered by Core at startup)
- OlivePluginInstance creates progress UIs through a
  PluginProgressReporter interface (Null fallback headless) and queries
  the active viewer through a provider callback, both registered by Core
- factory.h, diskmanager and pluginSupport no longer reference any
  widget//dialog//panel//window headers or classes
2026-07-20 02:05:01 +08:00
Mike-Solar bff06e00e5 refactor: split Core into EngineCore (engine) and Core (UI)
EngineCore (new app/coreengine.{h,cpp}) owns every engine-safe part of
the old Core singleton: CoreParams, lifecycle of the engine managers,
UndoStack, tool/snapping/timecode state, locale, autorecovery, recent
projects, footage filters, clipboard, project registry, type
declarations, and the proxy toggle. UI dependencies are inverted
through hooks instead: status-bar/cache-full signals and std::function
handlers for image-sequence confirmation, footage relink, OTIO import,
project save/close and layout load (same pattern as
Config::ErrorHandler).

Core (app/) now derives from EngineCore and keeps only UI behavior:
the main window, dialogs, panel heuristics, import/export flows and
project lifecycle presentation. Its public API is unchanged (all
inherited), and Core::instance() covariantly static_casts the engine
singleton. The render worker constructs EngineCore directly, making it
the first binary that no longer needs the UI side of Core.

~25 engine call sites move from core.h to coreengine.h; a dozen more
drop a vestigial core.h include (gaining direct includes for symbols
they were borrowing transitively). Full gtest suite green (1986 tests,
0 failures).
2026-07-20 01:38:42 +08:00
Mike-Solar 5109dd2995 refactor: move the ratio input dialog out of engine common/ into dialog/
get_float_ratio_from_user() is pure UI (QInputDialog/QMessageBox);
common/ stays dialog-free.
2026-07-20 01:04:57 +08:00
Mike-Solar 69cd8d3a75 refactor: move msg_box out of engine qtutils into the dialog layer
The engine-side QtUtils no longer references QMessageBox; the two
dialog call sites use the identical olive::msg_box() inline helper.
2026-07-20 01:01:18 +08:00
Mike-Solar 03d4087124 refactor: cut engine-to-UI include violations ahead of the liboakengine split
- slider DisplayType enums sink to node/sliderdisplaytype.h (canonical
  engine home); FloatSlider/RationalSlider alias them for compatibility
- DropWithoutSequenceBehavior enum sinks to common/dropworkflowbehavior.h
- Config errors now go through a registered ErrorHandler hook instead of
  QMessageBox with a MainWindow parent; the style default no longer
  depends on the UI style manager
- MainWindowLayoutInfo moves to node/project/serializer/ and its panel
  dependency is reduced to a plain std::map alias (PanelLayoutInfo),
  breaking the engine -> PanelWidget -> KDDockWidgets chain
- ProjectImportErrorDialog moves from task/ to dialog/projectimport/
- remove confirmed-redundant UI includes and give project.h/import.h/
  project.cpp the direct includes they were borrowing transitively
- project.h includes folder/sequence headers directly (it used both
  types in its own API all along)
2026-07-20 00:52:12 +08:00
Mike-Solar 98f2f3e224 build: install liboakcore from the default target set
core was added with EXCLUDE_FROM_ALL, which also excluded its install
rules, so liboakcore.so never made it into packages. It now installs to
the platform libdir (/usr/lib on this machine, verified via DESTDIR).
2026-07-19 23:52:59 +08:00
Mike-Solar 1128694257 core: restrict liboakcore exports to the C ABI via version script
Even statically linked runtime libraries must not leak symbols: the
linker version script now whitelists oakcore_* only. nm reports 188
oakcore_* exports and zero anything else.
2026-07-19 23:51:15 +08:00
Mike-Solar 4fc8b80d7e core: turn olivecore into liboakcore.so with a pure C ABI
liboakcore is now a shared library that exposes only a C ABI:
- every value class (Rational, TimeRange, Color, Bezier, AudioParams,
  SampleBuffer) and the free-function groups (StringUtils, fraction
  utils, Timecode) is wrapped in an opaque-handle C API under
  core/include/olive/core/oakcore/ (init/copy/free + self-first
  functions), implemented in core/src/capi/
- consumers keep the original C++ API unchanged through same-name
  wrapper classes that hold the handle and forward across the C
  boundary; original implementations moved to core/src/oliveimpl
  (namespace olive::core::internal) and are hidden from export
- TimeRangeList/TimeRangeListFrameIterator are reimplemented inline
  over the wrapper (iterators/containers don't cross C ABI)
- generic Value container stays internal (unused by consumers) and is
  no longer part of the public umbrella header
- hidden visibility + OAKCORE_BUILD export macro; nm shows zero
  olive::* symbols exported
- install into the platform's standard libdir (GNUInstallDirs);
  Windows DLLs next to the executables, macOS into the app bundle
- TimelineWorkArea::in/out/length now return by value: the wrapped
  TimeRange getters return values, and forwarding them through const
  references dangled (found via RenderWorkerFootageTest crash)
- tests: 9 new pure C ABI test executables (oakcore_*_test) covering
  every public C function; 4 stale legacy core tests removed (they
  targeted a long-renamed API and were never built due to a malformed
  option() that also kept OLIVECORE_BUILD_TESTS off)
- CI/CD: oakcore.dll staged for NSIS, liboakcore.so added to the
  AppImage validation list, build-tree DLL copies on Windows
2026-07-19 23:49:40 +08:00
Mike-Solar 52625f2b98 proxy: restore pdivider (de)serialization dropped in the commit split
The load/save of the per-footage custom divider attribute was lost when
the proxy changes were re-applied during the three-way commit split;
ProxyManager.FootagePersistsCustomProxyParams caught it.
2026-07-19 23:49:14 +08:00
Mike-Solar 7832cb30b3 build: move render worker out of app/ into top-level worker/
- oak-render-worker now builds from worker/ (own CMakeLists.txt) as a
  peer of app/; RenderWorkerPool resolves the new build-tree location
- deduplicated the Linux install() rules for the worker
- worker-spawning tests resolve build/worker instead of build/app
- cd.yml: Windows staging copies the worker from its new output path
2026-07-19 22:12:29 +08:00
Mike-Solar 76f5c2a65b color: input colorspace auto-detection, HDR export tags, LGG/white balance nodes, more LUT formats, waveform parade
- media color primaries/transfer tags now flow from the FFmpeg probe
  through VideoParams into Footage::get_colorspace_to_use(); precedence
  is user override > media tags > project default
- export nclc tags derive from the output colorspace (PQ/HLG/BT.2020,
  P3, sRGB, Rec.601, Rec.709) instead of hardcoded BT.709
- new OCIO Color Grading (Log) node (lift/gamma/gain) and White Balance
  node (kelvin temperature + tint, HDR-safe)
- LUT whitelist extended to 9 OCIO-supported formats
- waveform scope gains an RGB parade mode (GPU and software paths)
- tests updated for the new colorspace precedence
2026-07-19 21:45:51 +08:00
Mike-Solar a7ddc0f114 audio: master-clock playback timing, output clock compensation, buffer config, interpolated speed
- playback timer uses the audio output device as its master clock: the
  PortAudio callback counts consumed frames (including underrun
  zero-fill) so video cannot drift away from what is heard; wall clock
  remains as fallback when no clocked output is running
- output clock compensates for device output latency; new Preferences >
  Audio buffer size setting (0 = auto)
- SampleBuffer::speed() now uses linear interpolation instead of
  nearest-neighbor sampling
- regression tests: audio-clock driven timer (fwd/rev/speed), wall
  clock fallback, interpolation correctness
2026-07-19 21:44:34 +08:00
Mike-Solar 0c02ff0d77 proxy: isolate proxies from export renders and improve proxy workflow
- FootageJob::should_use_proxy() centralizes the proxy decision; worker
  pre-decode now honors the render mode so exports always decode the
  original media (previously every frame was pre-decoded from proxies)
- global Tools > Use Proxy Media toggle with footage invalidation
- ffmpeg -progress parsing for real percentage feedback while generating
- divider mode (1/2, 1/4, 1/8 of source resolution) with UI, proxy
  filename tags and per-footage persistence (pdivider)
- Media Offline warning slat rendered for missing footage
- regression tests: export isolation, relink invalidation, offline slat,
  progress parsing, divider arguments
2026-07-19 21:43:58 +08:00
Mike-Solar bb40b4923e style: unify identifier naming per updated conventions
Automated with clang-tidy readability-identifier-naming (config added to
.clang-tidy) plus scripted passes, per the updated rules now documented
in CONTRIBUTING.md:

- types (class/struct/enum/alias/template params): PascalCase
- functions, variables, members: snake_case (incl. rational -> Rational)
- private/protected members: trailing underscore; static member
  variables likewise (instance_, available_themes_)
- constants and enum values: snake_case (kLinear -> k_linear,
  F32P -> f32p); ALL_CAPS reserved for macros
- macros: OAK_ prefix (OLIVE_ADD_TEST/OLIVE_ASSERT/OLIVE_CONFIG ->
  OAK_ADD_TEST/OAK_ASSERT/OAK_CONFIG, GL_PREAMBLE -> OAK_GL_PREAMBLE,
  include guards -> OAK_*)
- file names: all lowercase (Current/Plugin/OliveHost/OliveClip/
  OlivePluginInstance -> current/plugin/olivehost/oliveclip/
  oliveplugininstance)
- getters share the member name sans underscore, setters set_foo()
- Qt and third-party (OpenFX) virtual overrides and framework callbacks
  keep their original names (exempt in .clang-tidy)

Manual follow-ups required where automation could not reach:
- string-based QMetaObject/SIGNAL/SLOT references updated to renamed
  methods (AddTask, CreatedFile, DeleteSpecificFile, moveSelectionUp, ...)
- macro bodies referencing renamed methods (OLIVE_CONFIG,
  NODE_DEFAULT_DESTRUCTOR, MANAGEDDISPLAYWIDGET_*)
- self-shadowing locals renamed where signals/methods became same-named
  (size_changed, worker_count, selected_items, import param, filters)
- third_party OFX member/namespace usages restored (OFX::Host::*,
  _created, _clipPrefsDirty, createInstance, clearPersistentMessage)
- STL protocol aliases restored (const_iterator) with .clang-tidy
  ignore rules; qHash overloads restored

Full build and test suite pass: ctest 4/4, ~1960 gtest cases green.
2026-07-19 16:10:54 +08:00
Mike-Solar cb1718a103 test: replace fake and duplicate tests with real assertions
Fake tests rewritten to assert real behavior:
- audio_smoke: conversion tests now actually Convert() samples and verify
  output; waveform length/summary assertions tightened to exact values
- plugin_format_conversion: RowBytes/U8ToU16/LoadImageFile now call real
  production code (VideoParams::GetBytesPerPixel, sws scaler, OIIO decode
  of tests/img.png with known pixel values)
- core_color: HSV round trip now verifies fromHsv(toHsv(c)) == c instead
  of comparing toHsv against its own accessors
- core_bezier/node_inputimmediate: expected values replaced with
  independently derived constants instead of re-running the code under test
- common_commandlineparser/common_debug/common_jobtime: capture
  stdout/stderr/qDebug and assert actual output content
- proxy_manager: ProxyFinished test now drives a real proxy job instead of
  emitting the signal itself; proxy_dialog/panel/proxy/preferences/timeruler
  tests assert real widget state
- viewer_smoke/preview_autocacher/render_misc: zero-assertion tests given
  observable-state assertions or removed where nothing is observable

Duplicates removed:
- plugin_smoke_test.cpp: 18 tests duplicated from plugin_paraminstance /
  plugin_support_* / plugin_renderer_readback (751 -> 180 lines)
- module_smoke HumanStrings tests covered precisely by ui_humanstrings_test
- render_misc duplicate kDefaultInterpolation constant check

Removed by policy (skip allowed, never disabled):
- all DISABLED_ prefixes: re-enabled as real offscreen tests or deleted
- ffmpeg_decoder_hw: hardcoded personal path replaced with
  OAK_TEST_HW_DECODE_FILE env var, GTEST_SKIP when unset

Also:
- config_test: restore Config defaults after run (cross-test pollution)
- render_worker_footage: drop /tmp debug-output scaffolding
- previewaudiodevice construction test asserts the real bugfix
2026-07-19 13:58:31 +08:00
Mike-Solar b0aa683499 fix: dangling OpenGL context crash and uninitialized audio frame size
- OpenGLRenderer: hold the viewer-owned QOpenGLContext in a QPointer so
  DestroyInternal() safely skips it when the context has already been
  destroyed by Qt's shared-context lifecycle. Fixes a SIGSEGV when the
  full gtest suite ran MainWindow.ConstructsOffscreenWithPanelsAndMenus
  after earlier viewer tests.
- PreviewAudioDevice: add SetParams() deriving bytes_per_frame from the
  audio format (bytes per sample * channel count) instead of staying 0.
2026-07-19 13:58:06 +08:00
Mike-Solar 842bc7632b update: Update build.md 2026-07-18 20:57:10 +08:00
Mike-Solar 64a250391c about: thank Enzo GD in the About and Welcome dialogs
Enzo GD, administrator of the Olive Facebook user group, gave this
project generous promotional support in its early days. The shared body
text now carries a special-thanks line in both the About dialog and the
first-run Welcome dialog; zh_CN translation included.
2026-07-18 20:13:58 +08:00
Mike-Solar fd93de42ca Merge remote-tracking branch 'origin/main' 2026-07-18 18:27:46 +08:00
Mike-Solar ca68005fc2 ci: switch to GitHub-hosted runners and add daily cache-warming build
- Runners: ubuntu-24.04, macos-15, windows-latest (free tier) instead
  of WarpBuild 8x/12x instances
- openfx-misc parallelism adapts to nproc (GitHub runners have fewer
  vCPUs than the WarpBuild instances)
- Schedule a daily 03:17 UTC run so ccache entries never hit GitHub's
  7-day cache expiry and system dependencies stay current
2026-07-18 14:29:14 +08:00
Mike-Solar 4eb0ef6fc6 change: remove dymatic backend. 2026-07-18 14:20:35 +08:00
Mike-Solar 7206ae8c9f ci: switch all caches to actions/cache (drop WarpBuilds/cache)
WarpBuild's cache service is billed; GitHub's cache is free (10 GB
repo limit with LRU eviction, ample for ccache entries). All three
cache sites — ccache for the matrix and dynamic-backend jobs, plus the
macOS OpenTimelineIO build — now use actions/cache@v4, which also
removes the Windows-specific fallback step.
2026-07-18 14:05:33 +08:00
Mike-Solar f6fc12bb9d ci: use actions/cache for ccache on Windows runners
WarpBuilds/cache does not support Windows; keep it for Linux/macOS and
restore the same keys from GitHub's cache service on Windows instead.
2026-07-18 14:03:58 +08:00
Mike-Solar 988e0e5b44 ci: fix ccache dir path — runner.temp is not a valid expression context
Point CCACHE_DIR and the cache action at a fixed workspace directory
instead; checkout cleans untracked files before the restore, so the
ordering is safe.
2026-07-18 14:02:42 +08:00
Mike-Solar bfce4d916a ci: add ccache compile caching via WarpBuilds/cache, shrink runners to 8x
- Restore ccache with WarpBuilds/cache@v1 on all three OS and the
  dynamic-backend job (per-OS keys with branch fallback), and wire it
  up through CMAKE_C/CXX_COMPILER_LAUNCHER; builds print ccache -s so
  hit rates are visible in CI logs
- Cache the macOS OpenTimelineIO build (keyed on v0.16.0) and skip the
  rebuild on cache hit
- Install ccache via apt/brew/MSYS2 as needed; CCACHE_MAXSIZE=1G
- Runner sizes 16x/32x -> 8x (macOS 12x kept); openfx-misc make -j8 to
  match
2026-07-18 13:57:15 +08:00
Mike-Solar 900120d9ba update: Update README.md 2026-07-17 22:51:46 +08:00
Mike-Solar 1cad5a8fdb update: Update README.md 2026-07-17 22:20:02 +08:00
Mike-Solar e18724129b tests: make ViewerSmokeTimer.DifferentTimebases robust to CI jitter
The test assumed msleep(100) measures close to 100 ms (expecting ~2
frames at 24fps vs ~6 at 60fps). On the loaded macOS CI runner the
24fps leg overslept ~2.5x, producing 6 frames at both rates. Measure
the actual interval per leg and compare against the expected frame
count with a 1-frame tolerance, and use highly distinct timebases
(1fps vs 240fps) for the rate-ordering check.
2026-07-17 22:08:03 +08:00
Mike-Solar dff1f2955d cd: Remove vulkan-headers which is replaced by libvulkan-dev. 2026-07-17 21:51:59 +08:00
Mike-Solar ae908441c8 packaging: ship the Vulkan backend on all platforms, standard lib layout
- Linux CD builds no longer disable Vulkan: the AppImage, deb, rpm and
  Arch packages now build and ship liboakvulkan (the AppImage deploys
  it via linuxdeploy --library so libvulkan is bundled too, and the
  verify step checks both backend libraries); deb/rpm dependencies gain
  libvulkan1/vulkan-loader; the Arch PKGBUILD gains vulkan-headers
- macOS CD installs vulkan-loader and exports VULKAN_SDK so
  find_package(Vulkan) locates the Homebrew loader (previously the
  Vulkan backend silently never built there)
- ffmpeg_bridge now installs to the standard lib directory with
  /../lib RPATH instead of the non-standard ffmpeg_bridge/bin
  layout (verified: editor, worker, oakgl and oakvulkan all resolve it)
- build guides (EN/ZH) document the macOS Vulkan backend dependencies
  and the VULKAN_SDK variable
2026-07-17 21:41:34 +08:00
Mike-Solar 6229381426 version: derive from git tag/commit hash; drop audio playback debug logs
Version is no longer hardcoded: at configure time CMake uses the tag
name when HEAD is exactly on a tag (leading "v" stripped), otherwise
the first 8 hex digits of the commit hash. When git is unavailable
(e.g. source tarball) it falls back to the contents of version.txt,
which is now the single place to bump the release version.

Also remove the temporary qDebug() flood in the audio playback path
(ViewerWidget::QueueNextAudioBuffer / ReceivedAudioBufferForPlayback,
AudioManager::PushToOutput).
2026-07-17 21:10:13 +08:00
Mike-Solar 016341ea7c fix: honor DirectoryIsValid create flag; unbreak Windows CI tests
DirectoryIsValid() ignored try_to_create_if_not_exists and always
called mkpath(). On the Windows CI runner (which may create dirs at
the drive root) PathWidget validation both created bogus directories
and never flagged invalid paths; on Linux the mkpath just failed.

- filefunctions: only mkpath when try_to_create_if_not_exists is set
- TaskProjectLoadTest: Project::set_filename() stores native
  separators on Windows, normalize before comparing paths
- DialogProjectProperties: use a nonexistent file inside a temp dir
  instead of a fixed /definitely/... path that prior tests may have
  partially created on a writable drive
2026-07-17 20:35:25 +08:00
Mike-Solar c734ecdba1 tests: make unknown-icon check robust across Qt builds
QIcon::addFile() differs between Qt versions in whether entries for
nonexistent files keep the icon non-null (Ubuntu CI failure). Assert
on what matters: no usable pixmap or size is produced.
2026-07-17 18:28:38 +08:00
Mike-Solar ff95ffa216 fix: initialize default OCIO config on first use
ColorManager::Init() (run by every Project construction) dereferenced
GetDefaultConfig() unconditionally. Any Project created before
SetUpDefaultConfig() crashed inside OCIO getCanonicalName on a null
config — the Windows CI SEGFAULT, where the suite order runs a
Project-creating test first. Reproduced locally by running
MainWindowLayoutInfo.AccessorsStoreAndRetrieve as the first suite.
2026-07-17 18:28:38 +08:00
Mike-Solar 9ca2b0e716 tests: skip MainWindow construction test where OpenGL is unavailable
The test forces GraphicsBackend=opengl and builds a full MainWindow
with QOpenGLWidget-based viewer panels. On headless platforms whose
QPA cannot provide a GL context (Ubuntu/Windows CI on offscreen), the
app's GL calls crash on a non-current context (SEGFAULT in CI on
both). Probe with QOpenGLContext+QOffscreenSurface and GTEST_SKIP when
GL is unusable; the test still runs on platforms with real GL.
2026-07-17 17:38:15 +08:00
Mike-Solar bbf3b2ca38 tests: include <cstring> for std::memset in oiio utils test
Only compiled on macOS via transitive includes; breaks on Linux/MSVC.
2026-07-17 17:26:52 +08:00
Mike-Solar 004e942cf9 fix: remaining issues recorded during test coverage work
- RenderManager: GPU-side members (context_, decoder_cache_,
  shader_cache_, auto_cacher_, worker_pool_, decoder_clear_timer_) were
  left uninitialized when the configured graphics backend is unknown
  (e.g. dummy); ViewerWidget then dereferenced garbage and crashed.
  Initialize them at declaration
- CurveView::SelectKeyframesOfInput ignored its reference parameter and
  selected keyframes of every connected track; select only the
  requested track's keyframes
- SeekableWidget::SeekToScenePoint dereferenced GetViewerNode()
  unconditionally; skip the playhead update when no viewer is connected
- LoadOTIOTask: unknown root schema leaked the freshly allocated
  project_ (delete + reset; OTIO is not enabled in local builds so this
  file is compile-verified by inspection only)

Locked by new tests: RenderManagerDummyBackend,
TimeRuler.SeekToScenePointWithoutViewerIsNoOp,
CurveViewTest.SelectKeyframesOfInputSelectsOnlyRequestedTrack
2026-07-17 17:20:50 +08:00
Mike-Solar 7223900b6d fix: initialize EncodingParams enum members
format_, video_codec_, audio_codec_, subtitle_sidecar_fmt_ and
subtitles_codec_ were left indeterminate by the constructor; reading
them before the corresponding Enable* call was UB. Initialize to the
kFormatCount/kCodecCount invalid sentinels.
2026-07-17 17:04:39 +08:00
Mike-Solar ba55123b13 tests: coverage for panels, main window, ratio dialog (44 cases)
- all 19 panel classes: construction, titles, context/signal wiring,
  save/load data round-trips
- MainWindowLayoutInfo XML round-trip, MainStatusBar, offscreen
  MainWindow construction with standard panels and menus
- RatioDialog parsing (decimal and : / ; separators), validation
2026-07-17 16:53:13 +08:00
Mike-Solar c812f6ea28 fix: panel/window bugs surfaced by new gtest coverage
- MainWindowLayoutInfo: toXml() iterated open_sequences_ for the
  <viewers> section and fromXml() never parsed it, so open footage
  viewers were lost on layout save/load
- PanelWidget never set QObject::objectName, so
  PanelManager::GetPanelWithName() always returned nullptr and every
  caller (layout restore, NodeView param panel lookup) was dead code
- PanelManager::DestroyInstance() left instance_ dangling (UAF on any
  later RegisterPanel), unlike the other singletons
2026-07-17 16:53:13 +08:00
Mike-Solar 0dae545cba tests: coverage for large widgets (61 cases)
- curve/keyframe views: connections, selection, undo commands
- time ruler / playback controls: time<->scene math, buttons, seek
- project explorer: view model hierarchy, MIME drag&drop, rename undo,
  folder heuristics, toolbar signals
- node table/tree/param views, task view, history, multicam play queue,
  timeline selections, node view scene
2026-07-17 15:48:03 +08:00
Mike-Solar b4db6ebc88 fix: define TimeRuler::SetCenteredText (declared but never defined)
Any caller would hit a link error; there were none, so the bug was
latent until the new widget tests called it.
2026-07-17 15:48:03 +08:00
Mike-Solar e9488904b3 tests: coverage for small utility widgets (66 cases)
- slider: SliderBase/FloatSlider/IntegerSlider mapping, clamping,
  display transforms, drag math, tristate, label substitution
- layouts: FlowLayout wrap math, ColumnedGridLayout placement
- combos: all standard combos, node combo box, color label combo
- misc: menu, file/path fields, toolbar, color button/wheel widgets,
  bezier, resizable scrollbar, hand-movable view, node value tree,
  pixel sampler, collapse button, clickable/focusable labels
2026-07-17 14:13:09 +08:00