use super::*;
use crate::asset_id::AssetId;
use crate::patching::topology::Port;
use crate::test_support::{collections_with, rgb_bar};
fn one_definition(definition: FixtureDefinition) -> (Collections, AssetId<FixtureDefinition>) {
let (collections, ids) = collections_with(vec![definition]);
(collections, ids[0])
}
fn rgb_collections() -> (Collections, AssetId<FixtureDefinition>) {
one_definition(rgb_bar(1, 0.05))
}
fn add(patch: &mut Patch, name: &str, definition: AssetId<FixtureDefinition>) -> FixtureId {
patch.add_fixture(
Fixture::with_definition(name, definition),
LayoutPosition::default(),
)
}
fn patch_with_one_fixture() -> (Patch, FixtureId, Collections) {
let (collections, definition) = rgb_collections();
let mut patch = Patch::default();
let id = add(&mut patch, "a", definition);
(patch, id, collections)
}
#[test]
fn addresses_follow_the_chain() {
let (collections, definition) = rgb_collections();
let mut patch = Patch::default();
let ids: Vec<FixtureId> = (0..3)
.map(|i| add(&mut patch, &format!("f{i}"), definition))
.collect();
patch.topology.make_successor(ids[0], ids[1]);
patch.topology.make_successor(ids[1], ids[2]);
let addresses = patch.addresses(&collections);
assert_eq!(addresses[&ids[0]].into_pair(), (0, 0));
assert_eq!(addresses[&ids[1]].into_pair(), (0, 3));
assert_eq!(addresses[&ids[2]].into_pair(), (0, 6));
}
#[test]
fn a_new_chain_starts_past_a_chain_that_overflowed() {
let (collections, definition) = one_definition(rgb_bar(100, 1.0));
let mut patch = Patch::default();
let head = add(&mut patch, "head", definition);
let tail = add(&mut patch, "tail", definition);
patch.topology.make_successor(head, tail);
let addresses = patch.addresses(&collections);
assert_eq!(addresses[&head].into_pair(), (0, 0));
assert_eq!(addresses[&tail].into_pair(), (1, 0));
assert_eq!(
patch.occupied_universes(&collections, None),
BTreeSet::from([0, 1])
);
assert_eq!(patch.lowest_free_universe(&collections, None), 2);
}
#[test]
fn the_chain_being_placed_is_not_in_its_own_way() {
let (collections, definition) = rgb_collections();
let mut patch = Patch::default();
let first = add(&mut patch, "first", definition);
let second = add(&mut patch, "second", definition);
assert_eq!(patch.lowest_free_universe(&collections, Some(second)), 1);
assert_eq!(patch.lowest_free_universe(&collections, Some(first)), 0);
}
#[test]
fn a_freed_universe_is_handed_out_again() {
let (collections, definition) = rgb_collections();
let mut patch = Patch::default();
let first = add(&mut patch, "first", definition);
add(&mut patch, "second", definition);
patch.remove_fixture(first);
assert_eq!(patch.lowest_free_universe(&collections, None), 0);
}
#[test]
fn a_fixture_never_straddles_a_universe_boundary() {
let (collections, definition) = rgb_collections();
let mut patch = Patch::default();
let ids: Vec<FixtureId> = (0..171)
.map(|i| add(&mut patch, &format!("f{i}"), definition))
.collect();
for pair in ids.windows(2) {
patch.topology.make_successor(pair[0], pair[1]);
}
let addresses = patch.addresses(&collections);
assert_eq!(addresses[&ids[169]].into_pair(), (0, 507));
assert_eq!(addresses[&ids[170]].into_pair(), (1, 0));
}
#[test]
fn a_chain_on_a_high_universe_is_addressed() {
let (collections, definition) = one_definition(rgb_bar(100, 1.0));
let mut patch = Patch::default();
let head = add(&mut patch, "head", definition);
let tail = add(&mut patch, "tail", definition);
patch.topology.make_successor(head, tail);
patch.topology.ports.insert(head, Port::new(1000));
let addresses = patch.addresses(&collections);
assert_eq!(addresses[&head].into_pair(), (1000, 0));
assert_eq!(addresses[&tail].into_pair(), (1001, 0));
}
#[test]
fn a_fixture_past_the_highest_universe_gets_no_address() {
let (collections, definition) = one_definition(rgb_bar(100, 1.0));
let mut patch = Patch::default();
let head = add(&mut patch, "head", definition);
let tail = add(&mut patch, "tail", definition);
patch.topology.make_successor(head, tail);
let beyond = add(&mut patch, "beyond", definition);
patch.topology.ports.insert(head, Port::new(MAX_UNIVERSE));
patch.topology.ports.insert(beyond, Port::new(u16::MAX));
let addresses = patch.addresses(&collections);
assert_eq!(addresses[&head].into_pair(), (32767, 0));
assert_eq!(addresses.get(&tail), None);
assert_eq!(addresses.get(&beyond), None);
}
#[test]
fn separate_chains_start_on_their_own_universe() {
let (collections, definition) = rgb_collections();
let mut patch = Patch::default();
let a = add(&mut patch, "a", definition);
let b = add(&mut patch, "b", definition);
let addresses = patch.addresses(&collections);
assert_eq!(addresses[&a].into_pair(), (0, 0));
assert_eq!(addresses[&b].into_pair(), (1, 0));
}
#[test]
fn default_layout_mode_falls_back_to_the_definition_default() {
let (patch, id, collections) = patch_with_one_fixture();
assert_eq!(
patch.fixture_mode(LayoutRef::Topology, 0, id, &collections),
Some(0)
);
}
#[test]
fn a_fixture_without_a_definition_resolves_to_nothing() {
let (collections, _) = rgb_collections();
let mut patch = Patch::default();
let id = patch.add_fixture(Fixture::new("orphan"), LayoutPosition::default());
assert!(patch.definition(id, &collections).is_none());
assert_eq!(
patch.fixture_mode(LayoutRef::Topology, 0, id, &collections),
None
);
assert!(!patch.addresses(&collections).contains_key(&id));
}
#[test]
fn an_undefined_fixture_stops_addressing_its_chain() {
let (collections, definition) = rgb_collections();
let mut patch = Patch::default();
let head = add(&mut patch, "head", definition);
let orphan = patch.add_fixture(Fixture::new("orphan"), LayoutPosition::default());
let tail = add(&mut patch, "tail", definition);
patch.topology.make_successor(head, orphan);
patch.topology.make_successor(orphan, tail);
let addresses = patch.addresses(&collections);
assert_eq!(addresses[&head].into_pair(), (0, 0));
assert!(!addresses.contains_key(&orphan));
assert!(!addresses.contains_key(&tail));
}
#[test]
fn a_user_mode_falls_back_for_unassigned_fixtures() {
let (mut patch, id, collections) = patch_with_one_fixture();
let layout = LayoutRef::Layout(patch.add_layout());
patch.add_member(layout, id);
let mode = patch
.layout_mut(layout)
.expect("the layout was just added")
.add_mode();
assert_eq!(patch.fixture_mode(layout, mode, id, &collections), Some(0));
}
#[test]
fn an_assigned_fixture_mode_wins() {
let (mut patch, id, collections) = patch_with_one_fixture();
let layout = LayoutRef::Layout(patch.add_layout());
patch.add_member(layout, id);
let mode = patch
.layout_mut(layout)
.expect("the layout was just added")
.add_mode();
patch.set_fixture_mode(layout, mode, id, 1);
assert_eq!(
patch.fixture_mode(layout, mode, id, &collections),
Some(0),
"an index the definition does not have falls back to its default"
);
patch.set_fixture_mode(layout, mode, id, 0);
assert_eq!(patch.fixture_mode(layout, mode, id, &collections), Some(0));
}
#[test]
fn assignments_are_per_layout_mode() {
let (mut patch, id, collections) = patch_with_one_fixture();
let layout = LayoutRef::Layout(patch.add_layout());
patch.add_member(layout, id);
let mode = patch
.layout_mut(layout)
.expect("the layout was just added")
.add_mode();
patch.set_fixture_mode(layout, mode, id, 0);
assert!(
patch
.layout(layout)
.expect("the layout was just added")
.mode(0)
.fixture_modes
.is_empty(),
"the default mode stores nothing"
);
assert_eq!(patch.fixture_mode(layout, 0, id, &collections), Some(0));
}
#[test]
fn layouts_are_overlapping_subsets_with_their_own_positions() {
let (mut patch, id, _) = patch_with_one_fixture();
let a = LayoutRef::Layout(patch.add_layout());
let b = LayoutRef::Layout(patch.add_layout());
patch.add_member(a, id);
patch.add_member(b, id);
patch
.layout_mut(a)
.expect("the layout was just added")
.members
.get_mut(&id)
.expect("the fixture was just added to the layout")
.pos
.x = 500.0;
assert_eq!(
patch.layout(b).expect("the layout was just added").members[&id]
.pos
.x,
100.0
);
assert_eq!(
patch.topology.default_layout.members[&id].pos.x, 100.0,
"moving a fixture in one layout leaves the others alone"
);
}
#[test]
fn topology_membership_cannot_be_switched_off() {
let (mut patch, id, _) = patch_with_one_fixture();
patch.set_member(LayoutRef::Topology, id, false);
assert!(patch.topology.default_layout.contains(id));
}
#[test]
fn setting_membership_adds_and_removes() {
let (mut patch, id, _) = patch_with_one_fixture();
let layout = LayoutRef::Layout(patch.add_layout());
patch.set_member(layout, id, true);
assert!(
patch
.layout(layout)
.expect("the layout was just added")
.contains(id)
);
patch.set_member(layout, id, false);
assert!(
!patch
.layout(layout)
.expect("the layout was just added")
.contains(id)
);
}
#[test]
fn a_patch_survives_serialization() {
let mut patch = Patch::default();
let a = patch.add_fixture(
Fixture::new("a"),
LayoutPosition::new(emath::pos2(12.0, 34.0), 0.5, [1.5, 2.0]),
);
let b = patch.add_fixture(Fixture::new("b"), LayoutPosition::default());
patch.topology.make_successor(a, b);
patch.topology.default_layout.background = Some(std::path::PathBuf::from("/tmp/stage.png"));
let layout = LayoutRef::Layout(patch.add_layout());
patch.add_member(layout, a);
patch
.layout_mut(layout)
.expect("the layout was just added")
.name = "Truss".to_string();
let mode = patch
.layout_mut(layout)
.expect("the layout was just added")
.add_mode();
patch.set_fixture_mode(layout, mode, a, 0);
let json = serde_json::to_string(&patch).expect("serialize");
let restored: Patch = serde_json::from_str(&json).expect("deserialize");
assert_eq!(patch, restored);
assert_eq!(restored.topology.chain_from(a), vec![a, b]);
assert_eq!(
restored
.layout(layout)
.expect("the layout was just added")
.name,
"Truss"
);
assert_eq!(
restored
.layout(layout)
.expect("the layout was just added")
.mode(mode)
.fixture_modes[&a],
0
);
}
#[test]
fn a_missing_patch_deserializes_to_the_default() {
let restored: Patch = serde_json::from_str("{}").expect("deserialize");
assert_eq!(restored, Patch::default());
assert!(restored.topology.fixtures.is_empty());
assert!(restored.layouts.is_empty());
}
#[test]
fn a_definition_with_more_channels_shifts_the_chain() {
let (collections, definition) = one_definition(rgb_bar(2, 0.1));
let mut patch = Patch::default();
let a = add(&mut patch, "a", definition);
let b = add(&mut patch, "b", definition);
patch.topology.make_successor(a, b);
let addresses = patch.addresses(&collections);
assert_eq!(addresses[&a].into_pair(), (0, 0));
assert_eq!(addresses[&b].into_pair(), (0, 6));
}