| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #![cfg(test)] |
| |
| use super::*; |
| use assert_matches::assert_matches; |
| use diagnostics_data::*; |
| use diagnostics_log_encoding::encode::Encoder; |
| use diagnostics_log_encoding::Record; |
| use fidl_fuchsia_diagnostics::Severity as StreamSeverity; |
| use fidl_fuchsia_logger::{LogLevelFilter, LogMessage}; |
| use lazy_static::lazy_static; |
| use std::io::Cursor; |
| use std::sync::Arc; |
| |
| lazy_static! { |
| static ref TEST_IDENTITY: Arc<MonikerWithUrl> = { |
| Arc::new(MonikerWithUrl { |
| moniker: "fake-test-env/test-component".to_string(), |
| url: "fuchsia-pkg://fuchsia.com/testing123#test-component.cm".to_string(), |
| }) |
| }; |
| } |
| |
| fn clear_raw_severity(data: &mut LogsData) { |
| data.payload_message_mut() |
| .unwrap() |
| .properties |
| .retain(|p| !matches!(p, LogsProperty::Int(LogsField::RawSeverity, _))); |
| } |
| |
| #[repr(C, packed)] |
| struct fx_log_metadata_t_packed { |
| pid: zx::sys::zx_koid_t, |
| tid: zx::sys::zx_koid_t, |
| time: zx::sys::zx_time_t, |
| severity: fx_log_severity_t, |
| dropped_logs: u32, |
| } |
| |
| #[repr(C, packed)] |
| struct fx_log_packet_t_packed { |
| metadata: fx_log_metadata_t_packed, |
| /// Contains concatenated tags and message and a null terminating character at the end. |
| /// `char(tag_len) + "tag1" + char(tag_len) + "tag2\0msg\0"` |
| data: [c_char; MAX_DATAGRAM_LEN - METADATA_SIZE], |
| } |
| |
| #[fuchsia::test] |
| fn abi_test() { |
| assert_eq!(METADATA_SIZE, 32); |
| assert_eq!(MAX_TAGS, 5); |
| assert_eq!(MAX_TAG_LEN, 64); |
| assert_eq!(mem::size_of::<fx_log_metadata_t>(), METADATA_SIZE); |
| assert_eq!(mem::size_of::<fx_log_packet_t>(), MAX_DATAGRAM_LEN); |
| |
| // Test that there is no padding |
| assert_eq!(mem::size_of::<fx_log_packet_t>(), mem::size_of::<fx_log_packet_t_packed>()); |
| |
| assert_eq!(mem::size_of::<fx_log_metadata_t>(), mem::size_of::<fx_log_metadata_t_packed>()); |
| } |
| fn test_packet() -> fx_log_packet_t { |
| let mut packet: fx_log_packet_t = Default::default(); |
| packet.metadata.pid = 1; |
| packet.metadata.tid = 2; |
| packet.metadata.time = 3; |
| packet.metadata.severity = LogLevelFilter::Debug as i32; |
| packet.metadata.dropped_logs = 10; |
| packet |
| } |
| |
| fn get_test_identity() -> MonikerWithUrl { |
| (**TEST_IDENTITY).clone() |
| } |
| |
| #[fuchsia::test] |
| fn short_reads() { |
| let packet = test_packet(); |
| let one_short = &packet.as_bytes()[..METADATA_SIZE]; |
| let two_short = &packet.as_bytes()[..METADATA_SIZE - 1]; |
| |
| assert_eq!(LoggerMessage::try_from(one_short), Err(MessageError::ShortRead { len: 32 })); |
| |
| assert_eq!(LoggerMessage::try_from(two_short), Err(MessageError::ShortRead { len: 31 })); |
| } |
| |
| #[fuchsia::test] |
| fn invalid_utf8_is_handled() { |
| let mut packet = test_packet(); |
| // No tags |
| packet.data[0] = 0; |
| // Start message |
| let bytes = b"This is \xF0\x90\x80 invalid"; |
| for i in 1..=bytes.len() { |
| packet.data[i] = bytes[i - 1] as c_char; |
| } |
| packet.data[bytes.len() + 1] = 0; |
| let buffer = &packet.as_bytes()[..METADATA_SIZE + bytes.len() + 2]; |
| let logger_message = LoggerMessage::try_from(buffer).unwrap(); |
| let parsed = crate::from_logger(get_test_identity(), logger_message); |
| let expected = LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: packet.metadata.time.into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Debug, |
| }) |
| .set_dropped(packet.metadata.dropped_logs.into()) |
| .set_pid(packet.metadata.pid) |
| .set_message("This is � invalid".to_string()) |
| .set_tid(packet.metadata.tid) |
| .build(); |
| assert_eq!(parsed, expected); |
| } |
| |
| #[fuchsia::test] |
| fn unterminated() { |
| let mut packet = test_packet(); |
| let end = 9; |
| packet.data[end] = 1; |
| |
| let buffer = &packet.as_bytes()[..MIN_PACKET_SIZE + end]; |
| let parsed = LoggerMessage::try_from(buffer); |
| |
| assert_eq!(parsed, Err(MessageError::NotNullTerminated { terminator: 1 })); |
| } |
| |
| #[fuchsia::test] |
| fn tags_no_message() { |
| let mut packet = test_packet(); |
| let end = 12; |
| packet.data[0] = end as c_char - 1; |
| packet.fill_data(1..end, 'A' as _); |
| packet.data[end] = 0; |
| |
| let buffer = &packet.as_bytes()[..MIN_PACKET_SIZE + end]; // omit null-terminated |
| let parsed = LoggerMessage::try_from(buffer); |
| |
| assert_eq!(parsed, Err(MessageError::OutOfBounds)); |
| } |
| |
| #[fuchsia::test] |
| fn tags_with_message() { |
| let mut packet = test_packet(); |
| let a_start = 1; |
| let a_count = 11; |
| let a_end = a_start + a_count; |
| |
| packet.data[0] = a_count as c_char; |
| packet.fill_data(a_start..a_end, 'A' as _); |
| packet.data[a_end] = 0; // terminate tags |
| |
| let b_start = a_start + a_count + 1; |
| let b_count = 5; |
| let b_end = b_start + b_count; |
| packet.fill_data(b_start..b_end, 'B' as _); |
| |
| let data_size = b_start + b_count; |
| |
| let buffer = &packet.as_bytes()[..METADATA_SIZE + data_size + 1]; // null-terminate message |
| let logger_message = LoggerMessage::try_from(buffer).unwrap(); |
| assert_eq!(logger_message.size_bytes, METADATA_SIZE + b_end); |
| let parsed = crate::from_logger(get_test_identity(), logger_message); |
| let expected = LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: packet.metadata.time.into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Debug, |
| }) |
| .set_dropped(packet.metadata.dropped_logs.into()) |
| .set_pid(packet.metadata.pid) |
| .set_message("BBBBB".to_string()) |
| .add_tag("AAAAAAAAAAA") |
| .set_tid(packet.metadata.tid) |
| .build(); |
| |
| assert_eq!(parsed, expected); |
| } |
| |
| #[fuchsia::test] |
| fn two_tags_no_message() { |
| let mut packet = test_packet(); |
| let a_start = 1; |
| let a_count = 11; |
| let a_end = a_start + a_count; |
| |
| packet.data[0] = a_count as c_char; |
| packet.fill_data(a_start..a_end, 'A' as _); |
| |
| let b_start = a_end + 1; |
| let b_count = 5; |
| let b_end = b_start + b_count; |
| |
| packet.data[a_end] = b_count as c_char; |
| packet.fill_data(b_start..b_end, 'B' as _); |
| |
| let buffer = &packet.as_bytes()[..MIN_PACKET_SIZE + b_end]; |
| let parsed = LoggerMessage::try_from(buffer); |
| |
| assert_eq!(parsed, Err(MessageError::OutOfBounds)); |
| } |
| |
| #[fuchsia::test] |
| fn two_tags_with_message() { |
| let mut packet = test_packet(); |
| let a_start = 1; |
| let a_count = 11; |
| let a_end = a_start + a_count; |
| |
| packet.data[0] = a_count as c_char; |
| packet.fill_data(a_start..a_end, 'A' as _); |
| |
| let b_start = a_end + 1; |
| let b_count = 5; |
| let b_end = b_start + b_count; |
| |
| packet.data[a_end] = b_count as c_char; |
| packet.fill_data(b_start..b_end, 'B' as _); |
| |
| let c_start = b_end + 1; |
| let c_count = 5; |
| let c_end = c_start + c_count; |
| packet.fill_data(c_start..c_end, 'C' as _); |
| |
| let data_size = c_start + c_count; |
| |
| let buffer = &packet.as_bytes()[..METADATA_SIZE + data_size + 1]; // null-terminated |
| let logger_message = LoggerMessage::try_from(buffer).unwrap(); |
| assert_eq!(logger_message.size_bytes, METADATA_SIZE + data_size); |
| let parsed = crate::from_logger(get_test_identity(), logger_message); |
| let expected = LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: packet.metadata.time.into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Debug, |
| }) |
| .set_dropped(packet.metadata.dropped_logs.into()) |
| .set_pid(packet.metadata.pid) |
| .set_message("CCCCC".to_string()) |
| .add_tag("AAAAAAAAAAA") |
| .add_tag("BBBBB") |
| .set_tid(packet.metadata.tid) |
| .build(); |
| |
| assert_eq!(parsed, expected); |
| } |
| |
| #[fuchsia::test] |
| fn max_tags_with_message() { |
| let mut packet = test_packet(); |
| |
| let tags_start = 1; |
| let tag_len = 2; |
| let tag_size = tag_len + 1; // the length-prefix byte |
| for tag_num in 0..MAX_TAGS { |
| let start = tags_start + (tag_size * tag_num); |
| let end = start + tag_len; |
| |
| packet.data[start - 1] = tag_len as c_char; |
| let ascii = 'A' as c_char + tag_num as c_char; |
| packet.fill_data(start..end, ascii); |
| } |
| |
| let msg_start = tags_start + (tag_size * MAX_TAGS); |
| let msg_len = 5; |
| let msg_end = msg_start + msg_len; |
| let msg_ascii = 'A' as c_char + MAX_TAGS as c_char; |
| packet.fill_data(msg_start..msg_end, msg_ascii); |
| |
| let min_buffer = &packet.as_bytes()[..METADATA_SIZE + msg_end + 1]; // null-terminated |
| let full_buffer = packet.as_bytes(); |
| |
| let logger_message = LoggerMessage::try_from(min_buffer).unwrap(); |
| assert_eq!(logger_message.size_bytes, METADATA_SIZE + msg_end); |
| let min_parsed = crate::from_logger(get_test_identity(), logger_message); |
| |
| let logger_message = LoggerMessage::try_from(full_buffer).unwrap(); |
| assert_eq!(logger_message.size_bytes, METADATA_SIZE + msg_end); |
| let full_parsed = |
| crate::from_logger(get_test_identity(), LoggerMessage::try_from(full_buffer).unwrap()); |
| |
| let tag_properties = (0..MAX_TAGS as _) |
| .map(|tag_num| String::from_utf8(vec![('A' as c_char + tag_num) as u8; tag_len]).unwrap()) |
| .collect::<Vec<_>>(); |
| let mut builder = LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: packet.metadata.time.into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Debug, |
| }) |
| .set_dropped(packet.metadata.dropped_logs.into()) |
| .set_pid(packet.metadata.pid) |
| .set_message(String::from_utf8(vec![msg_ascii as u8; msg_len]).unwrap()) |
| .set_tid(packet.metadata.tid); |
| for tag in tag_properties { |
| builder = builder.add_tag(tag); |
| } |
| let expected_message = builder.build(); |
| |
| assert_eq!(min_parsed, expected_message); |
| assert_eq!(full_parsed, expected_message); |
| } |
| |
| #[fuchsia::test] |
| fn max_tags() { |
| let mut packet = test_packet(); |
| let tags_start = 1; |
| let tag_len = 2; |
| let tag_size = tag_len + 1; // the length-prefix byte |
| for tag_num in 0..MAX_TAGS { |
| let start = tags_start + (tag_size * tag_num); |
| let end = start + tag_len; |
| |
| packet.data[start - 1] = tag_len as c_char; |
| let ascii = 'A' as c_char + tag_num as c_char; |
| packet.fill_data(start..end, ascii); |
| } |
| |
| let msg_start = tags_start + (tag_size * MAX_TAGS); |
| |
| let buffer_missing_terminator = &packet.as_bytes()[..METADATA_SIZE + msg_start]; |
| assert_eq!( |
| LoggerMessage::try_from(buffer_missing_terminator), |
| Err(MessageError::OutOfBounds), |
| "can't parse an empty message without a nul terminator" |
| ); |
| |
| let buffer = &packet.as_bytes()[..METADATA_SIZE + msg_start + 1]; // null-terminated |
| let logger_message = LoggerMessage::try_from(buffer).unwrap(); |
| assert_eq!(logger_message.size_bytes, 48); |
| let parsed = crate::from_logger(get_test_identity(), logger_message); |
| let mut builder = LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: packet.metadata.time.into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Debug, |
| }) |
| .set_dropped(packet.metadata.dropped_logs as u64) |
| .set_pid(packet.metadata.pid) |
| .set_tid(packet.metadata.tid) |
| .set_message("".to_string()); |
| for tag_num in 0..MAX_TAGS as _ { |
| builder = |
| builder.add_tag(String::from_utf8(vec![('A' as c_char + tag_num) as u8; 2]).unwrap()); |
| } |
| assert_eq!(parsed, builder.build()); |
| } |
| |
| #[fuchsia::test] |
| fn no_tags_with_message() { |
| let mut packet = test_packet(); |
| packet.data[0] = 0; |
| packet.data[1] = 'A' as _; |
| packet.data[2] = 'A' as _; // measured size ends here |
| packet.data[3] = 0; |
| |
| let buffer = &packet.as_bytes()[..METADATA_SIZE + 4]; // 0 tag size + 2 byte message + null |
| let logger_message = LoggerMessage::try_from(buffer).unwrap(); |
| assert_eq!(logger_message.size_bytes, METADATA_SIZE + 3); |
| let parsed = crate::from_logger(get_test_identity(), logger_message); |
| |
| assert_eq!( |
| parsed, |
| LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: zx::Time::from_nanos(3).into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Debug, |
| }) |
| .set_dropped(packet.metadata.dropped_logs as u64) |
| .set_pid(packet.metadata.pid) |
| .set_tid(packet.metadata.tid) |
| .set_message("AA".to_string()) |
| .build() |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn message_severity() { |
| let mut packet = test_packet(); |
| packet.metadata.severity = LogLevelFilter::Info as i32; |
| packet.data[0] = 0; // tag size |
| packet.data[1] = 0; // null terminated |
| |
| let mut buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; // tag size + null |
| let logger_message = LoggerMessage::try_from(buffer).unwrap(); |
| assert_eq!(logger_message.size_bytes, METADATA_SIZE + 1); |
| let mut parsed = crate::from_logger(get_test_identity(), logger_message); |
| |
| let mut expected_message = LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: packet.metadata.time.into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Info, |
| }) |
| .set_pid(packet.metadata.pid) |
| .set_message("".to_string()) |
| .set_dropped(10) |
| .set_tid(packet.metadata.tid) |
| .build(); |
| |
| assert_eq!(parsed, expected_message); |
| |
| packet.metadata.severity = LogLevelFilter::Trace as i32; |
| buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; |
| parsed = crate::from_logger(get_test_identity(), LoggerMessage::try_from(buffer).unwrap()); |
| expected_message.metadata.severity = Severity::Trace; |
| |
| assert_eq!(parsed, expected_message); |
| |
| packet.metadata.severity = LogLevelFilter::Debug as i32; |
| buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; |
| parsed = crate::from_logger(get_test_identity(), LoggerMessage::try_from(buffer).unwrap()); |
| expected_message.metadata.severity = Severity::Debug; |
| |
| assert_eq!(parsed, expected_message); |
| |
| packet.metadata.severity = LogLevelFilter::Warn as i32; |
| buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; |
| parsed = crate::from_logger(get_test_identity(), LoggerMessage::try_from(buffer).unwrap()); |
| expected_message.metadata.severity = Severity::Warn; |
| |
| assert_eq!(parsed, expected_message); |
| |
| packet.metadata.severity = LogLevelFilter::Error as i32; |
| buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; |
| parsed = crate::from_logger(get_test_identity(), LoggerMessage::try_from(buffer).unwrap()); |
| expected_message.metadata.severity = Severity::Error; |
| |
| assert_eq!(parsed, expected_message); |
| } |
| |
| #[fuchsia::test] |
| fn legacy_message_severity() { |
| let mut packet = test_packet(); |
| // raw_severity where raw_severity=10 |
| packet.metadata.severity = LogLevelFilter::Info as i32 - 10; |
| packet.data[0] = 0; // tag size |
| packet.data[1] = 0; // null terminated |
| |
| let mut buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; // tag size + null |
| let logger_message = LoggerMessage::try_from(buffer).unwrap(); |
| assert_eq!(logger_message.size_bytes, METADATA_SIZE + 1); |
| let mut parsed = crate::from_logger(get_test_identity(), logger_message); |
| let mut expected_message = LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: zx::Time::from_nanos(3).into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Debug, |
| }) |
| .set_dropped(packet.metadata.dropped_logs as u64) |
| .set_pid(1) |
| .set_tid(2) |
| .set_message("".to_string()) |
| .build(); |
| clear_raw_severity(&mut expected_message); |
| expected_message.set_raw_severity(10); |
| |
| assert_eq!(parsed, expected_message); |
| |
| // raw_severity where raw_severity=2 |
| packet.metadata.severity = LogLevelFilter::Info as i32 - 2; |
| buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; |
| parsed = crate::from_logger(get_test_identity(), LoggerMessage::try_from(buffer).unwrap()); |
| clear_raw_severity(&mut expected_message); |
| expected_message.set_raw_severity(2); |
| |
| assert_eq!(parsed, expected_message); |
| |
| // raw_severity where raw_severity=1 |
| packet.metadata.severity = LogLevelFilter::Info as i32 - 1; |
| buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; |
| parsed = crate::from_logger(get_test_identity(), LoggerMessage::try_from(buffer).unwrap()); |
| clear_raw_severity(&mut expected_message); |
| expected_message.set_raw_severity(1); |
| |
| assert_eq!(parsed, expected_message); |
| |
| packet.metadata.severity = 0; // legacy severity |
| buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; |
| parsed = crate::from_logger(get_test_identity(), LoggerMessage::try_from(buffer).unwrap()); |
| clear_raw_severity(&mut expected_message); |
| expected_message.metadata.severity = Severity::Info; |
| |
| assert_eq!(parsed, expected_message); |
| |
| packet.metadata.severity = 1; // legacy severity |
| buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; |
| parsed = crate::from_logger(get_test_identity(), LoggerMessage::try_from(buffer).unwrap()); |
| expected_message.metadata.severity = Severity::Warn; |
| |
| assert_eq!(parsed, expected_message); |
| |
| packet.metadata.severity = 2; // legacy severity |
| buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; |
| parsed = crate::from_logger(get_test_identity(), LoggerMessage::try_from(buffer).unwrap()); |
| expected_message.metadata.severity = Severity::Error; |
| |
| assert_eq!(parsed, expected_message); |
| |
| packet.metadata.severity = 3; // legacy severity |
| buffer = &packet.as_bytes()[..METADATA_SIZE + 2]; |
| parsed = crate::from_logger(get_test_identity(), LoggerMessage::try_from(buffer).unwrap()); |
| expected_message.metadata.severity = Severity::Fatal; |
| |
| assert_eq!(parsed, expected_message); |
| } |
| |
| #[fuchsia::test] |
| fn test_raw_severity_parsing_and_conversions() { |
| let record = Record { |
| timestamp: 72, |
| severity: 0x30 - 2, // INFO-2 |
| arguments: vec![ |
| Argument { |
| name: FILE_PATH_LABEL.to_string(), |
| value: Value::Text("some_file.cc".to_string()), |
| }, |
| Argument { name: LINE_NUMBER_LABEL.to_string(), value: Value::UnsignedInt(420) }, |
| Argument { name: "arg1".to_string(), value: Value::SignedInt(-23) }, |
| Argument { name: "arg2".to_string(), value: Value::Boolean(true) }, |
| Argument { name: PID_LABEL.to_string(), value: Value::UnsignedInt(43) }, |
| Argument { name: TID_LABEL.to_string(), value: Value::UnsignedInt(912) }, |
| Argument { name: DROPPED_LABEL.to_string(), value: Value::UnsignedInt(2) }, |
| Argument { name: TAG_LABEL.to_string(), value: Value::Text("tag".to_string()) }, |
| Argument { name: MESSAGE_LABEL.to_string(), value: Value::Text("msg".to_string()) }, |
| ], |
| }; |
| |
| let mut buffer = Cursor::new(vec![0u8; MAX_DATAGRAM_LEN]); |
| let mut encoder = Encoder::new(&mut buffer); |
| encoder.write_record(&record).unwrap(); |
| let encoded = &buffer.get_ref().as_slice()[..buffer.position() as usize]; |
| let mut parsed = crate::from_structured(get_test_identity(), encoded).unwrap(); |
| parsed.sort_payload(); |
| assert_eq!( |
| parsed, |
| LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: zx::Time::from_nanos(72).into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Debug, |
| }) |
| .set_dropped(2) |
| .set_file("some_file.cc".to_string()) |
| .set_line(420) |
| .set_pid(43u64) |
| .set_tid(912u64) |
| .add_tag("tag") |
| .set_message("msg".to_string()) |
| .override_raw_severity(2) |
| .add_key(LogsProperty::Int(LogsField::Other("arg1".to_string()), -23i64)) |
| .add_key(LogsProperty::Bool(LogsField::Other("arg2".to_string()), true)) |
| .build() |
| ); |
| |
| let severity: i32 = 0x30 - 2; // INFO-2 |
| let message: LogMessage = parsed.into(); |
| assert_eq!( |
| message, |
| LogMessage { |
| severity, |
| time: 72, |
| dropped_logs: 2, |
| pid: 43, |
| tid: 912, |
| msg: "[some_file.cc(420)] msg arg1=-23 arg2=true".into(), |
| tags: vec!["tag".into()] |
| } |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_from_structured() { |
| let record = Record { |
| timestamp: 72, |
| severity: StreamSeverity::Error.into_primitive(), |
| arguments: vec![ |
| Argument { |
| name: FILE_PATH_LABEL.to_string(), |
| value: Value::Text("some_file.cc".to_string()), |
| }, |
| Argument { name: LINE_NUMBER_LABEL.to_string(), value: Value::UnsignedInt(420) }, |
| Argument { name: "arg1".to_string(), value: Value::SignedInt(-23) }, |
| Argument { name: "arg2".to_string(), value: Value::Boolean(true) }, |
| Argument { name: PID_LABEL.to_string(), value: Value::UnsignedInt(43) }, |
| Argument { name: TID_LABEL.to_string(), value: Value::UnsignedInt(912) }, |
| Argument { name: DROPPED_LABEL.to_string(), value: Value::UnsignedInt(2) }, |
| Argument { name: TAG_LABEL.to_string(), value: Value::Text("tag".to_string()) }, |
| Argument { name: MESSAGE_LABEL.to_string(), value: Value::Text("msg".to_string()) }, |
| ], |
| }; |
| |
| let mut buffer = Cursor::new(vec![0u8; MAX_DATAGRAM_LEN]); |
| let mut encoder = Encoder::new(&mut buffer); |
| encoder.write_record(&record).unwrap(); |
| let encoded = &buffer.get_ref().as_slice()[..buffer.position() as usize]; |
| let parsed = crate::from_structured(get_test_identity(), encoded).unwrap(); |
| assert_eq!( |
| parsed, |
| LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: zx::Time::from_nanos(72).into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Error, |
| }) |
| .set_dropped(2) |
| .set_file("some_file.cc".to_string()) |
| .set_line(420) |
| .set_pid(43u64) |
| .set_tid(912u64) |
| .add_tag("tag") |
| .set_message("msg".to_string()) |
| .add_key(LogsProperty::Int(LogsField::Other("arg1".to_string()), -23i64)) |
| .add_key(LogsProperty::Bool(LogsField::Other("arg2".to_string()), true)) |
| .build() |
| ); |
| let severity: i32 = LegacySeverity::Error.into(); |
| let message: LogMessage = parsed.into(); |
| assert_eq!( |
| message, |
| LogMessage { |
| severity, |
| time: 72, |
| dropped_logs: 2, |
| pid: 43, |
| tid: 912, |
| msg: "[some_file.cc(420)] msg arg1=-23 arg2=true".into(), |
| tags: vec!["tag".into()] |
| } |
| ); |
| |
| // multiple tags |
| let record = Record { |
| timestamp: 72, |
| severity: StreamSeverity::Error.into_primitive(), |
| arguments: vec![ |
| Argument { name: TAG_LABEL.to_string(), value: Value::Text("tag1".to_string()) }, |
| Argument { name: TAG_LABEL.to_string(), value: Value::Text("tag2".to_string()) }, |
| Argument { name: TAG_LABEL.to_string(), value: Value::Text("tag3".to_string()) }, |
| ], |
| }; |
| let mut buffer = Cursor::new(vec![0u8; MAX_DATAGRAM_LEN]); |
| let mut encoder = Encoder::new(&mut buffer); |
| encoder.write_record(&record).unwrap(); |
| let encoded = &buffer.get_ref().as_slice()[..buffer.position() as usize]; |
| let parsed = crate::from_structured(get_test_identity(), encoded).unwrap(); |
| assert_eq!( |
| parsed, |
| LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: zx::Time::from_nanos(72).into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Error, |
| }) |
| .add_tag("tag1") |
| .add_tag("tag2") |
| .add_tag("tag3") |
| .build() |
| ); |
| |
| // empty record |
| let record = Record { |
| timestamp: 72, |
| severity: StreamSeverity::Error.into_primitive(), |
| arguments: vec![], |
| }; |
| let mut buffer = Cursor::new(vec![0u8; MAX_DATAGRAM_LEN]); |
| let mut encoder = Encoder::new(&mut buffer); |
| encoder.write_record(&record).unwrap(); |
| let encoded = &buffer.get_ref().as_slice()[..buffer.position() as usize]; |
| let parsed = crate::from_structured(get_test_identity(), encoded).unwrap(); |
| assert_eq!( |
| parsed, |
| LogsDataBuilder::new(BuilderArgs { |
| timestamp_nanos: zx::Time::from_nanos(72).into(), |
| component_url: Some(TEST_IDENTITY.url.clone()), |
| moniker: TEST_IDENTITY.moniker.clone(), |
| severity: Severity::Error, |
| }) |
| .build() |
| ); |
| |
| // parse error |
| assert_matches!( |
| crate::from_structured(get_test_identity(), &vec![]).unwrap_err(), |
| MessageError::ParseError { .. } |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn basic_structured_info() { |
| let expected_timestamp = 72; |
| let record = Record { |
| timestamp: expected_timestamp, |
| severity: StreamSeverity::Error.into_primitive(), |
| arguments: vec![], |
| }; |
| let mut buffer = Cursor::new(vec![0u8; MAX_DATAGRAM_LEN]); |
| let mut encoder = Encoder::new(&mut buffer); |
| encoder.write_record(&record).unwrap(); |
| let encoded = &buffer.get_ref().as_slice()[..buffer.position() as usize]; |
| |
| let (timestamp, severity) = parse_basic_structured_info(encoded).unwrap(); |
| assert_eq!(timestamp, expected_timestamp); |
| assert_eq!(severity, Severity::Error); |
| } |