Skip to content

Commit 6f24702

Browse files
committed
implment camera recorder integration test
Signed-off-by: Luis Liu <[email protected]>
1 parent 5e99c92 commit 6f24702

File tree

3 files changed

+144
-3
lines changed

3 files changed

+144
-3
lines changed

gst-plugin-pravega/src/pravegasink/imp.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const PROPERTY_NAME_RETENTION_TYPE: &str = "retention-type";
5656
const PROPERTY_NAME_RETENTION_DAYS: &str = "retention-days";
5757
const PROPERTY_NAME_RETENTION_BYTES: &str = "retention-bytes";
5858
const PROPERTY_NAME_RETENTION_MAINTENANCE_INTERVAL_SECONDS: &str = "retention-maintenance-interval-seconds";
59+
const PROPERTY_NAME_SIMULATE_FAILURE_AFTER_SEC: &str = "simulate-failure-after-sec";
5960

6061
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::GEnum)]
6162
#[repr(u32)]
@@ -244,6 +245,7 @@ struct Settings {
244245
retention_days: Option<f64>,
245246
retention_bytes: Option<u64>,
246247
retention_maintenance_interval_seconds: u64,
248+
simulate_failure_after_sec: Option<u64>,
247249
}
248250

249251
impl Default for Settings {
@@ -263,6 +265,7 @@ impl Default for Settings {
263265
retention_days: None,
264266
retention_bytes: None,
265267
retention_maintenance_interval_seconds: DEFAULT_RETENTION_MAINTENANCE_INTERVAL_SECONDS,
268+
simulate_failure_after_sec: None,
266269
}
267270
}
268271
}
@@ -284,6 +287,7 @@ enum State {
284287
buffers_written: u64,
285288
retention_thread_stop_tx: Sender<()>,
286289
retention_thread_handle: Option<JoinHandle<()>>,
290+
simulate_failure_after_sec: Option<u64>,
287291
},
288292
}
289293

