This repo contains a implementation of the subunit v2 protocol in Rust. It provides an interface for both writing and reading subunit streams natively in rust. The subunit v2 protocol is documented in the testing-cabal/subunit repository.
Reading subunit packets first requires an object implementing the Read trait containing the subunit stream. The iter_stream() function is used to parse the contents and return an iterator of ScannedItem enums. For example, parsing a subunit stream from a file:
use std::fs::File;
use subunit::io::sync::iter_stream;
use subunit::types::stream::ScannedItem;
let f = File::open("results.subunit")?;
for item in iter_stream(f) {
match item? {
ScannedItem::Event(event) => {
// Process the event
println!("Got event: {:?}", event);
},
ScannedItem::UTF8chars(text) => {
// Process text
println!("Got text: {:?}", text);
},
ScannedItem::Unknown(bytes, err) => {
// Handle unknown data
eprintln!("Unknown data: {:?}", err);
},
}
}
# Ok::<(), Box<dyn std::error::Error>>(())In this example, the results.subunit file will be opened and parsed with a
ScannedItem for each packet in the file.
Writing a subunit packet first requires creating an event structure to describe the contents of the packet. The Event API uses a builder pattern for construction. For example:
use subunit::types::{event::Event, teststatus::TestStatus};
use chrono::{TimeZone, Utc};
let event_start = Event::new(TestStatus::InProgress)
.test_id("A_test_id")
.datetime(Utc.with_ymd_and_hms(2014, 7, 8, 9, 10, 11).unwrap())?
.tag("tag_a")
.tag("tag_b")
.build();
# Ok::<(), Box<dyn std::error::Error>>(())A typical test event normally involves 2 packets though, one to mark the start and the other to mark the finish of a test:
use subunit::types::{event::Event, teststatus::TestStatus};
use chrono::{TimeZone, Utc};
let event_end = Event::new(TestStatus::Success)
.test_id("A_test_id")
.datetime(Utc.with_ymd_and_hms(2014, 7, 8, 9, 12, 0).unwrap())?
.tag("tag_a")
.tag("tag_b")
.mime_type("text/plain;charset=utf8")
.file_content("stdout:''", b"stdout content")
.build();
# Ok::<(), Box<dyn std::error::Error>>(())Then you'll want to write the packet out to something. Anything that implements the std::io::Write trait can be used for the packets, including things like a File and a TCPStream. In this case we'll use Vec to keep it in memory:
use subunit::serialize::Serializable;
use subunit::types::{event::Event, teststatus::TestStatus};
use chrono::{TimeZone, Utc};
let mut subunit_stream: Vec<u8> = Vec::new();
let event_start = Event::new(TestStatus::InProgress)
.test_id("A_test_id")
.datetime(Utc.with_ymd_and_hms(2014, 7, 8, 9, 10, 11).unwrap())?
.tag("tag_a")
.tag("tag_b")
.build();
let event_end = Event::new(TestStatus::Success)
.test_id("A_test_id")
.datetime(Utc.with_ymd_and_hms(2014, 7, 8, 9, 12, 0).unwrap())?
.tag("tag_a")
.tag("tag_b")
.mime_type("text/plain;charset=utf8")
.file_content("stdout:''", b"stdout content")
.build();
event_start.serialize(&mut subunit_stream)?;
event_end.serialize(&mut subunit_stream)?;
# Ok::<(), Box<dyn std::error::Error>>(())With this the subunit_stream buffer will contain the contents of the subunit stream for that test event.