Skip to content

Commit 7c9d490

Browse files
committed
Add tests for JsonBuffer
Signed-off-by: Michael X. Grey <mxgrey@intrinsic.ai>
1 parent ae65f24 commit 7c9d490

File tree

2 files changed

+276
-3
lines changed

2 files changed

+276
-3
lines changed

src/buffer/json_buffer.rs

Lines changed: 268 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use bevy_ecs::{
3131

3232
use serde::{de::DeserializeOwned, Serialize};
3333

34-
use serde_json::Value as JsonMessage;
34+
pub use serde_json::Value as JsonMessage;
3535

3636
use smallvec::SmallVec;
3737

@@ -427,6 +427,19 @@ impl<'a> JsonMut<'a> {
427427
*self.modified = true;
428428
self.interface.insert(message)
429429
}
430+
431+
/// Modify the data of the underlying message. This is equivalent to calling
432+
/// [`Self::serialize`], modifying the value, and then calling [`Self::insert`].
433+
/// The benefit of this function is that you do not need to remember to
434+
/// insert after you have finished your modifications.
435+
pub fn modify(
436+
&mut self,
437+
f: impl FnOnce(&mut JsonMessage),
438+
) -> Result<(), serde_json::Error> {
439+
let mut message = self.serialize()?;
440+
f(&mut message);
441+
self.insert(message)
442+
}
430443
}
431444

