feat(stage0): close native smoke acceptance gate
This commit is contained in:
+35
-14
@@ -48,7 +48,7 @@ jobs:
|
|||||||
--locked
|
--locked
|
||||||
|
|
||||||
stage0-matrix:
|
stage0-matrix:
|
||||||
name: Stage 0-2 CI (${{ matrix.os }})
|
name: Stage 0 CI (${{ matrix.os }})
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
strategy:
|
strategy:
|
||||||
@@ -73,22 +73,43 @@ jobs:
|
|||||||
run: cargo install cargo-deny --version 0.19.9 --locked
|
run: cargo install cargo-deny --version 0.19.9 --locked
|
||||||
- name: Run canonical CI gate
|
- name: Run canonical CI gate
|
||||||
run: cargo xtask ci
|
run: cargo xtask ci
|
||||||
- name: Record native Vulkan smoke status
|
- name: Run native Vulkan smoke
|
||||||
if: always()
|
|
||||||
shell: bash
|
|
||||||
run: >
|
run: >
|
||||||
cargo run -p fparkan-vulkan-smoke --locked --
|
cargo run -p fparkan-vulkan-smoke --locked --
|
||||||
--platform "${{ matrix.smoke_platform }}"
|
--out "target/fparkan/native-smoke/${{ matrix.smoke_platform }}.json"
|
||||||
--out "target/fparkan/native-smoke/${{ runner.os }}.json"
|
- name: Upload acceptance audit
|
||||||
--status blocked
|
|
||||||
--probe-surface
|
|
||||||
--reason "native Vulkan smoke runner is not enabled on this CI lane yet"
|
|
||||||
- name: Upload acceptance evidence
|
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||||
with:
|
with:
|
||||||
name: stage-0-2-acceptance-${{ matrix.os }}
|
name: stage-0-acceptance-${{ matrix.os }}
|
||||||
path: |
|
path: target/fparkan/acceptance/stage-0-audit.json
|
||||||
target/fparkan/acceptance/stage-0-2-audit.json
|
|
||||||
target/fparkan/native-smoke/*.json
|
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
- name: Upload native smoke report
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||||
|
with:
|
||||||
|
name: native-smoke-${{ matrix.smoke_platform }}
|
||||||
|
path: target/fparkan/native-smoke/*.json
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
native-smoke-audit:
|
||||||
|
name: Native smoke audit
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
|
needs: stage0-matrix
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
|
- uses: dtolnay/rust-toolchain@67ef31d5b988238dd797d409d6f9574278e20537
|
||||||
|
with:
|
||||||
|
toolchain: 1.87.0
|
||||||
|
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
|
||||||
|
with:
|
||||||
|
pattern: native-smoke-*
|
||||||
|
path: target/fparkan/native-smoke-artifacts
|
||||||
|
merge-multiple: true
|
||||||
|
- name: Aggregate native smoke reports
|
||||||
|
run: >
|
||||||
|
cargo xtask native-smoke audit
|
||||||
|
--dir target/fparkan/native-smoke-artifacts
|
||||||
|
|||||||
Generated
+1
@@ -717,6 +717,7 @@ dependencies = [
|
|||||||
"fparkan-platform",
|
"fparkan-platform",
|
||||||
"fparkan-platform-winit",
|
"fparkan-platform-winit",
|
||||||
"fparkan-render-vulkan",
|
"fparkan-render-vulkan",
|
||||||
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -63,6 +63,40 @@ FPARKAN_CORPORA_MANIFEST=/private/tmp/fparkan-corpora.toml \
|
|||||||
cargo xtask acceptance report --suite licensed --stage 5
|
cargo xtask acceptance report --suite licensed --stage 5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Stage 0 Vulkan smoke
|
||||||
|
|
||||||
|
Локальный Stage 0 smoke запускает реальный `winit` lifecycle и Vulkan triangle path с включёнными validation layers. Успешный прогон обязан:
|
||||||
|
|
||||||
|
- отрисовать 300 кадров;
|
||||||
|
- выполнить как минимум один реальный resize;
|
||||||
|
- пересоздать swapchain после resize;
|
||||||
|
- завершиться без validation warnings/errors.
|
||||||
|
|
||||||
|
Команда запуска:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run -p fparkan-vulkan-smoke --locked -- \
|
||||||
|
--out target/fparkan/native-smoke/local.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Перед запуском убедитесь, что на машине доступен Vulkan loader и рабочий ICD:
|
||||||
|
|
||||||
|
- macOS: установлены Vulkan SDK и MoltenVK; если используется нестандартная установка, проверьте `VK_ICD_FILENAMES`, `VK_LAYER_PATH` и наличие `VK_LAYER_KHRONOS_validation`.
|
||||||
|
- Linux: установлен `libvulkan` и драйвер/ICD (`mesa-vulkan-drivers`, Lavapipe или vendor GPU stack); smoke нужно запускать из активной графической сессии X11/Wayland.
|
||||||
|
- Windows: установлен Vulkan runtime от GPU vendor или LunarG Vulkan SDK; validation layer должен быть доступен из активного runtime.
|
||||||
|
|
||||||
|
Для полного локального closure gate используйте:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo xtask ci
|
||||||
|
```
|
||||||
|
|
||||||
|
GitHub workflow дополнительно собирает три platform reports и проверяет их aggregate gate:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo xtask native-smoke audit --dir target/fparkan/native-smoke-artifacts
|
||||||
|
```
|
||||||
|
|
||||||
## Contributing & Support
|
## Contributing & Support
|
||||||
|
|
||||||
Проект активно поддерживается и открыт для contribution. Issues и pull requests можно создавать в обоих репозиториях:
|
Проект активно поддерживается и открыт для contribution. Issues и pull requests можно создавать в обоих репозиториях:
|
||||||
|
|||||||
@@ -27,15 +27,14 @@ use fparkan_platform::{
|
|||||||
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::sync::OnceLock;
|
||||||
use winit::application::ApplicationHandler;
|
use std::time::Instant;
|
||||||
use winit::dpi::PhysicalSize as WinitPhysicalSize;
|
|
||||||
use winit::event::{Event, MouseButton, WindowEvent};
|
use winit::event::{Event, MouseButton, WindowEvent};
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
|
||||||
use winit::platform::scancode::PhysicalKeyExtScancode;
|
use winit::platform::scancode::PhysicalKeyExtScancode;
|
||||||
use winit::window::{Window, WindowId};
|
use winit::window::Window;
|
||||||
|
|
||||||
static NEXT_WINDOW_HANDLE_ID: AtomicU64 = AtomicU64::new(1);
|
static NEXT_WINDOW_HANDLE_ID: AtomicU64 = AtomicU64::new(1);
|
||||||
|
static CLOCK_START: OnceLock<Instant> = OnceLock::new();
|
||||||
const DEFAULT_SMOKE_WIDTH: u32 = 1280;
|
const DEFAULT_SMOKE_WIDTH: u32 = 1280;
|
||||||
const DEFAULT_SMOKE_HEIGHT: u32 = 720;
|
const DEFAULT_SMOKE_HEIGHT: u32 = 720;
|
||||||
|
|
||||||
@@ -49,10 +48,8 @@ pub struct WinitClock;
|
|||||||
|
|
||||||
impl MonotonicClock for WinitClock {
|
impl MonotonicClock for WinitClock {
|
||||||
fn now(&self) -> MonotonicInstant {
|
fn now(&self) -> MonotonicInstant {
|
||||||
let duration = SystemTime::now()
|
let elapsed = CLOCK_START.get_or_init(Instant::now).elapsed();
|
||||||
.duration_since(UNIX_EPOCH)
|
MonotonicInstant(elapsed.as_millis().try_into().unwrap_or(u64::MAX))
|
||||||
.unwrap_or_default();
|
|
||||||
MonotonicInstant(duration.as_millis().try_into().unwrap_or(u64::MAX))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,113 +184,6 @@ impl WinitWindowPlan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Native smoke window creation result.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct WinitSmokeWindowProbe {
|
|
||||||
/// Validated creation plan.
|
|
||||||
pub plan: WinitWindowPlan,
|
|
||||||
/// Captured window descriptor.
|
|
||||||
pub window: WinitWindow,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WinitSmokeWindowProbe {
|
|
||||||
/// Returns raw native handles captured from the native window.
|
|
||||||
#[must_use]
|
|
||||||
pub fn native_handles(&self) -> Option<NativeWindowHandles> {
|
|
||||||
self.window.native_handles()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a native smoke window, captures raw handles, then exits the event loop.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns [`PlatformError`] when the plan is invalid, the event loop/window
|
|
||||||
/// cannot be created, or raw native handles are unavailable.
|
|
||||||
pub fn probe_smoke_window() -> Result<WinitSmokeWindowProbe, PlatformError> {
|
|
||||||
let plan = WinitWindowPlan::smoke().validate()?;
|
|
||||||
let event_loop = EventLoop::new().map_err(|err| PlatformError::Backend {
|
|
||||||
context: "winit event loop",
|
|
||||||
message: err.to_string(),
|
|
||||||
})?;
|
|
||||||
let mut app = SmokeWindowApp::new(plan);
|
|
||||||
event_loop
|
|
||||||
.run_app(&mut app)
|
|
||||||
.map_err(|err| PlatformError::Backend {
|
|
||||||
context: "winit event loop",
|
|
||||||
message: err.to_string(),
|
|
||||||
})?;
|
|
||||||
app.into_probe()
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SmokeWindowApp {
|
|
||||||
plan: WinitWindowPlan,
|
|
||||||
window: Option<WinitWindow>,
|
|
||||||
error: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SmokeWindowApp {
|
|
||||||
const fn new(plan: WinitWindowPlan) -> Self {
|
|
||||||
Self {
|
|
||||||
plan,
|
|
||||||
window: None,
|
|
||||||
error: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_probe(self) -> Result<WinitSmokeWindowProbe, PlatformError> {
|
|
||||||
if let Some(message) = self.error {
|
|
||||||
return Err(PlatformError::Backend {
|
|
||||||
context: "winit smoke window",
|
|
||||||
message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let window = self.window.ok_or_else(|| PlatformError::Backend {
|
|
||||||
context: "winit smoke window",
|
|
||||||
message: "event loop exited before creating a window".to_string(),
|
|
||||||
})?;
|
|
||||||
if self.plan.requires_native_handles && window.native_handles().is_none() {
|
|
||||||
return Err(PlatformError::Backend {
|
|
||||||
context: "winit smoke window",
|
|
||||||
message: "native window/display handles are unavailable".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(WinitSmokeWindowProbe {
|
|
||||||
plan: self.plan,
|
|
||||||
window,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApplicationHandler for SmokeWindowApp {
|
|
||||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
||||||
if self.window.is_some() || self.error.is_some() {
|
|
||||||
event_loop.exit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let attributes = Window::default_attributes()
|
|
||||||
.with_title("FParkan Vulkan smoke")
|
|
||||||
.with_inner_size(WinitPhysicalSize::new(self.plan.width, self.plan.height));
|
|
||||||
match event_loop.create_window(attributes) {
|
|
||||||
Ok(window) => {
|
|
||||||
self.window = Some(WinitWindow::from_window(&window));
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
self.error = Some(err.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
event_loop.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn window_event(
|
|
||||||
&mut self,
|
|
||||||
_event_loop: &ActiveEventLoop,
|
|
||||||
_window_id: WindowId,
|
|
||||||
_event: WindowEvent,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Minimal window view over a `winit` window.
|
/// Minimal window view over a `winit` window.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct WinitWindow {
|
pub struct WinitWindow {
|
||||||
@@ -323,7 +213,7 @@ impl WinitWindow {
|
|||||||
focused: true,
|
focused: true,
|
||||||
minimized: false,
|
minimized: false,
|
||||||
occluded: false,
|
occluded: false,
|
||||||
native_handles: native_handles(window),
|
native_handles: window_native_handles(window),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,7 +274,9 @@ impl WindowPort for WinitWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn native_handles(window: &Window) -> Option<NativeWindowHandles> {
|
/// Extracts raw handles from a live `winit::Window`.
|
||||||
|
#[must_use]
|
||||||
|
pub fn window_native_handles(window: &Window) -> Option<NativeWindowHandles> {
|
||||||
let display = window.display_handle().ok()?.as_raw();
|
let display = window.display_handle().ok()?.as_raw();
|
||||||
let window = window.window_handle().ok()?.as_raw();
|
let window = window.window_handle().ok()?.as_raw();
|
||||||
Some(NativeWindowHandles { display, window })
|
Some(NativeWindowHandles { display, window })
|
||||||
@@ -454,33 +346,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn smoke_window_app_requires_created_native_window() {
|
fn monotonic_clock_uses_process_local_epoch() {
|
||||||
let app = SmokeWindowApp::new(WinitWindowPlan::smoke());
|
let clock = WinitClock;
|
||||||
|
let first = clock.now();
|
||||||
|
let second = clock.now();
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(second >= first);
|
||||||
app.into_probe(),
|
|
||||||
Err(PlatformError::Backend {
|
|
||||||
context: "winit smoke window",
|
|
||||||
..
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn smoke_window_app_rejects_synthetic_window_without_native_handles() {
|
|
||||||
let mut app = SmokeWindowApp::new(WinitWindowPlan::smoke());
|
|
||||||
app.window = Some(WinitWindow::synthetic(
|
|
||||||
DEFAULT_SMOKE_WIDTH,
|
|
||||||
DEFAULT_SMOKE_HEIGHT,
|
|
||||||
));
|
|
||||||
|
|
||||||
assert!(matches!(
|
|
||||||
app.into_probe(),
|
|
||||||
Err(PlatformError::Backend {
|
|
||||||
context: "winit smoke window",
|
|
||||||
..
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 in_color;
|
||||||
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
out_color = vec4(in_color, 1.0);
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,11 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 in_position;
|
||||||
|
layout(location = 1) in vec3 in_color;
|
||||||
|
|
||||||
|
layout(location = 0) out vec3 out_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
out_color = in_color;
|
||||||
|
gl_Position = vec4(in_position, 0.0, 1.0);
|
||||||
|
}
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -28,7 +28,7 @@ use fparkan_render::{
|
|||||||
DrawCommand, DrawId, GpuMaterialId, GpuMeshId, IndexRange, RenderBackend, RenderCommand,
|
DrawCommand, DrawId, GpuMaterialId, GpuMeshId, IndexRange, RenderBackend, RenderCommand,
|
||||||
RenderCommandList, RenderPhase,
|
RenderCommandList, RenderPhase,
|
||||||
};
|
};
|
||||||
use fparkan_render_vulkan::VulkanBackend;
|
use fparkan_render_vulkan::VulkanPlanningBackend;
|
||||||
use fparkan_runtime::{
|
use fparkan_runtime::{
|
||||||
create, frame, load_mission, loaded_mission_assets, EngineConfig, EngineMode, EngineServices,
|
create, frame, load_mission, loaded_mission_assets, EngineConfig, EngineMode, EngineServices,
|
||||||
MissionAssets, MissionRequest,
|
MissionAssets, MissionRequest,
|
||||||
@@ -71,7 +71,7 @@ fn run(args: &[String]) -> Result<String, String> {
|
|||||||
)
|
)
|
||||||
.map_err(|err| err.to_string())?;
|
.map_err(|err| err.to_string())?;
|
||||||
|
|
||||||
let mut backend = VulkanBackend::new();
|
let mut backend = VulkanPlanningBackend::new();
|
||||||
let _request = WinitWindow::default_render_request();
|
let _request = WinitWindow::default_render_request();
|
||||||
let window = WinitWindow::synthetic(1280, 720);
|
let window = WinitWindow::synthetic(1280, 720);
|
||||||
let _ = window.drawable_size();
|
let _ = window.drawable_size();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ repository.workspace = true
|
|||||||
fparkan-platform = { path = "../../crates/fparkan-platform" }
|
fparkan-platform = { path = "../../crates/fparkan-platform" }
|
||||||
fparkan-platform-winit = { path = "../../adapters/fparkan-platform-winit" }
|
fparkan-platform-winit = { path = "../../adapters/fparkan-platform-winit" }
|
||||||
fparkan-render-vulkan = { path = "../../adapters/fparkan-render-vulkan" }
|
fparkan-render-vulkan = { path = "../../adapters/fparkan-render-vulkan" }
|
||||||
|
winit = "0.30"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
+350
-1419
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ S0-ARCH-007 covered cargo xtask ci runs fmt, policy, workspace test, clippy, rus
|
|||||||
S0-ARCH-008 covered cargo xtask policy rejects moving Rust toolchains and workspace rust-version drift
|
S0-ARCH-008 covered cargo xtask policy rejects moving Rust toolchains and workspace rust-version drift
|
||||||
S0-ARCH-009 covered .github/workflows/ci.yml runs a pinned MSRV backend-neutral crate job
|
S0-ARCH-009 covered .github/workflows/ci.yml runs a pinned MSRV backend-neutral crate job
|
||||||
S0-ARCH-010 covered cargo xtask acceptance audit emits commit_sha, rust_toolchain, and msrv metadata into the JSON artifact
|
S0-ARCH-010 covered cargo xtask acceptance audit emits commit_sha, rust_toolchain, and msrv metadata into the JSON artifact
|
||||||
S0-ARCH-011 blocked cargo run -p fparkan-vulkan-smoke emits explicit per-platform blocked artifacts until real Vulkan 300-frame validation=0 runner is available
|
S0-ARCH-011 covered .github/workflows/ci.yml runs cargo run -p fparkan-vulkan-smoke --locked -- --out target/fparkan/native-smoke/<platform>.json and cargo xtask native-smoke audit enforces passed 300-frame reports with measured resize/recreate and validation=0
|
||||||
S0-DIAG-001 covered cargo test -p fparkan-diagnostics --offline diagnostic_chain_preserves_context
|
S0-DIAG-001 covered cargo test -p fparkan-diagnostics --offline diagnostic_chain_preserves_context
|
||||||
S0-DIAG-002 covered cargo test -p fparkan-diagnostics --offline json_is_stable
|
S0-DIAG-002 covered cargo test -p fparkan-diagnostics --offline json_is_stable
|
||||||
S0-CORPUS-001 covered cargo test -p fparkan-corpus --offline deterministic_traversal_is_creation_order_independent
|
S0-CORPUS-001 covered cargo test -p fparkan-corpus --offline deterministic_traversal_is_creation_order_independent
|
||||||
|
|||||||
|
@@ -0,0 +1,68 @@
|
|||||||
|
# Stage 0 acceptance IDs
|
||||||
|
|
||||||
|
`L0-COPYRIGHT-001`
|
||||||
|
`L0-P1-001`
|
||||||
|
`L0-P1-002`
|
||||||
|
`L0-P2-001`
|
||||||
|
`L0-P2-002`
|
||||||
|
`S0-ARCH-001`
|
||||||
|
`S0-ARCH-002`
|
||||||
|
`S0-ARCH-003`
|
||||||
|
`S0-ARCH-004`
|
||||||
|
`S0-ARCH-005`
|
||||||
|
`S0-ARCH-006`
|
||||||
|
`S0-ARCH-007`
|
||||||
|
`S0-ARCH-008`
|
||||||
|
`S0-ARCH-009`
|
||||||
|
`S0-ARCH-010`
|
||||||
|
`S0-ARCH-011`
|
||||||
|
`S0-DIAG-001`
|
||||||
|
`S0-DIAG-002`
|
||||||
|
`S0-CORPUS-001`
|
||||||
|
`S0-CORPUS-002`
|
||||||
|
`S0-CORPUS-003`
|
||||||
|
`S0-CORPUS-004`
|
||||||
|
`S0-CORPUS-005`
|
||||||
|
`S0-CORPUS-006`
|
||||||
|
`S0-CLI-001`
|
||||||
|
`S0-CLI-002`
|
||||||
|
`S0-PLAT-001`
|
||||||
|
`S0-PLAT-002`
|
||||||
|
`S0-PLAT-003`
|
||||||
|
`S0-PLAT-004`
|
||||||
|
`S0-VK-001`
|
||||||
|
`S0-VK-002`
|
||||||
|
`S0-VK-003`
|
||||||
|
`S0-VK-004`
|
||||||
|
`S0-VK-005`
|
||||||
|
`S0-VK-006`
|
||||||
|
`S0-VK-007`
|
||||||
|
`S0-VK-008`
|
||||||
|
`S0-VK-009`
|
||||||
|
`S0-VK-010`
|
||||||
|
`S0-VK-011`
|
||||||
|
`S0-VK-012`
|
||||||
|
`S0-VK-013`
|
||||||
|
`S0-VK-014`
|
||||||
|
`S0-VK-015`
|
||||||
|
`S0-VK-016`
|
||||||
|
`S0-VK-017`
|
||||||
|
`S0-VK-018`
|
||||||
|
`S0-VK-019`
|
||||||
|
`S0-VK-020`
|
||||||
|
`S0-VK-021`
|
||||||
|
`S0-VK-022`
|
||||||
|
`S0-VK-023`
|
||||||
|
`S0-VK-024`
|
||||||
|
`S0-VK-025`
|
||||||
|
`S0-VK-026`
|
||||||
|
`S0-VK-027`
|
||||||
|
`S0-VK-028`
|
||||||
|
`S0-VK-029`
|
||||||
|
`S0-VK-030`
|
||||||
|
`S0-VK-031`
|
||||||
|
`S0-VK-032`
|
||||||
|
`S0-VK-033`
|
||||||
|
`S0-VK-034`
|
||||||
|
`S0-LIMIT-001`
|
||||||
|
`S0-LIMIT-002`
|
||||||
+28
-16
@@ -34,9 +34,9 @@ use std::process::Command;
|
|||||||
const CORPORA_MANIFEST_ENV: &str = "FPARKAN_CORPORA_MANIFEST";
|
const CORPORA_MANIFEST_ENV: &str = "FPARKAN_CORPORA_MANIFEST";
|
||||||
const PART1_ROOT_ENV: &str = "FPARKAN_CORPUS_PART1_ROOT";
|
const PART1_ROOT_ENV: &str = "FPARKAN_CORPUS_PART1_ROOT";
|
||||||
const PART2_ROOT_ENV: &str = "FPARKAN_CORPUS_PART2_ROOT";
|
const PART2_ROOT_ENV: &str = "FPARKAN_CORPUS_PART2_ROOT";
|
||||||
const CI_ACCEPTANCE_ROADMAP: &str = "fixtures/acceptance/stage_0_2_roadmap.md";
|
const CI_ACCEPTANCE_ROADMAP: &str = "fixtures/acceptance/stage_0_roadmap.md";
|
||||||
const CI_ACCEPTANCE_COVERAGE: &str = "fixtures/acceptance/coverage.tsv";
|
const CI_ACCEPTANCE_COVERAGE: &str = "fixtures/acceptance/coverage.tsv";
|
||||||
const CI_ACCEPTANCE_REPORT: &str = "target/fparkan/acceptance/stage-0-2-audit.json";
|
const CI_ACCEPTANCE_REPORT: &str = "target/fparkan/acceptance/stage-0-audit.json";
|
||||||
const STAGE_PACKAGE_MANIFEST: &str = "fixtures/acceptance/stage_packages.toml";
|
const STAGE_PACKAGE_MANIFEST: &str = "fixtures/acceptance/stage_packages.toml";
|
||||||
const REQUIRED_NATIVE_SMOKE_PLATFORMS: &[&str] = &["linux", "macos", "windows"];
|
const REQUIRED_NATIVE_SMOKE_PLATFORMS: &[&str] = &["linux", "macos", "windows"];
|
||||||
const APPROVED_REGISTRY_SOURCE: &str = "registry+https://github.com/rust-lang/crates.io-index";
|
const APPROVED_REGISTRY_SOURCE: &str = "registry+https://github.com/rust-lang/crates.io-index";
|
||||||
@@ -193,16 +193,16 @@ fn run_cargo_fmt_check() -> Result<(), String> {
|
|||||||
|
|
||||||
fn run_cargo_deny() -> Result<(), String> {
|
fn run_cargo_deny() -> Result<(), String> {
|
||||||
let cargo_deny = std::env::var_os("CARGO_DENY").unwrap_or_else(|| "cargo-deny".into());
|
let cargo_deny = std::env::var_os("CARGO_DENY").unwrap_or_else(|| "cargo-deny".into());
|
||||||
let version_output = Command::new(&cargo_deny)
|
let version_output = match Command::new(&cargo_deny).arg("--version").output() {
|
||||||
.arg("--version")
|
Ok(output) => output,
|
||||||
.output()
|
Err(err) => {
|
||||||
.map_err(|err| {
|
return handle_cargo_deny_fallback(&format!(
|
||||||
format!(
|
"failed to run cargo-deny --version: {err}"
|
||||||
"cargo-deny is required; install cargo-deny {PINNED_CARGO_DENY_VERSION} or set {ALLOW_SUPPLY_CHAIN_FALLBACK_ENV}=1 for the built-in fallback: {err}"
|
));
|
||||||
)
|
}
|
||||||
})?;
|
};
|
||||||
if !version_output.status.success() {
|
if !version_output.status.success() {
|
||||||
return handle_cargo_deny_fallback(format!(
|
return handle_cargo_deny_fallback(&format!(
|
||||||
"cargo-deny --version exited with {}",
|
"cargo-deny --version exited with {}",
|
||||||
version_output.status
|
version_output.status
|
||||||
));
|
));
|
||||||
@@ -210,7 +210,7 @@ fn run_cargo_deny() -> Result<(), String> {
|
|||||||
let version_text = String::from_utf8(version_output.stdout)
|
let version_text = String::from_utf8(version_output.stdout)
|
||||||
.map_err(|err| format!("cargo-deny --version produced invalid UTF-8: {err}"))?;
|
.map_err(|err| format!("cargo-deny --version produced invalid UTF-8: {err}"))?;
|
||||||
if !version_text.contains(PINNED_CARGO_DENY_VERSION) {
|
if !version_text.contains(PINNED_CARGO_DENY_VERSION) {
|
||||||
return handle_cargo_deny_fallback(format!(
|
return handle_cargo_deny_fallback(&format!(
|
||||||
"cargo-deny version mismatch: expected {PINNED_CARGO_DENY_VERSION}, found {}",
|
"cargo-deny version mismatch: expected {PINNED_CARGO_DENY_VERSION}, found {}",
|
||||||
version_text.trim()
|
version_text.trim()
|
||||||
));
|
));
|
||||||
@@ -237,7 +237,7 @@ fn run_cargo_deny() -> Result<(), String> {
|
|||||||
|
|
||||||
const PINNED_CARGO_DENY_VERSION: &str = "0.19.9";
|
const PINNED_CARGO_DENY_VERSION: &str = "0.19.9";
|
||||||
|
|
||||||
fn handle_cargo_deny_fallback(reason: String) -> Result<(), String> {
|
fn handle_cargo_deny_fallback(reason: &str) -> Result<(), String> {
|
||||||
if std::env::var_os(ALLOW_SUPPLY_CHAIN_FALLBACK_ENV).is_some() {
|
if std::env::var_os(ALLOW_SUPPLY_CHAIN_FALLBACK_ENV).is_some() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{reason}; running built-in supply-chain policy fallback because {ALLOW_SUPPLY_CHAIN_FALLBACK_ENV} is set"
|
"{reason}; running built-in supply-chain policy fallback because {ALLOW_SUPPLY_CHAIN_FALLBACK_ENV} is set"
|
||||||
@@ -1605,6 +1605,7 @@ fn validate_native_smoke_report(
|
|||||||
expect_u64_at_least(platform, report, "frames", 300, failures);
|
expect_u64_at_least(platform, report, "frames", 300, failures);
|
||||||
expect_u64_at_least(platform, report, "resize_count", 1, failures);
|
expect_u64_at_least(platform, report, "resize_count", 1, failures);
|
||||||
expect_u64_at_least(platform, report, "swapchain_recreate_count", 1, failures);
|
expect_u64_at_least(platform, report, "swapchain_recreate_count", 1, failures);
|
||||||
|
expect_u64_field(platform, report, "validation_warning_count", 0, failures);
|
||||||
expect_u64_field(platform, report, "validation_error_count", 0, failures);
|
expect_u64_field(platform, report, "validation_error_count", 0, failures);
|
||||||
expect_nonempty_string(platform, report, "commit_sha", failures);
|
expect_nonempty_string(platform, report, "commit_sha", failures);
|
||||||
expect_string_field(
|
expect_string_field(
|
||||||
@@ -1889,6 +1890,10 @@ fn build_acceptance_audit(
|
|||||||
let mut missing = Vec::new();
|
let mut missing = Vec::new();
|
||||||
let mut by_stage = BTreeMap::new();
|
let mut by_stage = BTreeMap::new();
|
||||||
let mut coverage_evidence = BTreeMap::new();
|
let mut coverage_evidence = BTreeMap::new();
|
||||||
|
let required_scopes = required
|
||||||
|
.iter()
|
||||||
|
.filter_map(|id| id.get(0..2).map(ToString::to_string))
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
|
||||||
for id in required {
|
for id in required {
|
||||||
let stage = id
|
let stage = id
|
||||||
@@ -1909,7 +1914,12 @@ fn build_acceptance_audit(
|
|||||||
|
|
||||||
let unknown_coverage = coverage
|
let unknown_coverage = coverage
|
||||||
.keys()
|
.keys()
|
||||||
.filter(|id| !required.contains(*id))
|
.filter(|id| {
|
||||||
|
!required.contains(*id)
|
||||||
|
&& id
|
||||||
|
.get(0..2)
|
||||||
|
.is_some_and(|scope| required_scopes.contains(scope))
|
||||||
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -2367,7 +2377,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"S9-UNKNOWN-001".to_string(),
|
"S0-ARCH-099".to_string(),
|
||||||
CoverageEntry {
|
CoverageEntry {
|
||||||
status: CoverageStatus::Partial,
|
status: CoverageStatus::Partial,
|
||||||
evidence: "bad id".to_string(),
|
evidence: "bad id".to_string(),
|
||||||
@@ -2383,7 +2393,7 @@ mod tests {
|
|||||||
assert_eq!(audit.blocked, ["L5-RG40-001"]);
|
assert_eq!(audit.blocked, ["L5-RG40-001"]);
|
||||||
assert_eq!(audit.omitted, ["L3-DEVICE-001"]);
|
assert_eq!(audit.omitted, ["L3-DEVICE-001"]);
|
||||||
assert_eq!(audit.missing, ["S0-ARCH-002"]);
|
assert_eq!(audit.missing, ["S0-ARCH-002"]);
|
||||||
assert_eq!(audit.unknown_coverage, ["S9-UNKNOWN-001"]);
|
assert_eq!(audit.unknown_coverage, ["S0-ARCH-099"]);
|
||||||
assert_eq!(audit.by_stage.get("S0"), Some(&2));
|
assert_eq!(audit.by_stage.get("S0"), Some(&2));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
audit.strict_failures(),
|
audit.strict_failures(),
|
||||||
@@ -2436,6 +2446,7 @@ mod tests {
|
|||||||
"frames": 300,
|
"frames": 300,
|
||||||
"resize_count": 1,
|
"resize_count": 1,
|
||||||
"swapchain_recreate_count": 1,
|
"swapchain_recreate_count": 1,
|
||||||
|
"validation_warning_count": 0,
|
||||||
"validation_error_count": 0,
|
"validation_error_count": 0,
|
||||||
"shader_manifest_hash": "dd293e4ff08ffca1c037900d08b0ffd415db39f238b4fcdde46468fa049b679c",
|
"shader_manifest_hash": "dd293e4ff08ffca1c037900d08b0ffd415db39f238b4fcdde46468fa049b679c",
|
||||||
"vulkan_loader_status": "available",
|
"vulkan_loader_status": "available",
|
||||||
@@ -2474,6 +2485,7 @@ mod tests {
|
|||||||
"frames": 0,
|
"frames": 0,
|
||||||
"resize_count": 0,
|
"resize_count": 0,
|
||||||
"swapchain_recreate_count": 0,
|
"swapchain_recreate_count": 0,
|
||||||
|
"validation_warning_count": null,
|
||||||
"validation_error_count": null,
|
"validation_error_count": null,
|
||||||
"shader_manifest_hash": "dd293e4ff08ffca1c037900d08b0ffd415db39f238b4fcdde46468fa049b679c",
|
"shader_manifest_hash": "dd293e4ff08ffca1c037900d08b0ffd415db39f238b4fcdde46468fa049b679c",
|
||||||
"vulkan_loader_status": "unavailable",
|
"vulkan_loader_status": "unavailable",
|
||||||
|
|||||||
Reference in New Issue
Block a user