@@ -481,6 +485,15 @@ impl ObjectImpl for PravegaSink {
481485
DEFAULT_RETENTION_MAINTENANCE_INTERVAL_SECONDS,
482486
glib::ParamFlags::WRITABLE,
483487
),
488+
glib::ParamSpec::new_uint64(
489+
PROPERTY_NAME_SIMULATE_FAILURE_AFTER_SEC,
490+
"Simulate failure after seconds",
491+
"Simulate raw data writting failure after successful specfied seconds of index wrtting",
492+
0,
493+
std::u64::MAX,
494+
0,
495+
glib::ParamFlags::WRITABLE,
496+
),
484497
]});
485498
PROPERTIES.as_ref()
486499
}
@@ -664,7 +677,20 @@ impl ObjectImpl for PravegaSink {
664677
if let Err(err) = res {
665678
gst_error!(CAT, obj: obj, "Failed to set property `{}`: {}", PROPERTY_NAME_RETENTION_MAINTENANCE_INTERVAL_SECONDS, err);
666679
}
667-
},
680+
},
681+
PROPERTY_NAME_SIMULATE_FAILURE_AFTER_SEC => {
682+
let res: Result<(), glib::Error> = match value.get::<u64>() {
683+
Ok(seconds) => {
684+
let mut settings = self.settings.lock().unwrap();
685+
settings.simulate_failure_after_sec = Some(seconds);
686+
Ok(())
687+
},
688+
Err(_) => unreachable!("type checked upstream"),
689+
};
690+
if let Err(err) = res {
691+
gst_error!(CAT, obj: obj, "Failed to set property `{}`: {}", PROPERTY_NAME_SIMULATE_FAILURE_AFTER_SEC, err);
692+
}
693+
},
668694
_ => unimplemented!(),
669695
};
670696
}
@@ -744,6 +770,7 @@ impl BaseSinkImpl for PravegaSink {
744770
gst_info!(CAT, obj: element, "start: controller={}", controller);
745771
let keycloak_file = settings.keycloak_file.clone();
746772
gst_info!(CAT, obj: element, "start: keycloak_file={:?}", keycloak_file);
773+
gst_info!(CAT, obj: element, "start: simulate_failure_after_sec={:?}", settings.simulate_failure_after_sec);
747774
let config = utils::create_client_config(controller, keycloak_file).map_err(|error| {
748775
gst::error_msg!(gst::ResourceError::Settings, ["Failed to create Pravega client config: {}", error])
749776
})?;
@@ -845,6 +872,7 @@ impl BaseSinkImpl for PravegaSink {
845872
buffers_written: 0,
846873
retention_thread_stop_tx,
847874
retention_thread_handle,
875+
simulate_failure_after_sec: settings.simulate_failure_after_sec,
848876
};
849877
gst_info!(CAT, obj: element, "start: Started");
850878
Ok(())
@@ -867,7 +895,8 @@ impl BaseSinkImpl for PravegaSink {
867895
last_index_time,
868896
final_timestamp,
869897
final_offset,
870-
buffers_written) = match *state {
898+
buffers_written,
899+
simulate_failure_after_sec) = match *state {
871900
State::Started {
872901
ref mut writer,
873902
ref mut index_writer,
@@ -876,14 +905,16 @@ impl BaseSinkImpl for PravegaSink {
876905
ref mut final_timestamp,
877906
ref mut final_offset,
878907
ref mut buffers_written,
908+
simulate_failure_after_sec,
879909
..
880910
} => (writer,
881911
index_writer,
882912
first_valid_time,
883913
last_index_time,
884914
final_timestamp,
885915
final_offset,
886-
buffers_written),
916+
buffers_written,
917+
simulate_failure_after_sec),
887918
State::Stopped => {
888919
gst::element_error!(element, gst::CoreError::Failed, ["Not started yet"]);
889920
return Err(gst::FlowError::Error);
@@ -1114,6 +1145,18 @@ impl BaseSinkImpl for PravegaSink {
11141145
}
11151146
*final_offset = Some(writer_offset_end);
11161147

1148+
if let Some(seconds) = simulate_failure_after_sec {
1149+
if !first_valid_time.is_none() {
1150+
if (timestamp - first_valid_time.to_owned()).nanoseconds().unwrap() > (seconds as i32 * SECOND).nanoseconds().unwrap() {
1151+
// TODO: close pravega writers
1152+
//drop(writer.get_mut().get_mut().get_mut());
1153+
//drop(index_writer);
1154+
gst::element_error!(element, gst::CoreError::Failed, ["Simulate pravegasink failure"]);
1155+
return Err(gst::FlowError::Error);
1156+
}
1157+
}
1158+
}
1159+
11171160
Ok(gst::FlowSuccess::Ok)
11181161
})();
11191162
gst_trace!(CAT, obj: element, "render: END: result={:?}", result);
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//
2+
// Copyright (c) Dell Inc., or its subsidiaries. All Rights Reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
11+
#[cfg(test)]
12+
mod test {
13+
use gst::prelude::*;
14+
use gst::ClockType::Realtime;
15+
use pravega_video::timestamp::{PravegaTimestamp, SECOND};
16+
use rstest::rstest;
17+
#[allow(unused_imports)]
18+
use tracing::{error, info, debug};
19+
use uuid::Uuid;
20+
use crate::*;
21+
use crate::rtsp_camera_simulator::{start_or_get_rtsp_test_source, RTSPCameraSimulatorConfigBuilder};
22+
use crate::utils::*;
23+
24+
#[rstest]
25+
#[case(Realtime, 30, 15)]
26+
fn test_ungraceful_stop(#[case] clock_type: gst::ClockType, #[case] num_sec_to_record: u64, #[case] num_sec_to_failure: u64) {
27+
gst_init();
28+
let clock = gst::SystemClock::obtain();
29+
clock.set_property("clock-type", &clock_type).unwrap();
30+
gst::SystemClock::set_default(Some(&clock));
31+
let rtsp_server_config = RTSPCameraSimulatorConfigBuilder::default().fps(20).build().unwrap();
32+
let num_frames = num_sec_to_record * &rtsp_server_config.fps;
33+
let (rtsp_url, _rtsp_server) = start_or_get_rtsp_test_source(rtsp_server_config);
34+
let test_config = &get_test_config();
35+
info!("### BEGIN:test_config={:?}", test_config);
36+
let stream_name = &format!("test-pravegasrc-{}-{}", test_config.test_id, Uuid::new_v4())[..];
37+
let pipeline_description = format!("\
38+
rtspsrc \
39+
buffer-mode=none \
40+
drop-on-latency=true \
41+
latency=2000 \
42+
location={rtsp_url} \
43+
ntp-sync=true \
44+
ntp-time-source=running-time \
45+
! rtph264depay \
46+
! h264parse \
47+
! video/x-h264,alignment=au \
48+
! identity name=h264par silent=false eos-after={num_frames} \
49+
! timestampcvt \
50+
! identity name=tscvt__ silent=false \
51+
! mp4mux streamable=true fragment-duration=1 ! fragmp4pay \
52+
! pravegasink {pravega_plugin_properties} \
53+
timestamp-mode=tai sync=false \
54+
",
55+
rtsp_url = rtsp_url,
56+
num_frames = num_frames,
57+
pravega_plugin_properties = test_config.pravega_plugin_properties(stream_name),
58+
);
59+
let expected_timestamp = PravegaTimestamp::now();
60+
let _ = launch_pipeline_and_get_summary(format!("{} simulate-failure-after-sec={}", pipeline_description, num_sec_to_failure).as_ref());
61+
62+
info!("#### Read recorded stream from Pravega, no demux, no decoding, part 1");
63+
let pipeline_description_read = format!(
64+
"pravegasrc {pravega_plugin_properties} \
65+
start-mode=earliest \
66+
end-mode=latest \
67+
! appsink name=sink sync=false",
68+
pravega_plugin_properties = test_config.pravega_plugin_properties(stream_name),
69+
);
70+
let summary_read = launch_pipeline_and_get_summary(&pipeline_description_read).unwrap();
71+
debug!("summary_read={}", summary_read);
72+
let first_pts_read = summary_read.first_valid_pts();
73+
let last_pts_read = summary_read.last_valid_pts();
74+
assert!(first_pts_read.is_some(), "Pipeline is not recording timestamps");
75+
assert_between_u64("decreasing_pts_count", summary_read.decreasing_pts_count(), 0, 0);
76+
assert_between_u64("decreasing_dts_count", summary_read.decreasing_dts_count(), 0, 0);
77+
// the gap between fisrt_pts_read and expected_timestamp is caused by the duration of creation of scope & stream and first 5s invalid PTS when RTSP connection initialized
78+
let gap = 60;
79+
assert_timestamp_approx_eq("first_pts_read", first_pts_read, expected_timestamp, 0 * SECOND, gap * SECOND);
80+
assert_timestamp_approx_eq("last_pts_read", last_pts_read, expected_timestamp + num_sec_to_failure * SECOND, 0 * SECOND, gap * SECOND);
81+
assert!(summary_read.pts_range() >= (num_sec_to_failure - 10) * SECOND);
82+
assert!(summary_read.pts_range() <= num_sec_to_failure * SECOND);
83+
84+
let _ = launch_pipeline_and_get_summary(&pipeline_description).unwrap();
85+
let summary_read = launch_pipeline_and_get_summary(&pipeline_description_read).unwrap();
86+
debug!("summary_read={}", summary_read);
87+
let first_pts_read = summary_read.first_valid_pts();
88+
let last_pts_read = summary_read.last_valid_pts();
89+
assert!(first_pts_read.is_some(), "Pipeline is not recording timestamps");
90+
assert_between_u64("decreasing_pts_count", summary_read.decreasing_pts_count(), 0, 0);
91+
assert_between_u64("decreasing_dts_count", summary_read.decreasing_dts_count(), 0, 0);
92+
assert_timestamp_approx_eq("first_pts_read", first_pts_read, expected_timestamp, 0 * SECOND, gap * SECOND);
93+
assert_timestamp_approx_eq("last_pts_read", last_pts_read, expected_timestamp + (num_sec_to_record + num_sec_to_failure) * SECOND, 0 * SECOND, gap * 2 * SECOND);
94+
assert!(summary_read.pts_range() >= (num_sec_to_record + num_sec_to_failure - 10 * 2) * SECOND);
95+
assert!(summary_read.pts_range() <= (num_sec_to_record + num_sec_to_failure + gap) * SECOND);
96+
}
97+
}

integration-test/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#![allow(dead_code)]
1212

13+
mod camera_recorder_tests;
1314
mod extreme_tests;
1415
mod failure_recovery_tests;
1516
mod file_import_tests;

0 commit comments

Comments
 (0)