432445
/// The return type for functions that give a JSON view of a message in a buffer.
@@ -794,3 +807,257 @@ impl Accessed for JsonBuffer {
794807
key.is_in_use()
795808
}
796809
}
810+
811+
#[cfg(test)]
812+
mod tests {
813+
use bevy_ecs::prelude::World;
814+
use crate::{prelude::*, testing::*};
815+
use serde::{Serialize, Deserialize};
816+
817+
#[derive(Serialize, Deserialize, Clone)]
818+
struct TestMessage {
819+
v_i32: i32,
820+
v_u32: u32,
821+
v_string: String,
822+
}
823+
824+
impl TestMessage {
825+
fn new() -> Self {
826+
Self {
827+
v_i32: 1,
828+
v_u32: 2,
829+
v_string: "hello".to_string(),
830+
}
831+
}
832+
}
833+
834+
#[test]
835+
fn test_json_count() {
836+
let mut context = TestingContext::minimal_plugins();
837+
838+
let workflow = context.spawn_io_workflow(|scope, builder| {
839+
let buffer = builder.create_buffer(BufferSettings::keep_all());
840+
let push_multiple_times = builder.commands().spawn_service(
841+
push_multiple_times_into_buffer.into_blocking_service()
842+
);
843+
let count = builder.commands().spawn_service(
844+
get_buffer_count.into_blocking_service()
845+
);
846+
847+
scope
848+
.input
849+
.chain(builder)
850+
.with_access(buffer)
851+
.then(push_multiple_times)
852+
.then(count)
853+
.connect(scope.terminate);
854+
});
855+
856+
let msg = TestMessage::new();
857+
let mut promise = context.command(
858+
|commands| commands.request(msg, workflow).take_response()
859+
);
860+
861+
context.run_with_conditions(&mut promise, Duration::from_secs(2));
862+
let count = promise.take().available().unwrap();
863+
assert_eq!(count, 5);
864+
assert!(context.no_unhandled_errors());
865+
}
866+
867+
fn push_multiple_times_into_buffer(
868+
In((value, key)): In<(TestMessage, BufferKey<TestMessage>)>,
869+
mut access: BufferAccessMut<TestMessage>,
870+
) -> JsonBufferKey {
871+
let mut buffer = access.get_mut(&key).unwrap();
872+
for _ in 0..5 {
873+
buffer.push(value.clone());
874+
}
875+
876+
key.into()
877+
}
878+
879+
fn get_buffer_count(
880+
In(key): In<JsonBufferKey>,
881+
world: &mut World,
882+
) -> usize {
883+
world.json_buffer_mut(&key, |access| {
884+
access.len()
885+
}).unwrap()
886+
}
887+
888+
#[test]
889+
fn test_modify_json_message() {
890+
let mut context = TestingContext::minimal_plugins();
891+
892+
let workflow = context.spawn_io_workflow(|scope, builder| {
893+
let buffer = builder.create_buffer(BufferSettings::keep_all());
894+
let push_multiple_times = builder.commands().spawn_service(
895+
push_multiple_times_into_buffer.into_blocking_service()
896+
);
897+
let modify_content = builder.commands().spawn_service(
898+
modify_buffer_content.into_blocking_service()
899+
);
900+
let drain_content = builder.commands().spawn_service(
901+
pull_each_buffer_item.into_blocking_service()
902+
);
903+
904+
scope
905+
.input
906+
.chain(builder)
907+
.with_access(buffer)
908+
.then(push_multiple_times)
909+
.then(modify_content)
910+
.then(drain_content)
911+
.connect(scope.terminate);
912+
});
913+
914+
let msg = TestMessage::new();
915+
let mut promise = context.command(
916+
|commands| commands.request(msg, workflow).take_response()
917+
);
918+
919+
context.run_with_conditions(&mut promise, Duration::from_secs(2));
920+
let values = promise.take().available().unwrap();
921+
assert_eq!(values.len(), 5);
922+
for i in 0..values.len() {
923+
let v_i32 = values[i].get("v_i32").unwrap().as_i64().unwrap();
924+
assert_eq!(v_i32, i as i64);
925+
}
926+
assert!(context.no_unhandled_errors());
927+
}
928+
929+
fn modify_buffer_content(
930+
In(key): In<JsonBufferKey>,
931+
world: &mut World,
932+
) -> JsonBufferKey {
933+
world.json_buffer_mut(&key, |mut access| {
934+
for i in 0..access.len() {
935+
access.get_mut(i).unwrap().modify(|value| {
936+
let v_i32 = value.get_mut("v_i32").unwrap();
937+
let modified_v_i32 = i as i64 * v_i32.as_i64().unwrap();
938+
*v_i32 = modified_v_i32.into();
939+
}).unwrap();
940+
}
941+
}).unwrap();
942+
943+
key
944+
}
945+
946+
fn pull_each_buffer_item(
947+
In(key): In<JsonBufferKey>,
948+
world: &mut World,
949+
) -> Vec<JsonMessage> {
950+
world.json_buffer_mut(&key, |mut access| {
951+
let mut values = Vec::new();
952+
while let Ok(Some(value)) = access.pull() {
953+
values.push(value);
954+
}
955+
values
956+
}).unwrap()
957+
}
958+
959+
#[test]
960+
fn test_drain_json_message() {
961+
let mut context = TestingContext::minimal_plugins();
962+
963+
let workflow = context.spawn_io_workflow(|scope, builder| {
964+
let buffer = builder.create_buffer(BufferSettings::keep_all());
965+
let push_multiple_times = builder.commands().spawn_service(
966+
push_multiple_times_into_buffer.into_blocking_service()
967+
);
968+
let modify_content = builder.commands().spawn_service(
969+
modify_buffer_content.into_blocking_service()
970+
);
971+
let drain_content = builder.commands().spawn_service(
972+
drain_buffer_contents.into_blocking_service()
973+
);
974+
975+
scope
976+
.input
977+
.chain(builder)
978+
.with_access(buffer)
979+
.then(push_multiple_times)
980+
.then(modify_content)
981+
.then(drain_content)
982+
.connect(scope.terminate);
983+
});
984+
985+
let msg = TestMessage::new();
986+
let mut promise = context.command(
987+
|commands| commands.request(msg, workflow).take_response()
988+
);
989+
990+
context.run_with_conditions(&mut promise, Duration::from_secs(2));
991+
let values = promise.take().available().unwrap();
992+
assert_eq!(values.len(), 5);
993+
for i in 0..values.len() {
994+
let v_i32 = values[i].get("v_i32").unwrap().as_i64().unwrap();
995+
assert_eq!(v_i32, i as i64);
996+
}
997+
assert!(context.no_unhandled_errors());
998+
}
999+
1000+
fn drain_buffer_contents(
1001+
In(key): In<JsonBufferKey>,
1002+
world: &mut World,
1003+
) -> Vec<JsonMessage> {
1004+
world.json_buffer_mut(&key, |mut access| {
1005+
access
1006+
.drain(..)
1007+
.collect::<Result<Vec<_>, _>>()
1008+
})
1009+
.unwrap()
1010+
.unwrap()
1011+
}
1012+
1013+
#[test]
1014+
fn double_json_messages() {
1015+
let mut context = TestingContext::minimal_plugins();
1016+
1017+
let workflow = context.spawn_io_workflow(|scope, builder| {
1018+
let buffer_double_u32: JsonBuffer = builder.create_buffer::<TestMessage>(BufferSettings::default()).into();
1019+
let buffer_double_i32: JsonBuffer = builder.create_buffer::<TestMessage>(BufferSettings::default()).into();
1020+
let buffer_double_string: JsonBuffer = builder.create_buffer::<TestMessage>(BufferSettings::default()).into();
1021+
1022+
scope
1023+
.input
1024+
.chain(builder)
1025+
.fork_clone((
1026+
|chain: Chain<_>| chain
1027+
.map_block(|mut msg: TestMessage| {
1028+
msg.v_u32 *= 2;
1029+
msg
1030+
})
1031+
.connect(buffer_double_u32.downcast::<TestMessage>().unwrap().input_slot()),
1032+
|chain: Chain<_>| chain
1033+
.map_block(|mut msg: TestMessage| {
1034+
msg.v_i32 *= 2;
1035+
msg
1036+
})
1037+
.connect(buffer_double_i32.downcast::<TestMessage>().unwrap().input_slot()),
1038+
|chain: Chain<_>| chain
1039+
.map_block(|mut msg: TestMessage| {
1040+
msg.v_string = msg.v_string.clone() + &msg.v_string;
1041+
msg
1042+
})
1043+
.connect(buffer_double_string.downcast::<TestMessage>().unwrap().input_slot()),
1044+
));
1045+
1046+
(buffer_double_u32, buffer_double_i32, buffer_double_string)
1047+
.join(builder)
1048+
.connect(scope.terminate);
1049+
});
1050+
1051+
let msg = TestMessage::new();
1052+
let mut promise = context.command(
1053+
|commands| commands.request(msg, workflow).take_response()
1054+
);
1055+
1056+
context.run_with_conditions(&mut promise, Duration::from_secs(2));
1057+
let (double_u32, double_i32, double_string) = promise.take().available().unwrap();
1058+
assert_eq!(4, double_u32.get("v_u32").unwrap().as_i64().unwrap());
1059+
assert_eq!(2, double_i32.get("v_i32").unwrap().as_i64().unwrap());
1060+
assert_eq!("hellohello", double_string.get("v_string").unwrap().as_str().unwrap());
1061+
assert!(context.no_unhandled_errors());
1062+
}
1063+
}

