obs-testing-guide
npx machina-cli add skill UtakataKyosui/C2Lab/obs-testing-guide --openclawFiles (1)
SKILL.md
6.5 KB
OBS プラグインのテスト・デバッグガイド
テスト環境のセットアップ
必要なツール
# Linux(Ubuntu/Debian)
sudo apt install \
obs-studio \
gdb \
valgrind \
libasan8 \
cmake \
ninja-build
# macOS
brew install obs lldb cmake ninja
# Rust プロジェクトの場合
rustup component add rust-src
cargo install cargo-expand # マクロ展開確認
デバッグビルドの作成
# C/C++ プロジェクト
cmake -B build-debug \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build-debug
# Rust プロジェクト
cargo build # debug ビルド(デフォルト)
blog() でのログ出力
OBS のログシステムを使った基本的なデバッグ手法:
// C/C++: obs-module.h をインクルード
blog(LOG_INFO, "width=%d, height=%d", width, height);
blog(LOG_WARNING, "設定ファイルが見つかりません");
blog(LOG_ERROR, "テクスチャ生成失敗");
blog(LOG_DEBUG, "コールバック呼び出し: %s", __func__);
// Rust: FFI 経由でログ出力
extern "C" {
fn blog(level: i32, format: *const u8, ...);
}
macro_rules! obs_log {
($level:expr, $msg:literal) => {
unsafe { blog($level, concat!($msg, "\0").as_ptr()) }
};
}
obs_log!(300, "プラグインが初期化されました");
ログの確認:
# Linux
tail -f ~/.config/obs-studio/logs/*.txt
# OBS 起動時の詳細ログ
obs --verbose 2>&1 | grep "my-plugin"
C/C++ テストパターン
OBS 依存部分をモックしてユニットテストを実施する。
カスタムテストハーネス
// test/test_utils.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
static int test_pass_count = 0;
static int test_fail_count = 0;
#define ASSERT_EQ(a, b) do { \
if ((a) == (b)) { \
test_pass_count++; \
printf(" PASS: " #a " == " #b "\n"); \
} else { \
test_fail_count++; \
printf(" FAIL: " #a " == " #b " (got %d vs %d)\n", (int)(a), (int)(b)); \
} \
} while(0)
#define ASSERT_NOT_NULL(p) do { \
if ((p) != NULL) { \
test_pass_count++; \
} else { \
test_fail_count++; \
printf(" FAIL: " #p " should not be NULL\n"); \
} \
} while(0)
#define RUN_TEST(fn) do { \
printf("Running: " #fn "\n"); \
fn(); \
} while(0)
#define PRINT_RESULTS() do { \
printf("\n結果: %d passed, %d failed\n", test_pass_count, test_fail_count); \
exit(test_fail_count > 0 ? 1 : 0); \
} while(0)
詳細: references/test-patterns.md
Rust テストパターン
Rust の #[cfg(test)] を使ってビジネスロジックをテスト。
ロジック層と FFI 層の分離
src/
├── lib.rs # FFI エントリポイント(obs_module_load等)
├── source.rs # FFI ラッパー(unsafe extern "C" fn)
└── logic/
├── mod.rs # ロジック層(OBS非依存・テスト可能)
└── processing.rs
// src/logic/processing.rs - OBS 非依存のビジネスロジック
pub struct FrameProcessor {
pub width: u32,
pub height: u32,
pub intensity: f32,
}
impl FrameProcessor {
pub fn new(width: u32, height: u32) -> Self {
Self { width, height, intensity: 1.0 }
}
pub fn apply_grayscale(&self, pixel: [u8; 4]) -> [u8; 4] {
let gray = (pixel[0] as f32 * 0.299
+ pixel[1] as f32 * 0.587
+ pixel[2] as f32 * 0.114) as u8;
[gray, gray, gray, pixel[3]]
}
pub fn pixel_count(&self) -> u64 {
self.width as u64 * self.height as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_processor() {
let proc = FrameProcessor::new(1920, 1080);
assert_eq!(proc.width, 1920);
assert_eq!(proc.height, 1080);
assert_eq!(proc.intensity, 1.0);
}
#[test]
fn test_grayscale_red_pixel() {
let proc = FrameProcessor::new(100, 100);
let red = [255, 0, 0, 255];
let result = proc.apply_grayscale(red);
// R=255 の場合: gray = 255 * 0.299 ≈ 76
assert_eq!(result[0], result[1]);
assert_eq!(result[1], result[2]);
assert_eq!(result[3], 255); // アルファは保持
}
#[test]
fn test_pixel_count() {
let proc = FrameProcessor::new(1920, 1080);
assert_eq!(proc.pixel_count(), 1920 * 1080);
}
}
# テスト実行
cargo test
# 特定のテストのみ
cargo test test_grayscale
# 詳細出力
cargo test -- --nocapture
詳細: references/rust-testing.md
GDB/LLDB デバッグセッション
# GDB(Linux)
gdb --args obs --scene test-scene
(gdb) break my_source_create
(gdb) run
# ブレークポイントで停止したら
(gdb) info locals # ローカル変数表示
(gdb) bt # バックトレース
(gdb) p data->width # 変数の値確認
# LLDB(macOS)
lldb -- obs
(lldb) breakpoint set --name my_source_create
(lldb) run
(lldb) frame variable
Valgrind / AddressSanitizer
Valgrind(メモリリーク)
valgrind --leak-check=full \
--track-origins=yes \
obs 2>&1 | grep "my-plugin"
AddressSanitizer(ASan)
# CMake
cmake -B build-asan \
-DCMAKE_C_FLAGS="-fsanitize=address -g" \
-DCMAKE_BUILD_TYPE=Debug
# Rust
RUSTFLAGS="-Z sanitizer=address" cargo +nightly test \
--target x86_64-unknown-linux-gnu
OBS 内統合テスト
# OBS にテスト用シーンを用意して起動
obs --scene obs-plugin-test.json --verbose
# プラグインのロード確認
grep "my-plugin" ~/.config/obs-studio/logs/*.txt
# 特定シーンで動作確認後、OBS を終了
obs --scene test.json --minimize-to-tray
参考ファイル
references/test-patterns.md- C 言語テストの実装例(モックOBS API)references/rust-testing.md- Rust テスト戦略(cargo test、FFIモック、ロジック分離)
Source
git clone https://github.com/UtakataKyosui/C2Lab/blob/main/plugins/obs-plugin-dev/skills/obs-testing-guide/SKILL.mdView on GitHub Overview
OBS Studioプラグインのテストとデバッグを体系的に解説します。C/C++のユニットテスト、Rustの cargo test、gdb/lldbデバッグ、Valgrindのメモリ検査、blog()ログ出力、AddressSanitizerの使い方までをカバーします。
How This Skill Works
環境セットアップからデバッグビルド作成、テスト実行までの一連の手順を説明します。C/C++とRustのテストパターン、ログ出力の実装例、各種ツールの活用方法を具体的なコード例とともに紹介します。
When to Use It
- C/C++ プラグインのユニットテストを実施したいとき
- Rust 部分を cargo test で検証したいとき
- バグを再現して gdb/lldb でデバッグしたいとき
- メモリ問題を検出するため Valgrind や AddressSanitizer を使いたいとき
- 実行時の挙動を blog ログで追跡して検証したいとき
Quick Start
- Step 1: OS に必要なツールをインストールする(Linux は apt、macOS は brew を使用)
- Step 2: デバッグビルドを作成する。C/C++ は cmake -B build-debug -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON など、Rust は cargo build を実行
- Step 3: blog ログを有効化してログを確認する。OBS のログを tail や --verbose 出力で検証する
Best Practices
- OBS依存部をモックしてユニットテストを実施する
- デバッグビルドを cmake で C/C++、Rust は cargo build で作成する
- ブログログの出力を blog で統一して実行時挙動を検証する
- Valgrind と AddressSanitizer でメモリ関連の問題を検出する
- テストとログの結果を継続的にレビューして品質を改善する
Example Use Cases
- C/C++ のカスタムテストハーネスを使い ASSERT_EQ や ASSERT_NOT_NULL、RUN_TEST、PRINT_RESULTS のマクロでユニットテストを実行する
- Rust の FrameProcessor を対象に #[cfg(test)] テストを作成して新機能を検証する
- Rust 側の FFI 層とロジック層を分離してテストする構成を採用する
- デバッグビルド作成の具体コマンド例として cmake -B build-debug ... を使う
- OBS ログファイルの確認方法として tail -f ~/.config/obs-studio/logs/*.txt や obs --verbose の活用を実践する
Frequently Asked Questions
Add this skill to your agents