Skip to content

Commit 4ab1a45

Browse files
authored
Fix up bevy 0.19 regressions at load time (#8)
* ported to the new bevy 0.19 * Improved the resource loading * Bevy regression fixed on flight controller * remove tests downloading resources from CI
1 parent 53ffdad commit 4ab1a45

5 files changed

Lines changed: 191 additions & 27 deletions

File tree

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
## Testing
99
- [ ] `just fmt-check`
1010
- [ ] `just check`
11-
- [ ] `just test`
11+
- [ ] `just test` (local-only host tests, as needed)
1212
- [ ] Other (please specify):
1313

14-
pro-tip: `just` with no parameters in the root defaults to `just fmt-check`, `just check`, and `just test`.
14+
pro-tip: `just` with no parameters in the root defaults to `just fmt-check` and `just check`.
1515

1616
## Checklist
1717
- [ ] I have updated docs or examples where needed

.github/workflows/ci.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,6 @@ jobs:
5353
cargo check -p cu-feetech-demo --all-targets
5454
cargo check -p cu-gnss-ublox-demo --all-targets --features logexport
5555
56-
- name: Run host tests
57-
run: |
58-
cargo test -p cu-human-pose
59-
cargo test -p cu-flight-controller --bin quad-sim --features textlogs
60-
cargo test -p cu-rp-balancebot --bin balancebot-sim
61-
6256
embedded:
6357
name: Embedded Example Checks
6458
runs-on: ubuntu-latest

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,18 @@ workflow via GitHub's `workflow_dispatch` API.
4242
just pr-check
4343
```
4444

45-
`just pr-check` runs formatting verification, the existing host/embedded compile
46-
smoke checks, and the host-side unit tests that are stable in CI.
45+
`just pr-check` runs formatting verification and the existing host/embedded
46+
compile smoke checks.
4747

4848
`just check` remains available when you only want the compile-smoke pass. The
4949
host checks cover the Linux-targeted apps. `cu-human-pose` needs GStreamer
5050
development packages installed locally. The embedded smoke checks use cross-target
5151
`cargo check` against the same RP2350 and STM32H7 targets exercised by these
5252
demos.
5353

54+
Host-side tests are local-only and run on demand with `just test`; they are not
55+
part of CI because some examples download large assets or models at test time.
56+
5457
## License
5558

5659
This repository is licensed under the Apache License, Version 2.0.

examples/cu_flight_controller/src/sim.rs

Lines changed: 182 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -723,8 +723,15 @@ type BevyMonUnifiedLogger = UnifiedLoggerWrite;
723723
fn setup_copper(mut commands: Commands) {
724724
#[allow(clippy::identity_op)]
725725
const LOG_SLAB_SIZE: Option<usize> = Some(128 * 1024 * 1024);
726-
let logger_path = "logs/flight_controller_sim.copper";
727-
if let Some(parent) = Path::new(logger_path).parent()
726+
commands.insert_resource(build_sim_copper_state(
727+
Path::new("logs/flight_controller_sim.copper"),
728+
LOG_SLAB_SIZE,
729+
));
730+
}
731+
732+
#[cfg(all(not(target_arch = "wasm32"), feature = "sim"))]
733+
fn build_sim_copper_state(logger_path: &Path, log_slab_size: Option<usize>) -> CopperState {
734+
if let Some(parent) = logger_path.parent()
728735
&& !parent.exists()
729736
{
730737
fs::create_dir_all(parent).expect("failed to create logs directory");
@@ -733,7 +740,7 @@ fn setup_copper(mut commands: Commands) {
733740
let (clock, clock_mock) = RobotClock::mock();
734741
let mut app = gnss::FlightControllerSim::builder()
735742
.with_clock(clock.clone())
736-
.with_log_path(PathBuf::from(logger_path), LOG_SLAB_SIZE)
743+
.with_log_path(PathBuf::from(logger_path), log_slab_size)
737744
.expect("failed to create logger")
738745
.with_sim_callback(&mut default_callback)
739746
.build()
@@ -742,11 +749,11 @@ fn setup_copper(mut commands: Commands) {
742749
app.start_all_tasks(&mut default_callback)
743750
.expect("failed to start tasks");
744751

745-
commands.insert_resource(CopperState {
752+
CopperState {
746753
clock,
747754
clock_mock,
748755
app,
749-
});
756+
}
750757
}
751758

752759
#[cfg(feature = "bevymon")]
@@ -1506,11 +1513,29 @@ fn run_copper(
15061513
mut osd_overlay: ResMut<SimOsdOverlay>,
15071514
mut exit_writer: MessageWriter<AppExit>,
15081515
) {
1509-
copper
1510-
.clock_mock
1511-
.set_value(physics_time.elapsed().as_nanos() as u64);
1512-
let vehicle = sim_state.vehicle.clone();
1513-
let rc = rc_input.clone();
1516+
let elapsed_ns = physics_time.elapsed().as_nanos() as u64;
1517+
if let Err(err) = run_copper_iteration(
1518+
&mut copper,
1519+
elapsed_ns,
1520+
sim_state.vehicle.clone(),
1521+
rc_input.clone(),
1522+
&mut motor_commands,
1523+
&mut osd_overlay,
1524+
) {
1525+
error!("sim loop stopped: {}", err);
1526+
exit_writer.write(AppExit::Success);
1527+
}
1528+
}
1529+
1530+
fn run_copper_iteration(
1531+
copper: &mut CopperState,
1532+
elapsed_ns: u64,
1533+
vehicle: SimVehicleState,
1534+
rc: SimRcInput,
1535+
motor_commands: &mut SimMotorCommands,
1536+
osd_overlay: &mut SimOsdOverlay,
1537+
) -> CuResult<()> {
1538+
copper.clock_mock.set_value(elapsed_ns);
15141539
sim_battery_set_armed(rc.armed);
15151540
sim_battery_set_throttle(rc.throttle);
15161541
sim_gnss_set_vehicle_state(
@@ -1596,10 +1621,7 @@ fn run_copper(
15961621
}
15971622
};
15981623

1599-
if let Err(err) = copper.app.run_one_iteration(&mut sim_callback) {
1600-
error!("sim loop stopped: {}", err);
1601-
exit_writer.write(AppExit::Success);
1602-
}
1624+
copper.app.run_one_iteration(&mut sim_callback)
16031625
}
16041626

16051627
fn apply_multicopter_dynamics(
@@ -2047,6 +2069,7 @@ fn register_scene_reflect_types(app: &mut App) {
20472069
app.register_type::<bevy::prelude::Handle<bevy::prelude::WorldAsset>>();
20482070
app.register_type::<bevy::gltf::GltfExtras>();
20492071
app.register_type::<bevy::gltf::GltfSceneExtras>();
2072+
app.register_type::<bevy::gltf::GltfSceneName>();
20502073
app.register_type::<bevy::gltf::GltfMeshExtras>();
20512074
app.register_type::<bevy::gltf::GltfMeshName>();
20522075
app.register_type::<bevy::gltf::GltfMaterialExtras>();
@@ -2351,12 +2374,157 @@ mod tests {
23512374
);
23522375
}
23532376

2377+
#[cfg(not(target_arch = "wasm32"))]
2378+
fn build_test_copper_state() -> CopperState {
2379+
let (clock, clock_mock) = RobotClock::mock();
2380+
let mut sim_callback = default_callback;
2381+
let mut app = gnss::FlightControllerSim::builder()
2382+
.with_clock(clock.clone())
2383+
.with_sim_callback(&mut sim_callback)
2384+
.build()
2385+
.expect("failed to create runtime");
2386+
2387+
app.start_all_tasks(&mut sim_callback)
2388+
.expect("failed to start tasks");
2389+
2390+
CopperState {
2391+
clock,
2392+
clock_mock,
2393+
app,
2394+
}
2395+
}
2396+
2397+
#[cfg(not(target_arch = "wasm32"))]
2398+
fn build_scene_asset_test_app(register_reflect_types: bool) -> App {
2399+
let mut app = App::new();
2400+
app.add_plugins((
2401+
MinimalPlugins,
2402+
asset_plugin(),
2403+
bevy::world_serialization::WorldSerializationPlugin,
2404+
bevy::image::ImagePlugin::default(),
2405+
bevy::mesh::MeshPlugin,
2406+
bevy::pbr::MaterialPlugin::<bevy::prelude::StandardMaterial>::default(),
2407+
bevy::gltf::GltfPlugin::default(),
2408+
));
2409+
app.insert_resource(bevy::image::CompressedImageFormatSupport(
2410+
bevy::image::CompressedImageFormats::NONE,
2411+
));
2412+
if register_reflect_types {
2413+
register_scene_reflect_types(&mut app);
2414+
}
2415+
app.finish();
2416+
app
2417+
}
2418+
2419+
#[cfg(not(target_arch = "wasm32"))]
2420+
fn load_scene_asset_handles(
2421+
app: &mut App,
2422+
scene_assets: &SceneAssetPaths,
2423+
) -> (Handle<WorldAsset>, Handle<WorldAsset>) {
2424+
let asset_server = app.world().resource::<AssetServer>();
2425+
(
2426+
asset_server.load(GltfAssetLabel::Scene(0).from_asset(scene_assets.quadcopter.clone())),
2427+
asset_server.load(GltfAssetLabel::Scene(0).from_asset(scene_assets.city.clone())),
2428+
)
2429+
}
2430+
2431+
#[cfg(not(target_arch = "wasm32"))]
2432+
fn wait_for_scene_asset_loads(
2433+
app: &mut App,
2434+
quadcopter_scene: &Handle<WorldAsset>,
2435+
city_scene: &Handle<WorldAsset>,
2436+
) -> bool {
2437+
for _ in 0..300 {
2438+
app.update();
2439+
let asset_server = app.world().resource::<AssetServer>();
2440+
if asset_server.is_loaded_with_dependencies(quadcopter_scene.id())
2441+
&& asset_server.is_loaded_with_dependencies(city_scene.id())
2442+
{
2443+
return true;
2444+
}
2445+
std::thread::sleep(std::time::Duration::from_millis(10));
2446+
}
2447+
false
2448+
}
2449+
23542450
#[test]
23552451
fn sim_world_starts() {
23562452
let mut app = build_world(true, false);
23572453
app.update();
23582454
}
23592455

2456+
#[cfg(not(target_arch = "wasm32"))]
2457+
#[test]
2458+
fn sim_copper_runs_one_iteration() {
2459+
let mut copper = build_test_copper_state();
2460+
let mut motors = SimMotorCommands::default();
2461+
let mut osd_overlay = SimOsdOverlay::default();
2462+
2463+
run_copper_iteration(
2464+
&mut copper,
2465+
0,
2466+
SimVehicleState::default(),
2467+
SimRcInput::default(),
2468+
&mut motors,
2469+
&mut osd_overlay,
2470+
)
2471+
.expect("copper sim iteration should keep running");
2472+
2473+
copper
2474+
.app
2475+
.stop_all_tasks(&mut default_callback)
2476+
.expect("failed to stop tasks");
2477+
copper
2478+
.app
2479+
.log_shutdown_completed()
2480+
.expect("failed to log shutdown");
2481+
}
2482+
2483+
#[cfg(not(target_arch = "wasm32"))]
2484+
#[test]
2485+
fn sim_gltf_world_assets_load() {
2486+
let scene_assets = prepare_scene_assets();
2487+
let mut app = build_scene_asset_test_app(false);
2488+
let (quadcopter_scene, city_scene) = load_scene_asset_handles(&mut app, &scene_assets);
2489+
2490+
if wait_for_scene_asset_loads(&mut app, &quadcopter_scene, &city_scene) {
2491+
return;
2492+
}
2493+
2494+
let asset_server = app.world().resource::<AssetServer>();
2495+
panic!(
2496+
"scene assets did not load: quadcopter_loaded={} city_loaded={}",
2497+
asset_server.is_loaded_with_dependencies(quadcopter_scene.id()),
2498+
asset_server.is_loaded_with_dependencies(city_scene.id())
2499+
);
2500+
}
2501+
2502+
#[cfg(not(target_arch = "wasm32"))]
2503+
#[test]
2504+
fn sim_gltf_world_assets_clone_with_registered_types() {
2505+
let scene_assets = prepare_scene_assets();
2506+
let mut app = build_scene_asset_test_app(true);
2507+
let (quadcopter_scene, city_scene) = load_scene_asset_handles(&mut app, &scene_assets);
2508+
assert!(
2509+
wait_for_scene_asset_loads(&mut app, &quadcopter_scene, &city_scene),
2510+
"scene assets did not load"
2511+
);
2512+
2513+
let registry = app
2514+
.world()
2515+
.resource::<bevy::ecs::reflect::AppTypeRegistry>()
2516+
.clone();
2517+
let world_assets = app.world().resource::<Assets<WorldAsset>>();
2518+
for (name, handle) in [("quadcopter", &quadcopter_scene), ("city", &city_scene)] {
2519+
let world_asset = world_assets
2520+
.get(handle)
2521+
.unwrap_or_else(|| panic!("{name} world asset did not load"));
2522+
world_asset
2523+
.clone_with(&registry)
2524+
.unwrap_or_else(|err| panic!("{name} world asset cannot spawn: {err}"));
2525+
}
2526+
}
2527+
23602528
#[test]
23612529
fn sim_world_magnetic_field_is_three_dimensional() {
23622530
let world_mag = Vec3::from_array(WORLD_MAG_FIELD_UT);

justfile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ default: pr-check
33
pr-check:
44
just fmt-check
55
just check
6-
just test
76

87
fmt:
98
cargo fmt --all
@@ -24,8 +23,8 @@ check-embedded:
2423
cargo check -p cu-elrs-bdshot-demo --target thumbv8m.main-none-eabihf
2524
cargo check -p cu-flight-controller --target thumbv7em-none-eabihf --no-default-features --features firmware,textlogs --bin quad
2625

27-
# Host-side tests only. The embedded demo is compile-checked separately and the
28-
# balancebot resim/logreader paths pull in local-only or Python-linked flows.
26+
# Local host-side tests only. These are intentionally not part of pr-check or CI
27+
# because some host examples download large assets/models at test time.
2928
test:
3029
cargo test -p cu-human-pose
3130
cargo test -p cu-flight-controller --bin quad-sim --features textlogs

0 commit comments

Comments
 (0)