src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,9 @@ impl Plugin for ImpulsePlugin {
336336
pub mod prelude {
337337
pub use crate::{
338338
buffer::{
339-
AnyBuffer, AnyBufferKey, AnyBufferWorldAccess, Buffer, BufferAccess, BufferAccessMut,
340-
BufferKey, BufferSettings, Bufferable, Buffered, IterBufferable, RetentionPolicy,
339+
AnyBuffer, AnyBufferKey, AnyBufferMut, AnyBufferWorldAccess, AnyMessage,
340+
Buffer, BufferAccess, BufferAccessMut, BufferKey, BufferSettings, Bufferable,
341+
Buffered, IterBufferable, RetentionPolicy,
341342
},
342343
builder::Builder,
343344
callback::{AsCallback, Callback, IntoAsyncCallback, IntoBlockingCallback},
@@ -362,4 +363,9 @@ pub mod prelude {
362363
BlockingCallback, BlockingCallbackInput, BlockingMap, BlockingService,
363364
BlockingServiceInput, ContinuousQuery, ContinuousService, ContinuousServiceInput,
364365
};
366+
367+
#[cfg(feature = "diagram")]
368+
pub use crate::buffer::{
369+
JsonBuffer, JsonBufferKey, JsonBufferMut, JsonBufferWorldAccess, JsonMessage,
370+
};
365371
}

0 commit comments

Comments
 (0)