0.1.0-0-pre.4
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,8 +1,18 @@
|
|||||||
<!-- file: CHANGELOG.md -->
|
<!-- file: CHANGELOG.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.1.0-0-pre.4 — 2026-09-16
|
||||||
|
|
||||||
|
- validation de `0-pre.3` enregistrée avec suite workspace complète et smokes Desktop propres ;
|
||||||
|
- déplacement des tests unitaires hors `src/` vers `unit_tests/` et formalisation de `tests/` pour intégration/environnement ;
|
||||||
|
- ajout d’un audit empêchant le retour de corps de tests sous `src/` ;
|
||||||
|
- adoption des tests Cargo ciblés par défaut, la suite workspace complète devenant une gate lourde périodique ;
|
||||||
|
- `cargo fmt --all` devient obligatoire avant la gate lorsqu’un delta modifie du Rust, sans incrément d’en-tête pour les seules modifications rustfmt ;
|
||||||
|
- introduction de `game-logging-lib` avec `tracing`, `tracing-subscriber` et `tracing-appender` ;
|
||||||
|
- initialisation du tracing dans les runners Desktop et helper de tracing local pour les tests.
|
||||||
|
|
||||||
## 0.1.0-0-pre.3 — 2026-09-16
|
## 0.1.0-0-pre.3 — 2026-09-16
|
||||||
|
|
||||||
- validation utilisateur de `0.1.0-0-pre.2` avec formatage, audits, check, Clippy strict et tests workspace propres ;
|
- validation utilisateur de `0.1.0-0-pre.2` avec formatage, audits, check, Clippy strict et tests workspace propres ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 4
|
# version: 5
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -11,10 +11,11 @@ members = [
|
|||||||
"crates/games/game-snake-poc",
|
"crates/games/game-snake-poc",
|
||||||
"crates/apps/game-reflex-poc-desktop",
|
"crates/apps/game-reflex-poc-desktop",
|
||||||
"crates/apps/game-snake-poc-desktop",
|
"crates/apps/game-snake-poc-desktop",
|
||||||
|
"crates/common/game-logging-lib",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0-0-pre.3"
|
version = "0.1.0-0-pre.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/games"
|
repository = "https://git.sasedev.com/Sasedev/games"
|
||||||
@@ -22,6 +23,9 @@ authors = ["Sasedev <games@sasedev.com>"]
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
tracing = "0.1.44"
|
||||||
|
tracing-appender = "0.2.5"
|
||||||
|
tracing-subscriber = { version = "0.3.23", features = ["fmt"] }
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
missing_docs = "warn"
|
missing_docs = "warn"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: README.md -->
|
<!-- file: README.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# games.sasedev
|
# games.sasedev
|
||||||
|
|
||||||
@@ -36,3 +36,7 @@ Les deux premiers jeux sont des POC structurels : `game-reflex-poc` et `game-sna
|
|||||||
- [`docs/architecture/001-WORKSPACE_ARCHITECTURE.md`](docs/architecture/001-WORKSPACE_ARCHITECTURE.md)
|
- [`docs/architecture/001-WORKSPACE_ARCHITECTURE.md`](docs/architecture/001-WORKSPACE_ARCHITECTURE.md)
|
||||||
- [`docs/objectives/001-PROJECT_OBJECTIVES.md`](docs/objectives/001-PROJECT_OBJECTIVES.md)
|
- [`docs/objectives/001-PROJECT_OBJECTIVES.md`](docs/objectives/001-PROJECT_OBJECTIVES.md)
|
||||||
- [`docs/games/001-GAME_CLASSIFICATION.md`](docs/games/001-GAME_CLASSIFICATION.md)
|
- [`docs/games/001-GAME_CLASSIFICATION.md`](docs/games/001-GAME_CLASSIFICATION.md)
|
||||||
|
|
||||||
|
## Diagnostics et tests
|
||||||
|
|
||||||
|
Le socle transverse `crates/common/game-logging-lib` fournit le tracing commun. Les tests unitaires résident hors `src/` sous `unit_tests/`; les tests d’intégration/environnement résident sous `tests/`.
|
||||||
|
|||||||
15
ROADMAP.md
15
ROADMAP.md
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ROADMAP.md -->
|
<!-- file: ROADMAP.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# Roadmap
|
# Roadmap
|
||||||
|
|
||||||
@@ -8,12 +8,13 @@
|
|||||||
- [x] `0-pre.1` — squelette du workspace, règles, architecture, versions, assets, Java Android commun/spécifique et deux crates POC.
|
- [x] `0-pre.1` — squelette du workspace, règles, architecture, versions, assets, Java Android commun/spécifique et deux crates POC.
|
||||||
- [x] `0-pre.2` — consolidation documentaire issue de l'architecture de référence, runners Desktop par jeu et politique normative des commandes.
|
- [x] `0-pre.2` — consolidation documentaire issue de l'architecture de référence, runners Desktop par jeu et politique normative des commandes.
|
||||||
- [x] `0-pre.3` — première boucle moteur exécutable Desktop : temps, input abstrait, capacités de monétisation optionnelles et état de jeu minimal via les runners.
|
- [x] `0-pre.3` — première boucle moteur exécutable Desktop : temps, input abstrait, capacités de monétisation optionnelles et état de jeu minimal via les runners.
|
||||||
- [ ] `0-pre.4` — intégration SDL3 Desktop réelle et premier rendu POC Reflex.
|
- [x] `0-pre.4` — architecture de tests hors `src/`, stratégie de tests ciblés et socle `tracing`/`tracing-subscriber`/`tracing-appender`.
|
||||||
- [ ] `0-pre.5` — projet Gradle Android exécutable, SDL3 AAR/NDK, compilation Rust `cdylib`, lancement sur appareil/émulateur.
|
- [ ] `0-pre.5` — intégration SDL3 Desktop réelle et premier rendu POC Reflex.
|
||||||
- [ ] `0-pre.6` — bridge Java/JNI minimal et input tactile Android.
|
- [ ] `0-pre.6` — projet Gradle Android exécutable, SDL3 AAR/NDK, compilation Rust `cdylib`, lancement sur appareil/émulateur.
|
||||||
- [ ] `0-pre.7` — assets communs + spécifiques empaquetés sans copie dans les crates.
|
- [ ] `0-pre.7` — bridge Java/JNI minimal et input tactile Android.
|
||||||
- [ ] `0-pre.8` — POC Reflex jouable Desktop + Android.
|
- [ ] `0-pre.8` — assets communs + spécifiques empaquetés sans copie dans les crates.
|
||||||
- [ ] `0-pre.9` — POC Snake jouable et validation de la réutilisation du moteur.
|
- [ ] `0-pre.9` — POC Reflex jouable Desktop + Android.
|
||||||
|
- [ ] `0-pre.10` — POC Snake jouable et validation de la réutilisation du moteur.
|
||||||
- [ ] `1-alpha.1` — première API moteur V1 volontairement stabilisée.
|
- [ ] `1-alpha.1` — première API moteur V1 volontairement stabilisée.
|
||||||
- [ ] `2-beta.1` — stabilisation, packaging, tests multi-appareils.
|
- [ ] `2-beta.1` — stabilisation, packaging, tests multi-appareils.
|
||||||
- [ ] `3-rc.1` — candidat de release du socle 0.1.0.
|
- [ ] `3-rc.1` — candidat de release du socle 0.1.0.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: crates/apps/game-reflex-poc-desktop/Cargo.toml
|
# file: crates/apps/game-reflex-poc-desktop/Cargo.toml
|
||||||
# version: 2
|
# version: 3
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "game-reflex-poc-desktop"
|
name = "game-reflex-poc-desktop"
|
||||||
@@ -11,6 +11,8 @@ authors.workspace = true
|
|||||||
publish.workspace = true
|
publish.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
game-logging-lib = { path = "../../common/game-logging-lib" }
|
||||||
|
tracing.workspace = true
|
||||||
engine-v1-common = { path = "../../engines/engine-v1-common" }
|
engine-v1-common = { path = "../../engines/engine-v1-common" }
|
||||||
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
||||||
game-reflex-poc = { path = "../../games/game-reflex-poc" }
|
game-reflex-poc = { path = "../../games/game-reflex-poc" }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/apps/game-reflex-poc-desktop/src/main.rs
|
// file: crates/apps/game-reflex-poc-desktop/src/main.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
@@ -10,6 +10,14 @@
|
|||||||
//! This binary exercises the platform-independent fixed-step loop. SDL3 windowing and physical input mapping are introduced in the next dedicated delta.
|
//! This binary exercises the platform-independent fixed-step loop. SDL3 windowing and physical input mapping are introduced in the next dedicated delta.
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let _logging_guard = match game_logging_lib::init_console_tracing() {
|
||||||
|
std::result::Result::Ok(guard) => guard,
|
||||||
|
std::result::Result::Err(error) => {
|
||||||
|
eprintln!("failed to initialize tracing: {error}");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
tracing::info!(target: "games::runner", game = "reflex", "desktop POC runner started");
|
||||||
let _monetization = engine_v1_platform_api::MonetizationCapabilities::disabled();
|
let _monetization = engine_v1_platform_api::MonetizationCapabilities::disabled();
|
||||||
let input = engine_v1_common::InputState::none().with_action(engine_v1_common::GameAction::Primary, true);
|
let input = engine_v1_common::InputState::none().with_action(engine_v1_common::GameAction::Primary, true);
|
||||||
let mut runner = engine_v1_common::FixedStepRunner::new(std::time::Duration::from_millis(16));
|
let mut runner = engine_v1_common::FixedStepRunner::new(std::time::Duration::from_millis(16));
|
||||||
@@ -17,6 +25,7 @@ fn main() {
|
|||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
runner.tick(&mut state, input);
|
runner.tick(&mut state, input);
|
||||||
}
|
}
|
||||||
|
tracing::info!(target: "games::runner", game = "reflex", frames = runner.next_frame_index(), "desktop POC runner completed");
|
||||||
println!("Reflex POC desktop runner: score={} frames={}", state.score(), runner.next_frame_index());
|
println!("Reflex POC desktop runner: score={} frames={}", state.score(), runner.next_frame_index());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: crates/apps/game-snake-poc-desktop/Cargo.toml
|
# file: crates/apps/game-snake-poc-desktop/Cargo.toml
|
||||||
# version: 2
|
# version: 3
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "game-snake-poc-desktop"
|
name = "game-snake-poc-desktop"
|
||||||
@@ -11,6 +11,8 @@ authors.workspace = true
|
|||||||
publish.workspace = true
|
publish.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
game-logging-lib = { path = "../../common/game-logging-lib" }
|
||||||
|
tracing.workspace = true
|
||||||
engine-v1-common = { path = "../../engines/engine-v1-common" }
|
engine-v1-common = { path = "../../engines/engine-v1-common" }
|
||||||
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
||||||
game-snake-poc = { path = "../../games/game-snake-poc" }
|
game-snake-poc = { path = "../../games/game-snake-poc" }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/apps/game-snake-poc-desktop/src/main.rs
|
// file: crates/apps/game-snake-poc-desktop/src/main.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
@@ -10,6 +10,14 @@
|
|||||||
//! This binary exercises the platform-independent fixed-step loop. SDL3 windowing and physical input mapping are introduced in the next dedicated delta.
|
//! This binary exercises the platform-independent fixed-step loop. SDL3 windowing and physical input mapping are introduced in the next dedicated delta.
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let _logging_guard = match game_logging_lib::init_console_tracing() {
|
||||||
|
std::result::Result::Ok(guard) => guard,
|
||||||
|
std::result::Result::Err(error) => {
|
||||||
|
eprintln!("failed to initialize tracing: {error}");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
tracing::info!(target: "games::runner", game = "snake", "desktop POC runner started");
|
||||||
let _monetization = engine_v1_platform_api::MonetizationCapabilities::disabled();
|
let _monetization = engine_v1_platform_api::MonetizationCapabilities::disabled();
|
||||||
let input = engine_v1_common::InputState::none().with_action(engine_v1_common::GameAction::Primary, true);
|
let input = engine_v1_common::InputState::none().with_action(engine_v1_common::GameAction::Primary, true);
|
||||||
let mut runner = engine_v1_common::FixedStepRunner::new(std::time::Duration::from_millis(16));
|
let mut runner = engine_v1_common::FixedStepRunner::new(std::time::Duration::from_millis(16));
|
||||||
@@ -17,6 +25,7 @@ fn main() {
|
|||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
runner.tick(&mut state, input);
|
runner.tick(&mut state, input);
|
||||||
}
|
}
|
||||||
|
tracing::info!(target: "games::runner", game = "snake", frames = runner.next_frame_index(), "desktop POC runner completed");
|
||||||
println!("Snake POC desktop runner: length={} frames={}", state.length(), runner.next_frame_index());
|
println!("Snake POC desktop runner: length={} frames={}", state.length(), runner.next_frame_index());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
19
crates/common/game-logging-lib/Cargo.toml
Normal file
19
crates/common/game-logging-lib/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# file: crates/common/game-logging-lib/Cargo.toml
|
||||||
|
# version: 1
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "game-logging-lib"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tracing.workspace = true
|
||||||
|
tracing-appender.workspace = true
|
||||||
|
tracing-subscriber.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
20
crates/common/game-logging-lib/src/lib.rs
Normal file
20
crates/common/game-logging-lib/src/lib.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// file: crates/common/game-logging-lib/src/lib.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![deny(unreachable_pub)]
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
|
//! Shared tracing initialization and test diagnostics for games.sasedev applications and crates.
|
||||||
|
|
||||||
|
mod runtime;
|
||||||
|
mod test_support;
|
||||||
|
|
||||||
|
/// Re-export of the console tracing initialization error.
|
||||||
|
pub use self::runtime::LoggingInitError;
|
||||||
|
/// Re-export of the guard keeping the non-blocking tracing writer alive.
|
||||||
|
pub use self::runtime::LoggingWorkerGuard;
|
||||||
|
/// Re-export of the standard console tracing initializer.
|
||||||
|
pub use self::runtime::init_console_tracing;
|
||||||
|
/// Re-export of the scoped tracing helper intended for tests.
|
||||||
|
pub use self::test_support::with_test_tracing;
|
||||||
31
crates/common/game-logging-lib/src/runtime.rs
Normal file
31
crates/common/game-logging-lib/src/runtime.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// file: crates/common/game-logging-lib/src/runtime.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
/// Error returned when the process-global tracing subscriber is already configured.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct LoggingInitError;
|
||||||
|
|
||||||
|
impl std::fmt::Display for LoggingInitError {
|
||||||
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
return formatter.write_str("the process-global tracing subscriber is already configured");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for LoggingInitError {}
|
||||||
|
|
||||||
|
/// Guard keeping the non-blocking tracing writer alive for the application lifetime.
|
||||||
|
pub struct LoggingWorkerGuard {
|
||||||
|
_worker_guard: tracing_appender::non_blocking::WorkerGuard,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes a process-global non-blocking tracing subscriber writing formatted events to standard error.
|
||||||
|
///
|
||||||
|
/// The returned guard must remain alive for as long as events may still be emitted.
|
||||||
|
pub fn init_console_tracing() -> std::result::Result<LoggingWorkerGuard, LoggingInitError> {
|
||||||
|
let (writer, worker_guard) = tracing_appender::non_blocking(std::io::stderr());
|
||||||
|
let subscriber = tracing_subscriber::fmt().with_target(true).with_writer(writer).finish();
|
||||||
|
if tracing::subscriber::set_global_default(subscriber).is_err() {
|
||||||
|
return std::result::Result::Err(LoggingInitError);
|
||||||
|
}
|
||||||
|
return std::result::Result::Ok(LoggingWorkerGuard { _worker_guard: worker_guard });
|
||||||
|
}
|
||||||
13
crates/common/game-logging-lib/src/test_support.rs
Normal file
13
crates/common/game-logging-lib/src/test_support.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// file: crates/common/game-logging-lib/src/test_support.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
/// Executes one test body with a thread-local tracing subscriber configured for the Rust test writer.
|
||||||
|
pub fn with_test_tracing<T>(test_name: &str, operation: impl FnOnce() -> T) -> T {
|
||||||
|
let subscriber = tracing_subscriber::fmt().with_target(true).with_test_writer().without_time().finish();
|
||||||
|
return tracing::subscriber::with_default(subscriber, || {
|
||||||
|
tracing::debug!(test_name = test_name, "test started");
|
||||||
|
let result = operation();
|
||||||
|
tracing::debug!(test_name = test_name, "test completed");
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
11
crates/common/game-logging-lib/tests/test_support.rs
Normal file
11
crates/common/game-logging-lib/tests/test_support.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// file: crates/common/game-logging-lib/tests/test_support.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scoped_test_tracing_returns_test_result() {
|
||||||
|
let result = game_logging_lib::with_test_tracing("scoped_test_tracing_returns_test_result", || {
|
||||||
|
tracing::info!(value = 41_u32, "integration test event");
|
||||||
|
return 42_u32;
|
||||||
|
});
|
||||||
|
assert_eq!(result, 42_u32);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: crates/engines/engine-v1-common/Cargo.toml
|
# file: crates/engines/engine-v1-common/Cargo.toml
|
||||||
# version: 1
|
# version: 2
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "engine-v1-common"
|
name = "engine-v1-common"
|
||||||
@@ -10,5 +10,8 @@ repository.workspace = true
|
|||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
publish.workspace = true
|
publish.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
game-logging-lib = { path = "../../common/game-logging-lib" }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/engines/engine-v1-common/src/game_loop.rs
|
// file: crates/engines/engine-v1-common/src/game_loop.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
/// Minimal update contract implemented by a game state consumed by engine V1.
|
/// Minimal update contract implemented by a game state consumed by engine V1.
|
||||||
pub trait EngineGame {
|
pub trait EngineGame {
|
||||||
@@ -54,30 +54,5 @@ impl FixedStepRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod unit_tests {
|
#[path = "../unit_tests/game_loop.rs"]
|
||||||
#[derive(Default)]
|
mod tests;
|
||||||
struct CountingGame {
|
|
||||||
updates: u64,
|
|
||||||
last_frame: Option<crate::EngineFrame>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::EngineGame for CountingGame {
|
|
||||||
fn update(&mut self, frame: crate::EngineFrame, _input: crate::InputState) {
|
|
||||||
self.updates = self.updates.saturating_add(1);
|
|
||||||
self.last_frame = Some(frame);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn fixed_step_runner_advances_index_and_elapsed_time() {
|
|
||||||
let mut game = CountingGame::default();
|
|
||||||
let mut runner = crate::FixedStepRunner::new(std::time::Duration::from_millis(16));
|
|
||||||
runner.tick(&mut game, crate::InputState::none());
|
|
||||||
runner.tick(&mut game, crate::InputState::none());
|
|
||||||
assert_eq!(game.updates, 2);
|
|
||||||
assert_eq!(runner.next_frame_index(), 2);
|
|
||||||
assert_eq!(runner.elapsed(), std::time::Duration::from_millis(32));
|
|
||||||
assert_eq!(game.last_frame.map(crate::EngineFrame::index), Some(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/engines/engine-v1-common/src/input.rs
|
// file: crates/engines/engine-v1-common/src/input.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
/// Platform-independent snapshot of logical input actions for one update.
|
/// Platform-independent snapshot of logical input actions for one update.
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
@@ -51,11 +51,5 @@ impl InputState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod unit_tests {
|
#[path = "../unit_tests/input.rs"]
|
||||||
#[test]
|
mod tests;
|
||||||
fn action_state_is_independent() {
|
|
||||||
let state = crate::InputState::none().with_action(crate::GameAction::Primary, true);
|
|
||||||
assert!(state.is_active(crate::GameAction::Primary));
|
|
||||||
assert!(!state.is_active(crate::GameAction::Secondary));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
30
crates/engines/engine-v1-common/unit_tests/game_loop.rs
Normal file
30
crates/engines/engine-v1-common/unit_tests/game_loop.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// file: crates/engines/engine-v1-common/unit_tests/game_loop.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct CountingGame {
|
||||||
|
updates: u64,
|
||||||
|
last_frame: std::option::Option<crate::EngineFrame>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::EngineGame for CountingGame {
|
||||||
|
fn update(&mut self, frame: crate::EngineFrame, _input: crate::InputState) {
|
||||||
|
self.updates = self.updates.saturating_add(1);
|
||||||
|
self.last_frame = std::option::Option::Some(frame);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fixed_step_runner_advances_index_and_elapsed_time() {
|
||||||
|
game_logging_lib::with_test_tracing("fixed_step_runner_advances_index_and_elapsed_time", || {
|
||||||
|
let mut game = CountingGame::default();
|
||||||
|
let mut runner = crate::FixedStepRunner::new(std::time::Duration::from_millis(16));
|
||||||
|
runner.tick(&mut game, crate::InputState::none());
|
||||||
|
runner.tick(&mut game, crate::InputState::none());
|
||||||
|
assert_eq!(game.updates, 2);
|
||||||
|
assert_eq!(runner.next_frame_index(), 2);
|
||||||
|
assert_eq!(runner.elapsed(), std::time::Duration::from_millis(32));
|
||||||
|
assert_eq!(game.last_frame.map(crate::EngineFrame::index), std::option::Option::Some(1));
|
||||||
|
});
|
||||||
|
}
|
||||||
12
crates/engines/engine-v1-common/unit_tests/input.rs
Normal file
12
crates/engines/engine-v1-common/unit_tests/input.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// file: crates/engines/engine-v1-common/unit_tests/input.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn action_state_is_independent() {
|
||||||
|
game_logging_lib::with_test_tracing("action_state_is_independent", || {
|
||||||
|
let input = crate::InputState::none().with_action(crate::GameAction::Primary, true);
|
||||||
|
assert!(input.is_active(crate::GameAction::Primary));
|
||||||
|
assert!(!input.is_active(crate::GameAction::Secondary));
|
||||||
|
assert!(!input.is_active(crate::GameAction::Left));
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: crates/engines/engine-v1-platform-api/Cargo.toml
|
# file: crates/engines/engine-v1-platform-api/Cargo.toml
|
||||||
# version: 1
|
# version: 2
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "engine-v1-platform-api"
|
name = "engine-v1-platform-api"
|
||||||
@@ -10,5 +10,8 @@ repository.workspace = true
|
|||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
publish.workspace = true
|
publish.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
game-logging-lib = { path = "../../common/game-logging-lib" }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/engines/engine-v1-platform-api/src/service.rs
|
// file: crates/engines/engine-v1-platform-api/src/service.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
/// Platform capabilities that may have different implementations per target.
|
/// Platform capabilities that may have different implementations per target.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
@@ -61,11 +61,5 @@ impl MonetizationCapabilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod unit_tests {
|
#[path = "../unit_tests/service.rs"]
|
||||||
#[test]
|
mod tests;
|
||||||
fn disabled_monetization_disables_both_services() {
|
|
||||||
let capabilities = crate::MonetizationCapabilities::disabled();
|
|
||||||
assert_eq!(capabilities.advertising(), crate::PlatformServiceAvailability::Disabled);
|
|
||||||
assert_eq!(capabilities.billing(), crate::PlatformServiceAvailability::Disabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
11
crates/engines/engine-v1-platform-api/unit_tests/service.rs
Normal file
11
crates/engines/engine-v1-platform-api/unit_tests/service.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// file: crates/engines/engine-v1-platform-api/unit_tests/service.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn disabled_monetization_disables_both_services() {
|
||||||
|
game_logging_lib::with_test_tracing("disabled_monetization_disables_both_services", || {
|
||||||
|
let capabilities = crate::MonetizationCapabilities::disabled();
|
||||||
|
assert_eq!(capabilities.advertising(), crate::PlatformServiceAvailability::Disabled);
|
||||||
|
assert_eq!(capabilities.billing(), crate::PlatformServiceAvailability::Disabled);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: crates/games/game-reflex-poc/Cargo.toml
|
# file: crates/games/game-reflex-poc/Cargo.toml
|
||||||
# version: 1
|
# version: 2
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "game-reflex-poc"
|
name = "game-reflex-poc"
|
||||||
@@ -15,5 +15,8 @@ engine-v1-common = { path = "../../engines/engine-v1-common" }
|
|||||||
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
||||||
engine-v1-sdl = { path = "../../engines/engine-v1-sdl" }
|
engine-v1-sdl = { path = "../../engines/engine-v1-sdl" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
game-logging-lib = { path = "../../common/game-logging-lib" }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/games/game-reflex-poc/src/state.rs
|
// file: crates/games/game-reflex-poc/src/state.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
/// Minimal deterministic state used to validate the first game boundary.
|
/// Minimal deterministic state used to validate the first game boundary.
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
@@ -36,23 +36,5 @@ impl engine_v1_common::EngineGame for ReflexState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod unit_tests {
|
#[path = "../unit_tests/state.rs"]
|
||||||
#[test]
|
mod tests;
|
||||||
fn success_increments_score() {
|
|
||||||
let mut state = crate::ReflexState::new();
|
|
||||||
state.register_success();
|
|
||||||
assert_eq!(state.score(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn engine_update_maps_primary_action_to_success() {
|
|
||||||
let mut state = crate::ReflexState::new();
|
|
||||||
let input = engine_v1_common::InputState::none().with_action(engine_v1_common::GameAction::Primary, true);
|
|
||||||
engine_v1_common::EngineGame::update(
|
|
||||||
&mut state,
|
|
||||||
engine_v1_common::EngineFrame::new(0, std::time::Duration::from_millis(16), std::time::Duration::ZERO),
|
|
||||||
input,
|
|
||||||
);
|
|
||||||
assert_eq!(state.score(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
25
crates/games/game-reflex-poc/unit_tests/state.rs
Normal file
25
crates/games/game-reflex-poc/unit_tests/state.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// file: crates/games/game-reflex-poc/unit_tests/state.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn success_increments_score() {
|
||||||
|
game_logging_lib::with_test_tracing("success_increments_score", || {
|
||||||
|
let mut state = crate::ReflexState::new();
|
||||||
|
state.register_success();
|
||||||
|
assert_eq!(state.score(), 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn engine_update_maps_primary_action_to_success() {
|
||||||
|
game_logging_lib::with_test_tracing("engine_update_maps_primary_action_to_success", || {
|
||||||
|
let mut state = crate::ReflexState::new();
|
||||||
|
let input = engine_v1_common::InputState::none().with_action(engine_v1_common::GameAction::Primary, true);
|
||||||
|
engine_v1_common::EngineGame::update(
|
||||||
|
&mut state,
|
||||||
|
engine_v1_common::EngineFrame::new(0, std::time::Duration::from_millis(16), std::time::Duration::ZERO),
|
||||||
|
input,
|
||||||
|
);
|
||||||
|
assert_eq!(state.score(), 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: crates/games/game-snake-poc/Cargo.toml
|
# file: crates/games/game-snake-poc/Cargo.toml
|
||||||
# version: 1
|
# version: 2
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "game-snake-poc"
|
name = "game-snake-poc"
|
||||||
@@ -15,5 +15,8 @@ engine-v1-common = { path = "../../engines/engine-v1-common" }
|
|||||||
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
||||||
engine-v1-sdl = { path = "../../engines/engine-v1-sdl" }
|
engine-v1-sdl = { path = "../../engines/engine-v1-sdl" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
game-logging-lib = { path = "../../common/game-logging-lib" }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/games/game-snake-poc/src/state.rs
|
// file: crates/games/game-snake-poc/src/state.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
/// Minimal Snake state used to validate reusable engine dependencies.
|
/// Minimal Snake state used to validate reusable engine dependencies.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
@@ -42,23 +42,5 @@ impl engine_v1_common::EngineGame for SnakeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod unit_tests {
|
#[path = "../unit_tests/state.rs"]
|
||||||
#[test]
|
mod tests;
|
||||||
fn growth_increments_length() {
|
|
||||||
let mut state = crate::SnakeState::new();
|
|
||||||
state.grow();
|
|
||||||
assert_eq!(state.length(), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn engine_update_maps_primary_action_to_growth() {
|
|
||||||
let mut state = crate::SnakeState::new();
|
|
||||||
let input = engine_v1_common::InputState::none().with_action(engine_v1_common::GameAction::Primary, true);
|
|
||||||
engine_v1_common::EngineGame::update(
|
|
||||||
&mut state,
|
|
||||||
engine_v1_common::EngineFrame::new(0, std::time::Duration::from_millis(16), std::time::Duration::ZERO),
|
|
||||||
input,
|
|
||||||
);
|
|
||||||
assert_eq!(state.length(), 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
25
crates/games/game-snake-poc/unit_tests/state.rs
Normal file
25
crates/games/game-snake-poc/unit_tests/state.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// file: crates/games/game-snake-poc/unit_tests/state.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn growth_increments_length() {
|
||||||
|
game_logging_lib::with_test_tracing("growth_increments_length", || {
|
||||||
|
let mut state = crate::SnakeState::new();
|
||||||
|
state.grow();
|
||||||
|
assert_eq!(state.length(), 2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn engine_update_maps_primary_action_to_growth() {
|
||||||
|
game_logging_lib::with_test_tracing("engine_update_maps_primary_action_to_growth", || {
|
||||||
|
let mut state = crate::SnakeState::new();
|
||||||
|
let input = engine_v1_common::InputState::none().with_action(engine_v1_common::GameAction::Primary, true);
|
||||||
|
engine_v1_common::EngineGame::update(
|
||||||
|
&mut state,
|
||||||
|
engine_v1_common::EngineFrame::new(0, std::time::Duration::from_millis(16), std::time::Duration::ZERO),
|
||||||
|
input,
|
||||||
|
);
|
||||||
|
assert_eq!(state.length(), 2);
|
||||||
|
});
|
||||||
|
}
|
||||||
127
deltas/0.1.0/0-pre.4.md
Normal file
127
deltas/0.1.0/0-pre.4.md
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<!-- file: deltas/0.1.0/0-pre.4.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.1.0-0-pre.4
|
||||||
|
|
||||||
|
## Base
|
||||||
|
|
||||||
|
Base déclarée : `0.1.0-0-pre.3` validée par l'utilisateur le 2026-09-16.
|
||||||
|
|
||||||
|
## Validation du delta précédent
|
||||||
|
|
||||||
|
L'utilisateur a exécuté :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt --all
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Android deltas
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
cargo run -p game-reflex-poc-desktop
|
||||||
|
cargo run -p game-snake-poc-desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
Résultat communiqué : toutes les gates sont propres. Les tests workspace ont validé les deux tests `engine-v1-common`, le test `engine-v1-platform-api`, les deux tests Reflex et les deux tests Snake. Les smokes ont produit :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Reflex POC desktop runner: score=3 frames=3
|
||||||
|
Snake POC desktop runner: length=4 frames=3
|
||||||
|
```
|
||||||
|
|
||||||
|
`0.1.0-0-pre.3` est donc accepté comme base de ce delta.
|
||||||
|
|
||||||
|
## Objectifs
|
||||||
|
|
||||||
|
- sortir tous les corps de tests unitaires de `src/` selon le modèle KSP ;
|
||||||
|
- réserver `unit_tests/` aux tests unitaires rattachés aux modules et `tests/` aux tests d'intégration/environnement/smoke ;
|
||||||
|
- rendre cette séparation mécaniquement vérifiable ;
|
||||||
|
- rendre `cargo fmt --all` obligatoire lorsqu'un delta a modifié du Rust, avant la gate `--check` ;
|
||||||
|
- remplacer les tests workspace systématiques par une sélection ciblée par défaut ;
|
||||||
|
- introduire le socle de diagnostics structurés fondé sur `tracing`, `tracing-subscriber` et `tracing-appender` ;
|
||||||
|
- utiliser ce socle dans les runners et dans les tests sans initialisations globales concurrentes.
|
||||||
|
|
||||||
|
## Tests hors `src/`
|
||||||
|
|
||||||
|
Les tests unitaires existants sont déplacés vers les répertoires `unit_tests/` des crates concernées. Les modules de production utilisent désormais le pattern :
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "../unit_tests/module.rs"]
|
||||||
|
mod tests;
|
||||||
|
```
|
||||||
|
|
||||||
|
Le nouvel audit projet refuse un corps `#[test]` sous `src/` et exige qu'un `#[cfg(test)]` de production référence un fichier externe sous `unit_tests/`.
|
||||||
|
|
||||||
|
## Tracing
|
||||||
|
|
||||||
|
La nouvelle crate `crates/common/game-logging-lib` fournit :
|
||||||
|
|
||||||
|
- `init_console_tracing()` pour les applications ;
|
||||||
|
- un writer non bloquant via `tracing-appender` ;
|
||||||
|
- `LoggingWorkerGuard` pour conserver le worker vivant ;
|
||||||
|
- `with_test_tracing()` pour installer un subscriber local au test sans conflit de subscriber global.
|
||||||
|
|
||||||
|
Les deux runners Desktop initialisent désormais le tracing et les tests unitaires utilisent le helper de test commun.
|
||||||
|
|
||||||
|
## Politique des tests
|
||||||
|
|
||||||
|
La suite suivante n'est plus requise à chaque delta :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
Elle reste requise au démarrage d'une nouvelle version `X.Y.Z`, à la fin d'une phase/version, après un changement transversal important ou lorsqu'il existe un doute sur la portée des tests ciblés.
|
||||||
|
|
||||||
|
## Validation exécutée pendant la préparation
|
||||||
|
|
||||||
|
Les audits Python et contrôles de structure disponibles dans l'environnement de génération sont exécutés. Les commandes Cargo finales restent exécutées par l'utilisateur.
|
||||||
|
|
||||||
|
## Commandes de validation à exécuter par l'utilisateur
|
||||||
|
|
||||||
|
Le delta modifie du Rust ; commencer obligatoirement par le formatage mutatif puis sa vérification :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt --all
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Android deltas
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests ciblés requis pour ce delta :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test -p game-logging-lib --all-targets --all-features
|
||||||
|
cargo test -p engine-v1-common --all-targets --all-features
|
||||||
|
cargo test -p engine-v1-platform-api --all-targets --all-features
|
||||||
|
cargo test -p game-reflex-poc --all-targets --all-features
|
||||||
|
cargo test -p game-snake-poc --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
La suite workspace complète n'est pas demandée pour `0-pre.4`, car elle vient d'être exécutée proprement sur `0-pre.3` et l'impact de ce delta est borné par les tests ciblés ci-dessus.
|
||||||
|
|
||||||
|
Les runners ont changé pour initialiser le tracing ; rejouer également :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run -p game-reflex-poc-desktop
|
||||||
|
cargo run -p game-snake-poc-desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
Les lignes fonctionnelles finales attendues restent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Reflex POC desktop runner: score=3 frames=3
|
||||||
|
Snake POC desktop runner: length=4 frames=3
|
||||||
|
```
|
||||||
|
|
||||||
|
Des événements `tracing` supplémentaires peuvent apparaître autour de ces lignes.
|
||||||
|
|
||||||
|
## Règle de transition
|
||||||
|
|
||||||
|
Si les validations demandées sont propres, `0.1.0-0-pre.4` est accepté et le travail passe automatiquement à `0.1.0-0-pre.5`, consacré à l'intégration SDL3 Desktop réelle.
|
||||||
|
|
||||||
|
Si une validation échoue, ne pas passer à `0-pre.5` : produire `0.1.0-0-pre.4.fix.1`, puis incrémenter le suffixe de fix si nécessaire.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/000-README.md -->
|
<!-- file: docs/000-README.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# Documentation games.sasedev
|
# Documentation games.sasedev
|
||||||
|
|
||||||
@@ -44,3 +44,8 @@ Voir [`../RULES.md`](../RULES.md), notamment [`rules/RULES_COMMANDS.md`](rules/R
|
|||||||
- [`validation/001-VALIDATION_GATES.md`](validation/001-VALIDATION_GATES.md) — gates manuelles du workspace.
|
- [`validation/001-VALIDATION_GATES.md`](validation/001-VALIDATION_GATES.md) — gates manuelles du workspace.
|
||||||
|
|
||||||
- [`development/002-DESKTOP_DISTRIBUTIONS.md`](development/002-DESKTOP_DISTRIBUTIONS.md) — distributions Desktop natives et variante Tauri optionnelle.
|
- [`development/002-DESKTOP_DISTRIBUTIONS.md`](development/002-DESKTOP_DISTRIBUTIONS.md) — distributions Desktop natives et variante Tauri optionnelle.
|
||||||
|
## Tests et diagnostics
|
||||||
|
|
||||||
|
- [`testing/001-TEST_ARCHITECTURE.md`](testing/001-TEST_ARCHITECTURE.md) — séparation `unit_tests/` / `tests/`, tests ciblés et tracing de test.
|
||||||
|
- [`development/003-TRACING_AND_DIAGNOSTICS.md`](development/003-TRACING_AND_DIAGNOSTICS.md) — socle `tracing`, subscriber et appender communs.
|
||||||
|
|
||||||
|
|||||||
23
docs/development/003-TRACING_AND_DIAGNOSTICS.md
Normal file
23
docs/development/003-TRACING_AND_DIAGNOSTICS.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<!-- file: docs/development/003-TRACING_AND_DIAGNOSTICS.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Tracing et diagnostics
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
|
||||||
|
Le projet utilise `tracing` comme façade structurée commune pour les événements de diagnostic Rust. Les applications choisissent le subscriber adapté à leur distribution ; les librairies émettent des événements sans imposer une destination globale.
|
||||||
|
|
||||||
|
## Socle initial
|
||||||
|
|
||||||
|
`crates/common/game-logging-lib` regroupe l’initialisation réutilisable. La première implémentation fournit :
|
||||||
|
|
||||||
|
- un subscriber console formaté ;
|
||||||
|
- une écriture non bloquante via `tracing-appender` ;
|
||||||
|
- un guard dont la durée de vie protège le flush des événements ;
|
||||||
|
- un subscriber local destiné aux tests.
|
||||||
|
|
||||||
|
Les runners Desktop initialisent ce socle. Les futures intégrations Android pourront adapter la destination aux contraintes Logcat/plateforme sans modifier les crates jeu.
|
||||||
|
|
||||||
|
## Évolution
|
||||||
|
|
||||||
|
Les filtres par target/niveau, fichiers rotatifs, configuration utilisateur, traces Android et éventuelle télémétrie distante seront ajoutés uniquement lorsqu’un besoin concret les justifie.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/FILE_CONTRACTS.md -->
|
<!-- file: docs/rules/FILE_CONTRACTS.md -->
|
||||||
<!-- version: 2 -->
|
<!-- version: 3 -->
|
||||||
|
|
||||||
# Contrats des fichiers principaux
|
# Contrats des fichiers principaux
|
||||||
|
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
- `docs/monetization/` décrit les modèles de monétisation indépendamment des fournisseurs.
|
- `docs/monetization/` décrit les modèles de monétisation indépendamment des fournisseurs.
|
||||||
- `docs/services/` décrit leaderboards, backend, partage, anti-cheat et services communautaires.
|
- `docs/services/` décrit leaderboards, backend, partage, anti-cheat et services communautaires.
|
||||||
- `docs/development/` décrit les workflows de développement non normatifs complémentaires aux règles.
|
- `docs/development/` décrit les workflows de développement non normatifs complémentaires aux règles.
|
||||||
|
- `docs/testing/` décrit l’architecture des tests, leur placement et les stratégies de sélection.
|
||||||
|
- `crates/common/` contient les bibliothèques transverses indépendantes d’une génération spécifique du moteur, notamment le socle de logging.
|
||||||
- `deltas/` contient un document par livraison ou correctif versionné.
|
- `deltas/` contient un document par livraison ou correctif versionné.
|
||||||
- `prompts/` peut contenir les prompts de reprise de session lorsqu'ils deviennent utiles.
|
- `prompts/` peut contenir les prompts de reprise de session lorsqu'ils deviennent utiles.
|
||||||
- `scripts/` contient des audits en lecture seule et des outils du dépôt.
|
- `scripts/` contient des audits en lecture seule et des outils du dépôt.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/RULES_COMMANDS.md -->
|
<!-- file: docs/rules/RULES_COMMANDS.md -->
|
||||||
<!-- version: 2 -->
|
<!-- version: 3 -->
|
||||||
|
|
||||||
# Règles d'exécution des commandes
|
# Règles d'exécution des commandes
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
- **CMD-GEN-002** — Une commande mutante n'est pas utilisée lorsqu'une commande de contrôle en lecture seule suffit.
|
- **CMD-GEN-002** — Une commande mutante n'est pas utilisée lorsqu'une commande de contrôle en lecture seule suffit.
|
||||||
- **CMD-GEN-003** — Les commandes destructives ou de nettoyage global ne sont jamais exécutées par habitude.
|
- **CMD-GEN-003** — Les commandes destructives ou de nettoyage global ne sont jamais exécutées par habitude.
|
||||||
- **CMD-GEN-004** — Une gate n'est déclarée réussie que si la commande exacte a été exécutée sur l'état livré.
|
- **CMD-GEN-004** — Une gate n'est déclarée réussie que si la commande exacte a été exécutée sur l'état livré.
|
||||||
- **CMD-GEN-005** — Les commandes ciblées sont préférées pendant le développement ; les commandes workspace complètes sont utilisées aux gates de livraison.
|
- **CMD-GEN-005** — Les commandes ciblées sont préférées dès que leur portée est connue ; les gates workspace complètes ne sont imposées que lorsque leur coût est justifié par la portée du changement ou la phase de validation.
|
||||||
- **CMD-GEN-006** — Les scripts `audit_*` sont des contrôles en lecture seule et ne corrigent jamais automatiquement les fichiers.
|
- **CMD-GEN-006** — Les scripts `audit_*` sont des contrôles en lecture seule et ne corrigent jamais automatiquement les fichiers.
|
||||||
- **CMD-GEN-007** — Sauf demande explicite contraire, les commandes Cargo de validation finale sont exécutées par l'utilisateur sur son workspace local et non déclarées réussies par le générateur.
|
- **CMD-GEN-007** — Sauf demande explicite contraire, les commandes Cargo de validation finale sont exécutées par l'utilisateur sur son workspace local et non déclarées réussies par le générateur.
|
||||||
- **CMD-GEN-008** — Chaque fichier delta indique les commandes de validation que l'utilisateur doit exécuter et le statut connu de la validation du delta précédent.
|
- **CMD-GEN-008** — Chaque fichier delta indique les commandes de validation que l'utilisateur doit exécuter et le statut connu de la validation du delta précédent.
|
||||||
@@ -18,17 +18,18 @@
|
|||||||
|
|
||||||
## Rust et Cargo
|
## Rust et Cargo
|
||||||
|
|
||||||
- **CMD-RUST-001** — `cargo fmt --all` peut être utilisé après une tranche cohérente pour appliquer le formatage ; il ne sert pas de diagnostic.
|
- **CMD-RUST-001** — Lorsqu’un delta a modifié au moins un fichier Rust, l’utilisateur exécute `cargo fmt --all` au début de la validation afin d’appliquer le formatage canonique; les changements purement produits par rustfmt constituent l’unique exception à l’incrément obligatoire de l’en-tête de version du fichier.
|
||||||
- **CMD-RUST-002** — `cargo fmt --all -- --check` est la gate canonique de formatage et doit être exécutée avant livraison.
|
- **CMD-RUST-002** — `cargo fmt --all -- --check` suit immédiatement le formatage et constitue la gate canonique de conformité rustfmt.
|
||||||
- **CMD-RUST-003** — `cargo check --workspace` est la première gate de compilation globale après les audits statiques.
|
- **CMD-RUST-003** — `cargo check --workspace` est la première gate de compilation globale après les audits statiques.
|
||||||
- **CMD-RUST-004** — `cargo clippy --workspace --all-targets --all-features -- -D warnings` est exécuté après un `cargo check --workspace` propre pour la gate complète.
|
- **CMD-RUST-004** — `cargo clippy --workspace --all-targets --all-features -- -D warnings` est exécuté après un `cargo check --workspace` propre pour la gate complète.
|
||||||
- **CMD-RUST-005** — `cargo test --workspace --all-targets --all-features` est la gate de tests globale ; des tests `-p <crate>` peuvent être utilisés plus tôt pendant le développement.
|
- **CMD-RUST-005** — Les tests ciblés `cargo test -p <crate> --all-targets --all-features` sont la stratégie normale d’un delta et doivent couvrir toutes les crates directement ou transitivement affectées lorsque cela est raisonnablement déterminable.
|
||||||
- **CMD-RUST-006** — `cargo run -p <desktop-runner>` sert aux smokes manuels Desktop et n'est pas substitué aux tests automatisés.
|
- **CMD-RUST-006** — `cargo test --workspace --all-targets --all-features` est une gate lourde réservée au démarrage d’une nouvelle version `X.Y.Z`, à la fin d’une phase/version, aux changements transverses importants ou lorsqu’il existe un doute raisonnable sur la portée des tests ciblés.
|
||||||
- **CMD-RUST-007** — `cargo build` est utilisé lorsqu'un artefact exécutable ou une bibliothèque est réellement nécessaire ; il n'est pas lancé systématiquement en plus de `cargo check`.
|
- **CMD-RUST-007** — `cargo run -p <desktop-runner>` sert aux smokes manuels Desktop et n'est pas substitué aux tests automatisés.
|
||||||
- **CMD-RUST-008** — `cargo tree` et ses variantes sont des commandes de diagnostic de dépendances, pas des gates obligatoires de chaque delta.
|
- **CMD-RUST-008** — `cargo build` est utilisé lorsqu'un artefact exécutable ou une bibliothèque est réellement nécessaire ; il n'est pas lancé systématiquement en plus de `cargo check`.
|
||||||
- **CMD-RUST-009** — `cargo update` n'est jamais exécuté opportunistement. Toute mise à jour de dépendance doit appartenir à une tranche explicitement consacrée aux dépendances ou être nécessaire à la fonctionnalité en cours.
|
- **CMD-RUST-009** — `cargo tree` et ses variantes sont des commandes de diagnostic de dépendances, pas des gates obligatoires de chaque delta.
|
||||||
- **CMD-RUST-010** — `cargo clean` n'est pas une gate et n'est pas utilisé en routine. Il n'est autorisé qu'en cas de diagnostic de build corrompu, de contrainte disque explicite ou de demande ciblée, avec justification.
|
- **CMD-RUST-010** — `cargo update` n'est jamais exécuté opportunistement. Toute mise à jour de dépendance doit appartenir à une tranche explicitement consacrée aux dépendances ou être nécessaire à la fonctionnalité en cours.
|
||||||
- **CMD-RUST-011** — La suppression manuelle de `target/` ne remplace pas `cargo clean` et suit les mêmes restrictions.
|
- **CMD-RUST-011** — `cargo clean` n'est pas une gate et n'est pas utilisé en routine. Il n'est autorisé qu'en cas de diagnostic de build corrompu, de contrainte disque explicite ou de demande ciblée, avec justification.
|
||||||
|
- **CMD-RUST-012** — La suppression manuelle de `target/` ne remplace pas `cargo clean` et suit les mêmes restrictions.
|
||||||
|
|
||||||
## Runners Desktop
|
## Runners Desktop
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/RULES_PROJECT.md -->
|
<!-- file: docs/rules/RULES_PROJECT.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# Règles spécifiques games.sasedev
|
# Règles spécifiques games.sasedev
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
- **GAME-WS-001** — Un seul workspace Cargo racine contient les crates Rust du dépôt.
|
- **GAME-WS-001** — Un seul workspace Cargo racine contient les crates Rust du dépôt.
|
||||||
- **GAME-WS-002** — Toutes les crates Rust résident sous `crates/`.
|
- **GAME-WS-002** — Toutes les crates Rust résident sous `crates/`.
|
||||||
- **GAME-WS-003** — Les crates moteur résident sous `crates/engines/`, les crates jeu sous `crates/games/` et les exécutables/launchers réutilisant ces libs sous `crates/apps/`.
|
- **GAME-WS-003** — Les crates moteur résident sous `crates/engines/`, les crates jeu sous `crates/games/`, les bibliothèques transverses indépendantes d’une génération de moteur sous `crates/common/` et les exécutables/launchers réutilisant ces libs sous `crates/apps/`.
|
||||||
- **GAME-WS-004** — Une crate hérite par défaut de `workspace.package.version`.
|
- **GAME-WS-004** — Une crate hérite par défaut de `workspace.package.version`.
|
||||||
- **GAME-WS-005** — Une crate arrivée à maturité peut porter sa propre version SemVer lorsqu'une décision documentée rend son cycle autonome nécessaire.
|
- **GAME-WS-005** — Une crate arrivée à maturité peut porter sa propre version SemVer lorsqu'une décision documentée rend son cycle autonome nécessaire.
|
||||||
- **GAME-WS-006** — Les dépendances tierces communes sont centralisées sous `[workspace.dependencies]` et consommées avec `workspace = true` lorsqu'elles sont partagées.
|
- **GAME-WS-006** — Les dépendances tierces communes sont centralisées sous `[workspace.dependencies]` et consommées avec `workspace = true` lorsqu'elles sont partagées.
|
||||||
@@ -54,3 +54,10 @@
|
|||||||
|
|
||||||
- **GAME-POC-001** — Les premiers POC servent à valider l'architecture et doivent rester volontairement petits.
|
- **GAME-POC-001** — Les premiers POC servent à valider l'architecture et doivent rester volontairement petits.
|
||||||
- **GAME-POC-002** — Un POC ne justifie pas l'introduction prématurée d'un ECS, moteur physique ou backend complet s'il n'en a pas besoin.
|
- **GAME-POC-002** — Un POC ne justifie pas l'introduction prématurée d'un ECS, moteur physique ou backend complet s'il n'en a pas besoin.
|
||||||
|
|
||||||
|
## Diagnostics structurés
|
||||||
|
|
||||||
|
- **GAME-TRACE-001** — `tracing` est le mécanisme Rust canonique pour les diagnostics structurés.
|
||||||
|
- **GAME-TRACE-002** — `tracing-subscriber` compose les subscribers applicatifs et de test ; une librairie métier ne configure pas silencieusement le subscriber global.
|
||||||
|
- **GAME-TRACE-003** — `tracing-appender` est utilisé lorsque l’écriture non bloquante ou les fichiers de logs deviennent nécessaires ; le guard associé reste vivant pendant toute la durée utile.
|
||||||
|
- **GAME-TRACE-004** — La configuration de logging commune réside dans une crate transverse et ne doit pas être dupliquée par jeu.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/RULES_RUST.md -->
|
<!-- file: docs/rules/RULES_RUST.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Règles Rust générales
|
# Règles Rust générales
|
||||||
|
|
||||||
@@ -35,8 +35,9 @@
|
|||||||
## Formatage
|
## Formatage
|
||||||
|
|
||||||
- **RUST-FMT-001** — `rustfmt.toml` racine est canonique.
|
- **RUST-FMT-001** — `rustfmt.toml` racine est canonique.
|
||||||
- **RUST-FMT-002** — `cargo fmt --all -- --check` fait partie des gates.
|
- **RUST-FMT-002** — Lorsqu’un delta modifie du code Rust, `cargo fmt --all` est exécuté avant la gate afin d’appliquer `rustfmt.toml`; les seules modifications produites par cette commande ne nécessitent pas d’incrémenter l’en-tête `// version: N` des fichiers concernés.
|
||||||
- **RUST-FMT-003** — La largeur maximale canonique est 160 caractères.
|
- **RUST-FMT-003** — `cargo fmt --all -- --check` vérifie ensuite que l’état livré est conforme au formatage canonique.
|
||||||
|
- **RUST-FMT-004** — La largeur maximale canonique est 160 caractères.
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
|
|||||||
28
docs/testing/001-TEST_ARCHITECTURE.md
Normal file
28
docs/testing/001-TEST_ARCHITECTURE.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!-- file: docs/testing/001-TEST_ARCHITECTURE.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Architecture des tests
|
||||||
|
|
||||||
|
## Séparation physique
|
||||||
|
|
||||||
|
Le code de production sous `src/` ne contient aucun corps de test. Les tests unitaires résident dans `unit_tests/` à la racine de la crate et sont rattachés explicitement au module testé :
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "../unit_tests/example.rs"]
|
||||||
|
mod tests;
|
||||||
|
```
|
||||||
|
|
||||||
|
Les tests d’intégration, d’environnement et les smokes automatisés résident sous `tests/`, selon le mécanisme Cargo standard.
|
||||||
|
|
||||||
|
Cette séparation reprend le modèle utilisé dans KSP : elle garde `src/` exclusivement consacré au code de production tout en permettant aux unit tests d’accéder au contexte privé du module auquel ils sont rattachés.
|
||||||
|
|
||||||
|
## Tests ciblés
|
||||||
|
|
||||||
|
Un delta ne relance pas automatiquement tous les tests du workspace. Les crates affectées sont testées explicitement avec `cargo test -p ... --all-targets --all-features`.
|
||||||
|
|
||||||
|
La suite globale est conservée comme gate lourde pour les frontières de version, la stabilisation et les changements transverses dont l’impact ne peut pas être borné avec confiance.
|
||||||
|
|
||||||
|
## Tracing dans les tests
|
||||||
|
|
||||||
|
`game-logging-lib::with_test_tracing` installe un subscriber local au thread pour la durée du corps de test. Il évite les conflits liés à plusieurs initialisations globales et permet aux tests d’émettre des événements `tracing` consultables avec les mécanismes habituels du test runner.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/validation/001-VALIDATION_GATES.md -->
|
<!-- file: docs/validation/001-VALIDATION_GATES.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# Gates de validation
|
# Gates de validation
|
||||||
|
|
||||||
@@ -9,19 +9,34 @@ Les audits Python peuvent être exécutés pendant la préparation d'un delta. S
|
|||||||
|
|
||||||
Le générateur ne marque jamais une commande Cargo comme propre s'il ne dispose que du résultat d'un delta antérieur.
|
Le générateur ne marque jamais une commande Cargo comme propre s'il ne dispose que du résultat d'un delta antérieur.
|
||||||
|
|
||||||
## Gate canonique Rust et documentation
|
## Gate normale d’un delta Rust
|
||||||
|
|
||||||
À exécuter par l'utilisateur pour valider chaque delta contenant du code, du build, des manifests ou des règles affectant le workspace :
|
Lorsqu’un delta modifie du code Rust, la validation commence par un formatage mutatif volontaire :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
cargo fmt --all
|
||||||
cargo fmt --all -- --check
|
cargo fmt --all -- --check
|
||||||
|
```
|
||||||
|
|
||||||
|
Les modifications exclusivement produites par `cargo fmt --all` n’imposent pas d’incrémenter `// version: N`, car elles font partie de la préparation du même état avant commit.
|
||||||
|
|
||||||
|
Les audits et gates de compilation restent ensuite :
|
||||||
|
|
||||||
|
```bash
|
||||||
python3 scripts/audit_rust_workspace_rules.py
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Android deltas
|
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Android deltas
|
||||||
cargo check --workspace
|
cargo check --workspace
|
||||||
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
cargo test --workspace --all-targets --all-features
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Les tests sont ciblés par défaut :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test -p <crate-affectée> --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
Le fichier delta énumère les crates à tester. `cargo test --workspace --all-targets --all-features` n’est demandé systématiquement qu’au démarrage d’une nouvelle version `X.Y.Z`, vers la fin d’une phase/version, après un changement transversal important ou lorsque l’impact réel ne permet pas de sélectionner avec confiance les tests ciblés.
|
||||||
|
|
||||||
## Smokes Desktop
|
## Smokes Desktop
|
||||||
|
|
||||||
Lorsqu'un delta modifie la boucle exécutable, le runtime ou le comportement visible des runners Desktop, exécuter également :
|
Lorsqu'un delta modifie la boucle exécutable, le runtime ou le comportement visible des runners Desktop, exécuter également :
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: prompts/000-V0_1_0_START_PROMPT.md -->
|
<!-- file: prompts/000-V0_1_0_START_PROMPT.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# Prompt de reprise 0.1.0
|
# Prompt de reprise 0.1.0
|
||||||
|
|
||||||
@@ -8,3 +8,7 @@ Partir de la dernière livraison `0.1.0-*`, lire `RULES.md`, `ROADMAP.md`, `CHAN
|
|||||||
## Validation et enchaînement
|
## Validation et enchaînement
|
||||||
|
|
||||||
La validation Cargo finale d'un delta est exécutée par l'utilisateur sauf demande explicite contraire. Le delta suivant doit reprendre le statut de cette validation. Une gate entièrement propre autorise le passage automatique au delta suivant ; une gate en échec impose un `.fix.N` du delta courant avant progression, sauf décision explicite contraire.
|
La validation Cargo finale d'un delta est exécutée par l'utilisateur sauf demande explicite contraire. Le delta suivant doit reprendre le statut de cette validation. Une gate entièrement propre autorise le passage automatique au delta suivant ; une gate en échec impose un `.fix.N` du delta courant avant progression, sauf décision explicite contraire.
|
||||||
|
|
||||||
|
## Formatage, tests et diagnostics
|
||||||
|
|
||||||
|
Lorsqu’un delta modifie du Rust, demander `cargo fmt --all` avant `cargo fmt --all -- --check`. Les seules modifications produites par rustfmt n’imposent pas d’incrémenter les en-têtes `// version: N`. Préférer les tests `cargo test -p ... --all-targets --all-features` ciblés ; réserver la suite workspace complète aux frontières de version/phase, aux changements transverses ou aux cas où la portée n’est pas sûre. Les tests unitaires résident sous `unit_tests/`, les tests d’intégration/environnement sous `tests/`, jamais sous `src/`. Utiliser le socle `game-logging-lib`/`tracing` pour les diagnostics structurés lorsque pertinent.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# file: scripts/audit_project_workspace_rules.py
|
# file: scripts/audit_project_workspace_rules.py
|
||||||
# version: 2
|
# version: 3
|
||||||
|
|
||||||
"""Audit mechanically verifiable games.sasedev workspace boundaries."""
|
"""Audit mechanically verifiable games.sasedev workspace boundaries."""
|
||||||
|
|
||||||
@@ -44,6 +44,25 @@ def main() -> int:
|
|||||||
errors.append(f"GAME-WS-005: {relative}: explicit crate version does not follow the canonical games.sasedev SemVer scheme")
|
errors.append(f"GAME-WS-005: {relative}: explicit crate version does not follow the canonical games.sasedev SemVer scheme")
|
||||||
else:
|
else:
|
||||||
errors.append(f"GAME-WS-004: {relative}: crate must inherit or explicitly own a version")
|
errors.append(f"GAME-WS-004: {relative}: crate must inherit or explicitly own a version")
|
||||||
|
for source_path in sorted((root / "crates").rglob("src/**/*.rs")):
|
||||||
|
relative = source_path.relative_to(root).as_posix()
|
||||||
|
text = source_path.read_text(encoding="utf-8")
|
||||||
|
if "#[test]" in text:
|
||||||
|
errors.append(f"RUST-TEST-001: test body forbidden under src/: {relative}")
|
||||||
|
if "#[cfg(test)]" in text:
|
||||||
|
external_test_module = re.search(
|
||||||
|
r'#\[cfg\(test\)\]\s*#\[path\s*=\s*"[^"]*unit_tests/[^"]+"\]\s*mod\s+tests\s*;',
|
||||||
|
text,
|
||||||
|
re.MULTILINE,
|
||||||
|
)
|
||||||
|
if external_test_module is None:
|
||||||
|
errors.append(f"RUST-TEST-001: cfg(test) under src/ must reference an external unit_tests file: {relative}")
|
||||||
|
for test_dir in sorted((root / "crates").rglob("src/**/unit_tests")):
|
||||||
|
if test_dir.is_dir():
|
||||||
|
errors.append(f"RUST-TEST-002: unit_tests directory forbidden under src/: {test_dir.relative_to(root).as_posix()}")
|
||||||
|
for test_dir in sorted((root / "crates").rglob("src/**/tests")):
|
||||||
|
if test_dir.is_dir():
|
||||||
|
errors.append(f"RUST-TEST-003: integration tests directory forbidden under src/: {test_dir.relative_to(root).as_posix()}")
|
||||||
forbidden_assets = [path for path in (root / "crates").rglob("assets") if path.is_dir()]
|
forbidden_assets = [path for path in (root / "crates").rglob("assets") if path.is_dir()]
|
||||||
for path in forbidden_assets:
|
for path in forbidden_assets:
|
||||||
errors.append(f"GAME-ASSET-001: assets directory forbidden inside crates: {path.relative_to(root).as_posix()}")
|
errors.append(f"GAME-ASSET-001: assets directory forbidden inside crates: {path.relative_to(root).as_posix()}")
|
||||||
|
|||||||
Reference in New Issue
Block a user