ci: tighten stage 0 acceptance gates
This commit is contained in:
+14
-10
@@ -2,19 +2,21 @@ name: fparkan-ci
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [devel, main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [devel, main]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
msrv-backend-neutral:
|
msrv-backend-neutral:
|
||||||
name: MSRV backend-neutral crates
|
name: MSRV backend-neutral crates
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@67ef31d5b988238dd797d409d6f9574278e20537
|
||||||
with:
|
with:
|
||||||
toolchain: 1.87.0
|
toolchain: 1.87.0
|
||||||
- name: Test backend-neutral crates
|
- name: Test backend-neutral crates
|
||||||
@@ -48,6 +50,7 @@ jobs:
|
|||||||
stage0-matrix:
|
stage0-matrix:
|
||||||
name: Stage 0-2 CI (${{ matrix.os }})
|
name: Stage 0-2 CI (${{ matrix.os }})
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
timeout-minutes: 30
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -61,12 +64,13 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@67ef31d5b988238dd797d409d6f9574278e20537
|
||||||
with:
|
with:
|
||||||
toolchain-file: rust-toolchain.toml
|
toolchain: 1.87.0
|
||||||
|
components: clippy,rustfmt
|
||||||
- name: Install cargo-deny
|
- name: Install cargo-deny
|
||||||
run: cargo install cargo-deny --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: Record native Vulkan smoke status
|
||||||
@@ -81,10 +85,10 @@ jobs:
|
|||||||
--reason "native Vulkan smoke runner is not enabled on this CI lane yet"
|
--reason "native Vulkan smoke runner is not enabled on this CI lane yet"
|
||||||
- name: Upload acceptance evidence
|
- name: Upload acceptance evidence
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||||
with:
|
with:
|
||||||
name: stage-0-2-acceptance-${{ matrix.os }}
|
name: stage-0-2-acceptance-${{ matrix.os }}
|
||||||
path: |
|
path: |
|
||||||
target/fparkan/acceptance/stage-0-2-audit.json
|
target/fparkan/acceptance/stage-0-2-audit.json
|
||||||
target/fparkan/native-smoke/*.json
|
target/fparkan/native-smoke/*.json
|
||||||
if-no-files-found: ignore
|
if-no-files-found: error
|
||||||
|
|||||||
@@ -345,14 +345,20 @@ impl VulkanLogicalDeviceProbe {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn graphics_queue(&self) -> vk::Queue {
|
pub fn graphics_queue(&self) -> vk::Queue {
|
||||||
// SAFETY: The queue-family index belongs to this live logical device.
|
// SAFETY: The queue-family index belongs to this live logical device.
|
||||||
unsafe { self.device.get_device_queue(self.report.graphics_queue_family, 0) }
|
unsafe {
|
||||||
|
self.device
|
||||||
|
.get_device_queue(self.report.graphics_queue_family, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the presentation queue selected by the Stage 0 policy.
|
/// Returns the presentation queue selected by the Stage 0 policy.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn present_queue(&self) -> vk::Queue {
|
pub fn present_queue(&self) -> vk::Queue {
|
||||||
// SAFETY: The queue-family index belongs to this live logical device.
|
// SAFETY: The queue-family index belongs to this live logical device.
|
||||||
unsafe { self.device.get_device_queue(self.report.present_queue_family, 0) }
|
unsafe {
|
||||||
|
self.device
|
||||||
|
.get_device_queue(self.report.present_queue_family, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a shared reference to the live logical device.
|
/// Returns a shared reference to the live logical device.
|
||||||
@@ -440,8 +446,12 @@ pub enum VulkanSmokeRunError {
|
|||||||
impl std::fmt::Display for VulkanSmokeRunError {
|
impl std::fmt::Display for VulkanSmokeRunError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::AcquireImage { result } => write!(f, "failed to acquire swapchain image: {result}"),
|
Self::AcquireImage { result } => {
|
||||||
Self::PresentImage { result } => write!(f, "failed to present swapchain image: {result}"),
|
write!(f, "failed to acquire swapchain image: {result}")
|
||||||
|
}
|
||||||
|
Self::PresentImage { result } => {
|
||||||
|
write!(f, "failed to present swapchain image: {result}")
|
||||||
|
}
|
||||||
Self::RecreateSwapchain { result } => {
|
Self::RecreateSwapchain { result } => {
|
||||||
write!(f, "failed to recreate swapchain: {result}")
|
write!(f, "failed to recreate swapchain: {result}")
|
||||||
}
|
}
|
||||||
@@ -464,9 +474,11 @@ pub fn run_vulkan_smoke_pass(
|
|||||||
let timeout_ns = u64::MAX;
|
let timeout_ns = u64::MAX;
|
||||||
|
|
||||||
let image_available = vk::SemaphoreCreateInfo::default();
|
let image_available = vk::SemaphoreCreateInfo::default();
|
||||||
let image_ready = unsafe { device.device().create_semaphore(&image_available, None) }
|
let image_ready =
|
||||||
.map_err(|error| VulkanSmokeRunError::RecreateSwapchain {
|
unsafe { device.device().create_semaphore(&image_available, None) }.map_err(|error| {
|
||||||
|
VulkanSmokeRunError::RecreateSwapchain {
|
||||||
result: format!("{error:?}"),
|
result: format!("{error:?}"),
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let recreate_interval = if recreate_count == 0 {
|
let recreate_interval = if recreate_count == 0 {
|
||||||
@@ -479,18 +491,27 @@ pub fn run_vulkan_smoke_pass(
|
|||||||
let mut created = 0_u32;
|
let mut created = 0_u32;
|
||||||
|
|
||||||
for frame in 0..frames {
|
for frame in 0..frames {
|
||||||
if recreate_interval > 0 && frame > 0 && frame % recreate_interval == 0 && created < recreate_count {
|
if recreate_interval > 0
|
||||||
swapchain = create_vulkan_swapchain_probe(instance, surface, device)
|
&& frame > 0
|
||||||
.map_err(|error| VulkanSmokeRunError::RecreateSwapchain {
|
&& frame % recreate_interval == 0
|
||||||
|
&& created < recreate_count
|
||||||
|
{
|
||||||
|
swapchain =
|
||||||
|
create_vulkan_swapchain_probe(instance, surface, device).map_err(|error| {
|
||||||
|
VulkanSmokeRunError::RecreateSwapchain {
|
||||||
result: error.to_string(),
|
result: error.to_string(),
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
created = created.saturating_add(1);
|
created = created.saturating_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let image_index = unsafe {
|
let image_index = unsafe {
|
||||||
swapchain
|
swapchain.loader().acquire_next_image(
|
||||||
.loader()
|
swapchain.swapchain(),
|
||||||
.acquire_next_image(swapchain.swapchain(), timeout_ns, image_ready, vk::Fence::null())
|
timeout_ns,
|
||||||
|
image_ready,
|
||||||
|
vk::Fence::null(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.map(|(index, _)| index)
|
.map(|(index, _)| index)
|
||||||
.map_err(|error| VulkanSmokeRunError::AcquireImage {
|
.map_err(|error| VulkanSmokeRunError::AcquireImage {
|
||||||
@@ -513,9 +534,10 @@ pub fn run_vulkan_smoke_pass(
|
|||||||
result: format!("{error:?}"),
|
result: format!("{error:?}"),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
unsafe { device.device().queue_wait_idle(render_queue) }
|
unsafe { device.device().queue_wait_idle(render_queue) }.map_err(|error| {
|
||||||
.map_err(|error| VulkanSmokeRunError::PresentImage {
|
VulkanSmokeRunError::PresentImage {
|
||||||
result: format!("{error:?}"),
|
result: format!("{error:?}"),
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
swaps = swaps.saturating_add(1);
|
swaps = swaps.saturating_add(1);
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ use fparkan_platform_winit::{probe_smoke_window, WinitWindowPlan};
|
|||||||
use fparkan_render_vulkan::{
|
use fparkan_render_vulkan::{
|
||||||
create_vulkan_instance_probe, create_vulkan_logical_device_probe, create_vulkan_surface_probe,
|
create_vulkan_instance_probe, create_vulkan_logical_device_probe, create_vulkan_surface_probe,
|
||||||
create_vulkan_swapchain_probe, probe_vulkan_loader, run_vulkan_smoke_pass,
|
create_vulkan_swapchain_probe, probe_vulkan_loader, run_vulkan_smoke_pass,
|
||||||
triangle_shader_manifest, validate_shader_manifest, VulkanInstanceConfig,
|
triangle_shader_manifest, validate_shader_manifest, VulkanInstanceConfig, VulkanInstanceProbe,
|
||||||
VulkanInstanceProbe, VulkanLogicalDeviceProbe, VulkanSwapchainProbe,
|
VulkanLogicalDeviceProbe, VulkanSwapchainProbe,
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -67,9 +67,13 @@ fn run(args: &[String]) -> Result<String, String> {
|
|||||||
return Err("passed native smoke report requires frames to be advanced".to_string());
|
return Err("passed native smoke report requires frames to be advanced".to_string());
|
||||||
}
|
}
|
||||||
if smoke_run.validation_error_count
|
if smoke_run.validation_error_count
|
||||||
!= options.validation_error_count.unwrap_or(smoke_run.validation_error_count)
|
!= options
|
||||||
|
.validation_error_count
|
||||||
|
.unwrap_or(smoke_run.validation_error_count)
|
||||||
{
|
{
|
||||||
return Err("passed native smoke report requires validation errors to be zero".to_string());
|
return Err(
|
||||||
|
"passed native smoke report requires validation errors to be zero".to_string(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let report = render_smoke_report_json(&options, &bootstrap)?;
|
let report = render_smoke_report_json(&options, &bootstrap)?;
|
||||||
@@ -443,12 +447,13 @@ impl VulkanBootstrapProbe {
|
|||||||
options: &SmokeOptions,
|
options: &SmokeOptions,
|
||||||
instance: &VulkanInstanceProbe,
|
instance: &VulkanInstanceProbe,
|
||||||
window_handles: Option<NativeWindowHandles>,
|
window_handles: Option<NativeWindowHandles>,
|
||||||
) -> Option<fparkan_render_vulkan::VulkanSurfaceProbe>
|
) -> Option<fparkan_render_vulkan::VulkanSurfaceProbe> {
|
||||||
{
|
|
||||||
if options.probes.vulkan.includes_surface()
|
if options.probes.vulkan.includes_surface()
|
||||||
&& self.instance_status == VulkanInstanceStatus::Created
|
&& self.instance_status == VulkanInstanceStatus::Created
|
||||||
{
|
{
|
||||||
match create_vulkan_surface_probe(instance, window_handles).map_err(|err| err.to_string()) {
|
match create_vulkan_surface_probe(instance, window_handles)
|
||||||
|
.map_err(|err| err.to_string())
|
||||||
|
{
|
||||||
Ok(surface) => {
|
Ok(surface) => {
|
||||||
self.surface_status = VulkanSurfaceStatus::Created;
|
self.surface_status = VulkanSurfaceStatus::Created;
|
||||||
return Some(surface);
|
return Some(surface);
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
[graph]
|
||||||
|
all-features = true
|
||||||
|
|
||||||
|
[advisories]
|
||||||
|
yanked = "deny"
|
||||||
|
|
||||||
|
[bans]
|
||||||
|
multiple-versions = "allow"
|
||||||
|
wildcards = "deny"
|
||||||
|
deny = [
|
||||||
|
{ name = "native-tls" },
|
||||||
|
{ name = "openssl" },
|
||||||
|
{ name = "openssl-sys" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[licenses]
|
||||||
|
unlicensed = "deny"
|
||||||
|
copyleft = "allow"
|
||||||
|
allow = [
|
||||||
|
"Apache-2.0",
|
||||||
|
"BSD-2-Clause",
|
||||||
|
"BSD-3-Clause",
|
||||||
|
"CC0-1.0",
|
||||||
|
"GPL-2.0-only",
|
||||||
|
"ISC",
|
||||||
|
"MIT",
|
||||||
|
"MPL-2.0",
|
||||||
|
"Unicode-3.0",
|
||||||
|
"Zlib",
|
||||||
|
]
|
||||||
|
|
||||||
|
[sources]
|
||||||
|
unknown-registry = "deny"
|
||||||
|
unknown-git = "deny"
|
||||||
|
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
schema = 1
|
||||||
|
|
||||||
|
[stages]
|
||||||
|
"0" = [
|
||||||
|
"fparkan-binary",
|
||||||
|
"fparkan-corpus",
|
||||||
|
"fparkan-diagnostics",
|
||||||
|
"fparkan-platform",
|
||||||
|
"fparkan-platform-winit",
|
||||||
|
"fparkan-render",
|
||||||
|
"fparkan-render-vulkan",
|
||||||
|
"fparkan-test-support",
|
||||||
|
"fparkan-vulkan-smoke",
|
||||||
|
"xtask",
|
||||||
|
]
|
||||||
|
"1" = [
|
||||||
|
"fparkan-cli",
|
||||||
|
"fparkan-inspection",
|
||||||
|
"fparkan-nres",
|
||||||
|
"fparkan-path",
|
||||||
|
"fparkan-resource",
|
||||||
|
"fparkan-rsli",
|
||||||
|
"fparkan-vfs",
|
||||||
|
]
|
||||||
|
"2" = [
|
||||||
|
"fparkan-prototype",
|
||||||
|
]
|
||||||
|
"3" = [
|
||||||
|
"fparkan-assets",
|
||||||
|
"fparkan-material",
|
||||||
|
"fparkan-msh",
|
||||||
|
"fparkan-texm",
|
||||||
|
"fparkan-viewer",
|
||||||
|
]
|
||||||
|
"4" = [
|
||||||
|
"fparkan-animation",
|
||||||
|
"fparkan-fx",
|
||||||
|
]
|
||||||
|
"5" = [
|
||||||
|
"fparkan-game",
|
||||||
|
"fparkan-headless",
|
||||||
|
"fparkan-mission-format",
|
||||||
|
"fparkan-runtime",
|
||||||
|
"fparkan-terrain",
|
||||||
|
"fparkan-terrain-format",
|
||||||
|
"fparkan-world",
|
||||||
|
]
|
||||||
+276
-102
@@ -37,11 +37,24 @@ 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_2_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-2-audit.json";
|
||||||
|
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";
|
||||||
const SUPPLY_CHAIN_BANNED_PACKAGES: &[&str] = &["native-tls", "openssl", "openssl-sys"];
|
const SUPPLY_CHAIN_BANNED_PACKAGES: &[&str] = &["native-tls", "openssl", "openssl-sys"];
|
||||||
const PINNED_RUST_TOOLCHAIN: &str = "1.87.0";
|
const PINNED_RUST_TOOLCHAIN: &str = "1.87.0";
|
||||||
const WORKSPACE_MSRV: &str = "1.87";
|
const WORKSPACE_MSRV: &str = "1.87";
|
||||||
|
const ALLOW_SUPPLY_CHAIN_FALLBACK_ENV: &str = "FPARKAN_ALLOW_SUPPLY_CHAIN_FALLBACK";
|
||||||
|
|
||||||
|
fn workspace_root_path() -> PathBuf {
|
||||||
|
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||||
|
.parent()
|
||||||
|
.unwrap_or_else(|| Path::new(env!("CARGO_MANIFEST_DIR")))
|
||||||
|
.to_path_buf()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn workspace_relative_path(path: &str) -> PathBuf {
|
||||||
|
workspace_root_path().join(path)
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
||||||
@@ -180,13 +193,27 @@ 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 available = Command::new(&cargo_deny).arg("--version").status();
|
let version_output = Command::new(&cargo_deny)
|
||||||
match available {
|
.arg("--version")
|
||||||
Ok(status) if status.success() => {}
|
.output()
|
||||||
Ok(_) | Err(_) => {
|
.map_err(|err| {
|
||||||
eprintln!("cargo-deny is unavailable; running built-in supply-chain policy fallback");
|
format!(
|
||||||
return run_builtin_supply_chain_policy(Path::new("."));
|
"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() {
|
||||||
|
return handle_cargo_deny_fallback(format!(
|
||||||
|
"cargo-deny --version exited with {}",
|
||||||
|
version_output.status
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
let version_text = String::from_utf8(version_output.stdout)
|
||||||
|
.map_err(|err| format!("cargo-deny --version produced invalid UTF-8: {err}"))?;
|
||||||
|
if !version_text.contains(PINNED_CARGO_DENY_VERSION) {
|
||||||
|
return handle_cargo_deny_fallback(format!(
|
||||||
|
"cargo-deny version mismatch: expected {PINNED_CARGO_DENY_VERSION}, found {}",
|
||||||
|
version_text.trim()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = Command::new(cargo_deny)
|
let status = Command::new(cargo_deny)
|
||||||
@@ -208,6 +235,21 @@ fn run_cargo_deny() -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PINNED_CARGO_DENY_VERSION: &str = "0.19.9";
|
||||||
|
|
||||||
|
fn handle_cargo_deny_fallback(reason: String) -> Result<(), String> {
|
||||||
|
if std::env::var_os(ALLOW_SUPPLY_CHAIN_FALLBACK_ENV).is_some() {
|
||||||
|
eprintln!(
|
||||||
|
"{reason}; running built-in supply-chain policy fallback because {ALLOW_SUPPLY_CHAIN_FALLBACK_ENV} is set"
|
||||||
|
);
|
||||||
|
run_builtin_supply_chain_policy(Path::new("."))
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"{reason}; install cargo-deny {PINNED_CARGO_DENY_VERSION} or explicitly opt into the fallback with {ALLOW_SUPPLY_CHAIN_FALLBACK_ENV}=1"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn run_builtin_supply_chain_policy(root: &Path) -> Result<(), String> {
|
fn run_builtin_supply_chain_policy(root: &Path) -> Result<(), String> {
|
||||||
let mut failures = Vec::new();
|
let mut failures = Vec::new();
|
||||||
validate_workspace_license(root, &mut failures)?;
|
validate_workspace_license(root, &mut failures)?;
|
||||||
@@ -473,12 +515,7 @@ fn validate_cargo_metadata(root: &Path, failures: &mut Vec<String>) -> Result<()
|
|||||||
if !manifest.exists() {
|
if !manifest.exists() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let metadata = MetadataCommand::new()
|
let metadata = workspace_metadata(root)?;
|
||||||
.manifest_path(&manifest)
|
|
||||||
.no_deps()
|
|
||||||
.other_options(["--offline".to_string(), "--locked".to_string()])
|
|
||||||
.exec()
|
|
||||||
.map_err(|error| format!("{}: cargo metadata failed: {}", manifest.display(), error))?;
|
|
||||||
if metadata.workspace_members.is_empty() {
|
if metadata.workspace_members.is_empty() {
|
||||||
failures.push(format!(
|
failures.push(format!(
|
||||||
"{}: cargo metadata produced no workspace members",
|
"{}: cargo metadata produced no workspace members",
|
||||||
@@ -486,6 +523,18 @@ fn validate_cargo_metadata(root: &Path, failures: &mut Vec<String>) -> Result<()
|
|||||||
));
|
));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
let stage_manifest_path = root.join(STAGE_PACKAGE_MANIFEST);
|
||||||
|
let stage_manifest = load_stage_package_manifest(&stage_manifest_path)?;
|
||||||
|
let workspace_packages = metadata
|
||||||
|
.workspace_packages()
|
||||||
|
.iter()
|
||||||
|
.map(|package| package.name.to_string())
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
if let Err(err) =
|
||||||
|
validate_stage_package_entries(&stage_manifest, &workspace_packages, &stage_manifest_path)
|
||||||
|
{
|
||||||
|
failures.push(err);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1185,40 +1234,6 @@ enum Stage {
|
|||||||
Number(u8),
|
Number(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALL_WORKSPACE_PACKAGES: &[&str] = &[
|
|
||||||
"fparkan-animation",
|
|
||||||
"fparkan-assets",
|
|
||||||
"fparkan-binary",
|
|
||||||
"fparkan-corpus",
|
|
||||||
"fparkan-diagnostics",
|
|
||||||
"fparkan-fx",
|
|
||||||
"fparkan-material",
|
|
||||||
"fparkan-mission-format",
|
|
||||||
"fparkan-msh",
|
|
||||||
"fparkan-nres",
|
|
||||||
"fparkan-path",
|
|
||||||
"fparkan-platform",
|
|
||||||
"fparkan-prototype",
|
|
||||||
"fparkan-render",
|
|
||||||
"fparkan-resource",
|
|
||||||
"fparkan-rsli",
|
|
||||||
"fparkan-runtime",
|
|
||||||
"fparkan-terrain",
|
|
||||||
"fparkan-terrain-format",
|
|
||||||
"fparkan-test-support",
|
|
||||||
"fparkan-texm",
|
|
||||||
"fparkan-vfs",
|
|
||||||
"fparkan-world",
|
|
||||||
"fparkan-platform-winit",
|
|
||||||
"fparkan-render-vulkan",
|
|
||||||
"fparkan-cli",
|
|
||||||
"fparkan-game",
|
|
||||||
"fparkan-headless",
|
|
||||||
"fparkan-vulkan-smoke",
|
|
||||||
"fparkan-viewer",
|
|
||||||
"xtask",
|
|
||||||
];
|
|
||||||
|
|
||||||
impl Stage {
|
impl Stage {
|
||||||
fn parse(value: &str) -> Result<Self, String> {
|
fn parse(value: &str) -> Result<Self, String> {
|
||||||
if value == "all" {
|
if value == "all" {
|
||||||
@@ -1390,9 +1405,10 @@ fn parse_acceptance_options(args: &[String]) -> Result<AcceptanceOptions, String
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_audit_options(args: &[String]) -> Result<AuditOptions, String> {
|
fn parse_audit_options(args: &[String]) -> Result<AuditOptions, String> {
|
||||||
let mut roadmap = PathBuf::from("FPARKAN_ARCHITECTURE_ROADMAP_STAGES_0_5.md");
|
let mut roadmap = workspace_relative_path(CI_ACCEPTANCE_ROADMAP);
|
||||||
let mut coverage = PathBuf::from("fixtures/acceptance/coverage.tsv");
|
let mut coverage = workspace_relative_path(CI_ACCEPTANCE_COVERAGE);
|
||||||
let mut out = PathBuf::from("target")
|
let mut out = workspace_root_path()
|
||||||
|
.join("target")
|
||||||
.join("fparkan")
|
.join("fparkan")
|
||||||
.join("reports")
|
.join("reports")
|
||||||
.join("acceptance")
|
.join("acceptance")
|
||||||
@@ -1478,25 +1494,55 @@ fn read_native_smoke_reports(dir: &Path) -> Result<BTreeMap<String, serde_json::
|
|||||||
.map_err(|err| format!("{}: {err}", path.display()))?;
|
.map_err(|err| format!("{}: {err}", path.display()))?;
|
||||||
let platform = json_string_field(&json, "platform")
|
let platform = json_string_field(&json, "platform")
|
||||||
.map_err(|err| format!("{}: {err}", path.display()))?;
|
.map_err(|err| format!("{}: {err}", path.display()))?;
|
||||||
reports.insert(platform.to_string(), json);
|
let platform = platform.to_string();
|
||||||
|
if reports.insert(platform.clone(), json).is_some() {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: duplicate native smoke report for platform {platform}",
|
||||||
|
path.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(reports)
|
Ok(reports)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn audit_native_smoke_reports(reports: &BTreeMap<String, serde_json::Value>) -> Vec<String> {
|
fn audit_native_smoke_reports(reports: &BTreeMap<String, serde_json::Value>) -> Vec<String> {
|
||||||
let mut failures = Vec::new();
|
let mut failures = Vec::new();
|
||||||
|
let mut commit_shas = BTreeSet::new();
|
||||||
|
let mut rust_toolchains = BTreeSet::new();
|
||||||
for platform in REQUIRED_NATIVE_SMOKE_PLATFORMS {
|
for platform in REQUIRED_NATIVE_SMOKE_PLATFORMS {
|
||||||
let Some(report) = reports.get(*platform) else {
|
let Some(report) = reports.get(*platform) else {
|
||||||
failures.push(format!("{platform}: missing native smoke report"));
|
failures.push(format!("{platform}: missing native smoke report"));
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
validate_native_smoke_report(platform, report, &mut failures);
|
validate_native_smoke_report(platform, report, &mut failures);
|
||||||
|
if let Ok(commit_sha) = json_string_field(report, "commit_sha") {
|
||||||
|
if commit_sha == "unknown" {
|
||||||
|
failures.push(format!("{platform}: commit_sha must not be \"unknown\""));
|
||||||
|
} else {
|
||||||
|
commit_shas.insert(commit_sha.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(toolchain) = json_string_field(report, "rust_toolchain") {
|
||||||
|
rust_toolchains.insert(toolchain.to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for platform in reports.keys() {
|
for platform in reports.keys() {
|
||||||
if !REQUIRED_NATIVE_SMOKE_PLATFORMS.contains(&platform.as_str()) {
|
if !REQUIRED_NATIVE_SMOKE_PLATFORMS.contains(&platform.as_str()) {
|
||||||
failures.push(format!("{platform}: unexpected native smoke platform"));
|
failures.push(format!("{platform}: unexpected native smoke platform"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if commit_shas.len() > 1 {
|
||||||
|
failures.push(format!(
|
||||||
|
"native smoke reports disagree on commit_sha: {}",
|
||||||
|
commit_shas.into_iter().collect::<Vec<_>>().join(", ")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if rust_toolchains.len() > 1 {
|
||||||
|
failures.push(format!(
|
||||||
|
"native smoke reports disagree on rust_toolchain: {}",
|
||||||
|
rust_toolchains.into_iter().collect::<Vec<_>>().join(", ")
|
||||||
|
));
|
||||||
|
}
|
||||||
failures
|
failures
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1561,7 +1607,14 @@ fn validate_native_smoke_report(
|
|||||||
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_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_nonempty_string(platform, report, "rust_toolchain", failures);
|
expect_string_field(
|
||||||
|
platform,
|
||||||
|
report,
|
||||||
|
"rust_toolchain",
|
||||||
|
&measured_rust_toolchain_version(),
|
||||||
|
failures,
|
||||||
|
);
|
||||||
|
expect_string_field(platform, report, "platform", platform, failures);
|
||||||
expect_nonempty_string(platform, report, "target_triple", failures);
|
expect_nonempty_string(platform, report, "target_triple", failures);
|
||||||
expect_nonempty_string(platform, report, "shader_manifest_hash", failures);
|
expect_nonempty_string(platform, report, "shader_manifest_hash", failures);
|
||||||
expect_nonempty_string(platform, report, "vulkan_device_name", failures);
|
expect_nonempty_string(platform, report, "vulkan_device_name", failures);
|
||||||
@@ -1741,13 +1794,20 @@ impl AcceptanceAudit {
|
|||||||
self.partial
|
self.partial
|
||||||
.iter()
|
.iter()
|
||||||
.chain(&self.blocked)
|
.chain(&self.blocked)
|
||||||
|
.chain(&self.omitted)
|
||||||
.chain(&self.missing)
|
.chain(&self.missing)
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strict_failures(&self) -> Vec<String> {
|
fn strict_failures(&self) -> Vec<String> {
|
||||||
self.partial.iter().chain(&self.missing).cloned().collect()
|
self.partial
|
||||||
|
.iter()
|
||||||
|
.chain(&self.blocked)
|
||||||
|
.chain(&self.omitted)
|
||||||
|
.chain(&self.missing)
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1855,7 +1915,7 @@ fn build_acceptance_audit(
|
|||||||
|
|
||||||
AcceptanceAudit {
|
AcceptanceAudit {
|
||||||
commit_sha: current_git_commit_sha(),
|
commit_sha: current_git_commit_sha(),
|
||||||
rust_toolchain: PINNED_RUST_TOOLCHAIN.to_string(),
|
rust_toolchain: measured_rust_toolchain_version(),
|
||||||
msrv: WORKSPACE_MSRV.to_string(),
|
msrv: WORKSPACE_MSRV.to_string(),
|
||||||
required_total: required.len(),
|
required_total: required.len(),
|
||||||
covered,
|
covered,
|
||||||
@@ -1930,6 +1990,23 @@ fn current_git_commit_sha() -> String {
|
|||||||
.unwrap_or_else(|| "unknown".to_string())
|
.unwrap_or_else(|| "unknown".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn measured_rust_toolchain_version() -> String {
|
||||||
|
Command::new("rustc")
|
||||||
|
.args(["-Vv"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|output| output.status.success())
|
||||||
|
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||||
|
.and_then(|stdout| {
|
||||||
|
stdout.lines().find_map(|line| {
|
||||||
|
line.strip_prefix("release:")
|
||||||
|
.map(str::trim)
|
||||||
|
.map(ToString::to_string)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| PINNED_RUST_TOOLCHAIN.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
fn render_string_usize_map(values: &BTreeMap<String, usize>) -> String {
|
fn render_string_usize_map(values: &BTreeMap<String, usize>) -> String {
|
||||||
let pairs = values
|
let pairs = values
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1994,6 +2071,7 @@ fn run_acceptance_report(options: &AcceptanceOptions) -> Result<(), String> {
|
|||||||
|
|
||||||
fn render_acceptance_report(options: &AcceptanceOptions) -> String {
|
fn render_acceptance_report(options: &AcceptanceOptions) -> String {
|
||||||
let packages = stage_report_packages(options.stage)
|
let packages = stage_report_packages(options.stage)
|
||||||
|
.unwrap_or_default()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|package| format!(" \"{package}\""))
|
.map(|package| format!(" \"{package}\""))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
@@ -2023,10 +2101,12 @@ fn render_acceptance_report(options: &AcceptanceOptions) -> String {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stage_report_packages(stage: Stage) -> Vec<&'static str> {
|
fn stage_report_packages(stage: Stage) -> Result<Vec<String>, String> {
|
||||||
|
let workspace_root = workspace_root_path();
|
||||||
match stage {
|
match stage {
|
||||||
Stage::All => ALL_WORKSPACE_PACKAGES.to_vec(),
|
Stage::All => workspace_package_names(&workspace_root)
|
||||||
Stage::Number(number) => stage_packages(number).unwrap_or(&[]).to_vec(),
|
.map(|packages| packages.into_iter().collect::<Vec<_>>()),
|
||||||
|
Stage::Number(number) => stage_packages(number),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2052,12 +2132,19 @@ fn run_stage_tests(
|
|||||||
}
|
}
|
||||||
Stage::Number(number) => {
|
Stage::Number(number) => {
|
||||||
for package in stage_packages(number)? {
|
for package in stage_packages(number)? {
|
||||||
let mut args = vec!["test", "-p", package, "--locked", "--offline"];
|
let mut args = vec![
|
||||||
args.extend(suffix.iter().copied());
|
"test".to_string(),
|
||||||
|
"-p".to_string(),
|
||||||
|
package,
|
||||||
|
"--locked".to_string(),
|
||||||
|
"--offline".to_string(),
|
||||||
|
];
|
||||||
|
args.extend(suffix.iter().map(|value| (*value).to_string()));
|
||||||
if let Some(envs) = envs {
|
if let Some(envs) = envs {
|
||||||
cargo_with_env(&args, &envs)?;
|
let borrowed = args.iter().map(String::as_str).collect::<Vec<_>>();
|
||||||
|
cargo_with_env(&borrowed, &envs)?;
|
||||||
} else {
|
} else {
|
||||||
cargo(&args)?;
|
cargo_owned(&args)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -2065,43 +2152,108 @@ fn run_stage_tests(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stage_packages(stage: u8) -> Result<&'static [&'static str], String> {
|
fn stage_packages(stage: u8) -> Result<Vec<String>, String> {
|
||||||
match stage {
|
let manifest_path = workspace_relative_path(STAGE_PACKAGE_MANIFEST);
|
||||||
0 => Ok(&[
|
let manifest = load_stage_package_manifest(&manifest_path)?;
|
||||||
"fparkan-corpus",
|
let packages = manifest
|
||||||
"fparkan-diagnostics",
|
.stages
|
||||||
"fparkan-test-support",
|
.get(&stage.to_string())
|
||||||
]),
|
.cloned()
|
||||||
1 => Ok(&[
|
.ok_or_else(|| format!("stage out of range: {stage}"))?;
|
||||||
"fparkan-binary",
|
validate_stage_package_entries(
|
||||||
"fparkan-path",
|
&manifest,
|
||||||
"fparkan-nres",
|
&workspace_package_names(&workspace_root_path())?,
|
||||||
"fparkan-rsli",
|
&manifest_path,
|
||||||
"fparkan-resource",
|
)?;
|
||||||
"fparkan-vfs",
|
Ok(packages)
|
||||||
]),
|
}
|
||||||
2 => Ok(&["fparkan-prototype"]),
|
|
||||||
3 => Ok(&[
|
fn workspace_package_names(root: &Path) -> Result<BTreeSet<String>, String> {
|
||||||
"fparkan-msh",
|
let metadata = workspace_metadata(root)?;
|
||||||
"fparkan-material",
|
Ok(metadata
|
||||||
"fparkan-texm",
|
.workspace_packages()
|
||||||
"fparkan-assets",
|
.iter()
|
||||||
"fparkan-render",
|
.map(|package| package.name.to_string())
|
||||||
"fparkan-viewer",
|
.collect())
|
||||||
]),
|
}
|
||||||
4 => Ok(&["fparkan-animation", "fparkan-fx"]),
|
|
||||||
5 => Ok(&[
|
fn workspace_metadata(root: &Path) -> Result<cargo_metadata::Metadata, String> {
|
||||||
"fparkan-terrain-format",
|
let manifest = root.join("Cargo.toml");
|
||||||
"fparkan-terrain",
|
MetadataCommand::new()
|
||||||
"fparkan-mission-format",
|
.manifest_path(&manifest)
|
||||||
"fparkan-world",
|
.no_deps()
|
||||||
"fparkan-runtime",
|
.other_options(["--offline".to_string(), "--locked".to_string()])
|
||||||
"fparkan-headless",
|
.exec()
|
||||||
"fparkan-game",
|
.map_err(|error| format!("{}: cargo metadata failed: {}", manifest.display(), error))
|
||||||
"fparkan-vulkan-smoke",
|
}
|
||||||
]),
|
|
||||||
_ => Err(format!("stage out of range: {stage}")),
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
struct StagePackageManifest {
|
||||||
|
schema: Option<u8>,
|
||||||
|
stages: BTreeMap<String, Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_stage_package_manifest(path: &Path) -> Result<StagePackageManifest, String> {
|
||||||
|
let text = fs::read_to_string(path).map_err(|err| format!("{}: {err}", path.display()))?;
|
||||||
|
let manifest = toml::from_str::<StagePackageManifest>(&text)
|
||||||
|
.map_err(|err| format!("failed to parse {}: {err}", path.display()))?;
|
||||||
|
if manifest.schema != Some(1) {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: unsupported stage package manifest schema {:?} (expected 1)",
|
||||||
|
path.display(),
|
||||||
|
manifest.schema
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
Ok(manifest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_stage_package_entries(
|
||||||
|
manifest: &StagePackageManifest,
|
||||||
|
workspace_packages: &BTreeSet<String>,
|
||||||
|
path: &Path,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let required_stages = (0_u8..=5_u8)
|
||||||
|
.map(|stage| stage.to_string())
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
let declared_stages = manifest.stages.keys().cloned().collect::<BTreeSet<_>>();
|
||||||
|
if declared_stages != required_stages {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: stage package manifest must declare stages 0 through 5 exactly once",
|
||||||
|
path.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut assigned = BTreeSet::new();
|
||||||
|
for (stage, packages) in &manifest.stages {
|
||||||
|
for package in packages {
|
||||||
|
if !workspace_packages.contains(package) {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: stage {stage} references unknown package {package}",
|
||||||
|
path.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if !assigned.insert(package.clone()) {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: package {package} is assigned to multiple stages",
|
||||||
|
path.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let missing = workspace_packages
|
||||||
|
.difference(&assigned)
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if !missing.is_empty() {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: stage package manifest is missing workspace packages: {}",
|
||||||
|
path.display(),
|
||||||
|
missing.join(", ")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -2233,6 +2385,10 @@ mod tests {
|
|||||||
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, ["S9-UNKNOWN-001"]);
|
||||||
assert_eq!(audit.by_stage.get("S0"), Some(&2));
|
assert_eq!(audit.by_stage.get("S0"), Some(&2));
|
||||||
|
assert_eq!(
|
||||||
|
audit.strict_failures(),
|
||||||
|
strings(&["L5-RG40-001", "L3-DEVICE-001", "S0-ARCH-002"])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2273,7 +2429,7 @@ mod tests {
|
|||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"schema_version": "fparkan-native-smoke-v1",
|
"schema_version": "fparkan-native-smoke-v1",
|
||||||
"commit_sha": "0123456789abcdef0123456789abcdef01234567",
|
"commit_sha": "0123456789abcdef0123456789abcdef01234567",
|
||||||
"rust_toolchain": "1.87.0",
|
"rust_toolchain": measured_rust_toolchain_version(),
|
||||||
"target_triple": format!("{platform}-test-target"),
|
"target_triple": format!("{platform}-test-target"),
|
||||||
"platform": platform,
|
"platform": platform,
|
||||||
"status": "passed",
|
"status": "passed",
|
||||||
@@ -2311,7 +2467,7 @@ mod tests {
|
|||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"schema_version": "fparkan-native-smoke-v1",
|
"schema_version": "fparkan-native-smoke-v1",
|
||||||
"commit_sha": "0123456789abcdef0123456789abcdef01234567",
|
"commit_sha": "0123456789abcdef0123456789abcdef01234567",
|
||||||
"rust_toolchain": "1.87.0",
|
"rust_toolchain": measured_rust_toolchain_version(),
|
||||||
"target_triple": "aarch64-apple-darwin",
|
"target_triple": "aarch64-apple-darwin",
|
||||||
"platform": "macos",
|
"platform": "macos",
|
||||||
"status": "blocked",
|
"status": "blocked",
|
||||||
@@ -2412,13 +2568,31 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn maps_stage_packages() {
|
fn maps_stage_packages() {
|
||||||
assert!(stage_packages(3).is_ok_and(|packages| packages.contains(&"fparkan-assets")));
|
assert!(stage_packages(0)
|
||||||
assert!(stage_packages(3).is_ok_and(|packages| packages.contains(&"fparkan-viewer")));
|
.is_ok_and(|packages| packages.contains(&"fparkan-platform".to_string())));
|
||||||
assert!(stage_packages(5).is_ok_and(|packages| packages.contains(&"fparkan-runtime")));
|
assert!(stage_packages(0)
|
||||||
assert!(stage_packages(5).is_ok_and(|packages| packages.contains(&"fparkan-game")));
|
.is_ok_and(|packages| packages.contains(&"fparkan-vulkan-smoke".to_string())));
|
||||||
|
assert!(stage_packages(1)
|
||||||
|
.is_ok_and(|packages| packages.contains(&"fparkan-inspection".to_string())));
|
||||||
|
assert!(stage_packages(5)
|
||||||
|
.is_ok_and(|packages| packages.contains(&"fparkan-runtime".to_string())));
|
||||||
|
assert!(
|
||||||
|
stage_packages(5).is_ok_and(|packages| packages.contains(&"fparkan-game".to_string()))
|
||||||
|
);
|
||||||
assert_eq!(stage_packages(9), Err("stage out of range: 9".to_string()));
|
assert_eq!(stage_packages(9), Err("stage out of range: 9".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stage_package_manifest_covers_workspace_once() -> Result<(), String> {
|
||||||
|
let manifest_path = workspace_relative_path(STAGE_PACKAGE_MANIFEST);
|
||||||
|
let manifest = load_stage_package_manifest(&manifest_path)?;
|
||||||
|
let workspace_packages = workspace_package_names(&workspace_root_path())?;
|
||||||
|
|
||||||
|
validate_stage_package_entries(&manifest, &workspace_packages, &manifest_path)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_manifest_dependencies_for_arch_policy() {
|
fn parses_manifest_dependencies_for_arch_policy() {
|
||||||
let manifest = r#"
|
let manifest = r#"
|
||||||
|
|||||||
Reference in New Issue
Block a user