mirror of https://github.com/dnaka91/obws
Add integration tests and fix some API calls
parent
da5dc4a6dc
commit
601ce2dded
@ -0,0 +1,14 @@
|
||||
# Integration tests
|
||||
|
||||
To run integration tests, obws will connect to your OBS instance and send several commands against
|
||||
the obs-websocket API to make sure most of the API works as expected.
|
||||
|
||||
For this to work, a few settings need to be set and some scene items created so that the tests have
|
||||
items to work on. This has to be done manually as the API doesn't allow to create new sources and
|
||||
scenes or modify specific settings.
|
||||
|
||||
- Use at least OBS version `26.1.0`.
|
||||
- Create a **source collection** called `OBWS-TEST`.
|
||||
- Create a **scene** called `OBWS-TEST-Scene`.
|
||||
- Create a **Freetype2 text source** called `OBWS-TEST-Text`.
|
||||
- Make sure a global **Desktop Audio** device is configured.
|
@ -0,0 +1,92 @@
|
||||
use std::sync::Once;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
use obws::{
|
||||
responses::{Output, Scene, SceneCollection, SourceListItem},
|
||||
Client,
|
||||
};
|
||||
use tokio::time;
|
||||
|
||||
pub const TEST_OUTPUT: &str = "virtualcam_output";
|
||||
pub const TEST_COLLECTION: &str = "OBWS-TEST";
|
||||
pub const TEST_SCENE: &str = "OBWS-TEST-Scene";
|
||||
pub const TEXT_SOURCE: &str = "OBWS-TEST-Text";
|
||||
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
pub async fn new_client() -> Result<Client> {
|
||||
INIT.call_once(|| {
|
||||
dotenv::dotenv().ok();
|
||||
pretty_env_logger::init();
|
||||
});
|
||||
|
||||
let client = Client::connect("localhost", 4444).await?;
|
||||
client.login(std::env::var("OBS_PASSWORD").ok()).await?;
|
||||
|
||||
let collections = client.scene_collections().list_scene_collections().await?;
|
||||
ensure!(
|
||||
collections.iter().any(is_required_scene_collection),
|
||||
"scene collection `{}` not found, required for all tests",
|
||||
TEST_COLLECTION
|
||||
);
|
||||
|
||||
client
|
||||
.scene_collections()
|
||||
.set_current_scene_collection("OBWS-TEST")
|
||||
.await?;
|
||||
|
||||
// Give OBS some time to load the scene collection
|
||||
time::sleep(Duration::from_millis(500)).await;
|
||||
|
||||
ensure_obs_setup(&client).await?;
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
async fn ensure_obs_setup(client: &Client) -> Result<()> {
|
||||
let outputs = client.outputs().list_outputs().await?;
|
||||
ensure!(
|
||||
outputs.iter().any(is_required_output),
|
||||
"output `{}` not found, required for output tests",
|
||||
TEST_OUTPUT
|
||||
);
|
||||
|
||||
let scenes = client.scenes().get_scene_list().await?;
|
||||
ensure!(
|
||||
scenes.scenes.iter().any(is_required_scene),
|
||||
"scene `{}` not found, required for scenes tests",
|
||||
TEST_SCENE
|
||||
);
|
||||
|
||||
let sources = client.sources().get_sources_list().await?;
|
||||
ensure!(
|
||||
sources.iter().any(is_required_source),
|
||||
"text source `{}` not found, required for sources tests",
|
||||
TEXT_SOURCE
|
||||
);
|
||||
|
||||
let special_sources = client.sources().get_special_sources().await?;
|
||||
ensure!(
|
||||
special_sources.desktop_1.is_some(),
|
||||
"desktop audio device required for sources tests"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_required_output(output: &Output) -> bool {
|
||||
output.name == TEST_OUTPUT
|
||||
}
|
||||
|
||||
fn is_required_scene_collection(output: &SceneCollection) -> bool {
|
||||
output.sc_name == TEST_COLLECTION
|
||||
}
|
||||
|
||||
fn is_required_scene(scene: &Scene) -> bool {
|
||||
scene.name == TEST_SCENE
|
||||
}
|
||||
|
||||
fn is_required_source(source: &SourceListItem) -> bool {
|
||||
source.name == TEXT_SOURCE && source.ty == "input" && source.type_id == "text_ft2_source_v2"
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
#![cfg(feature = "test-integration")]
|
||||
|
||||
use anyhow::Result;
|
||||
use obws::requests::{Projector, ProjectorType};
|
||||
use serde_json::json;
|
||||
|
||||
mod common;
|
||||
|
||||
#[tokio::test]
|
||||
async fn general() -> Result<()> {
|
||||
let client = common::new_client().await?;
|
||||
let client = client.general();
|
||||
|
||||
client.get_version().await?;
|
||||
|
||||
client.get_auth_required().await?;
|
||||
|
||||
let original = client.get_filename_formatting().await?;
|
||||
client.set_filename_formatting("test").await?;
|
||||
client.set_filename_formatting(&original).await?;
|
||||
|
||||
client.get_stats().await?;
|
||||
|
||||
client
|
||||
.broadcast_custom_message("test", &json! {{"greeting":"hello"}})
|
||||
.await?;
|
||||
|
||||
client.get_video_info().await?;
|
||||
|
||||
// Currently no API function available to close the projector again.
|
||||
client
|
||||
.open_projector(Projector {
|
||||
ty: Some(ProjectorType::Multiview),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
#![cfg(feature = "test-integration")]
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use common::TEST_OUTPUT;
|
||||
|
||||
mod common;
|
||||
|
||||
#[tokio::test]
|
||||
async fn general() -> Result<()> {
|
||||
let client = common::new_client().await?;
|
||||
let client = client.outputs();
|
||||
|
||||
client.list_outputs().await?;
|
||||
client.get_output_info(TEST_OUTPUT).await?;
|
||||
client.start_output(TEST_OUTPUT).await?;
|
||||
client.stop_output(TEST_OUTPUT, Some(true)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
#![cfg(feature = "test-integration")]
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use chrono::Duration;
|
||||
use obws::{
|
||||
common::MonitorType,
|
||||
requests::{
|
||||
AddFilter, MoveFilter, ReorderFilter, SourceFilterSettings, SourceFilterVisibility,
|
||||
SourceScreenshot, SourceSettings, Volume,
|
||||
},
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use common::TEXT_SOURCE;
|
||||
|
||||
mod common;
|
||||
|
||||
#[tokio::test]
|
||||
async fn sources() -> Result<()> {
|
||||
let client = common::new_client().await?;
|
||||
let client = client.sources();
|
||||
|
||||
client.get_sources_list().await?;
|
||||
client.get_sources_types_list().await?;
|
||||
|
||||
// Volume
|
||||
|
||||
let original = client.get_volume(TEXT_SOURCE, None).await?.volume;
|
||||
client.get_volume(TEXT_SOURCE, Some(true)).await?;
|
||||
|
||||
client
|
||||
.set_volume(Volume {
|
||||
source: TEXT_SOURCE,
|
||||
volume: 0.5,
|
||||
use_decibel: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
client
|
||||
.set_volume(Volume {
|
||||
source: TEXT_SOURCE,
|
||||
volume: original,
|
||||
use_decibel: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Mute
|
||||
|
||||
let original = client.get_mute(TEXT_SOURCE).await?.muted;
|
||||
client.toggle_mute(TEXT_SOURCE).await?;
|
||||
client.set_mute(TEXT_SOURCE, original).await?;
|
||||
|
||||
// Source name
|
||||
|
||||
let new_name = format!("{}-Test", TEXT_SOURCE);
|
||||
client.set_source_name(TEXT_SOURCE, &new_name).await?;
|
||||
client.set_source_name(&new_name, TEXT_SOURCE).await?;
|
||||
|
||||
// Sync offset
|
||||
|
||||
let original = client.get_sync_offset(TEXT_SOURCE).await?;
|
||||
client
|
||||
.set_sync_offset(TEXT_SOURCE, Duration::milliseconds(200))
|
||||
.await?;
|
||||
client.set_sync_offset(TEXT_SOURCE, original.offset).await?;
|
||||
|
||||
// Source settings
|
||||
|
||||
let settings = client
|
||||
.get_source_settings::<serde_json::Value>(TEXT_SOURCE, None)
|
||||
.await?;
|
||||
client
|
||||
.set_source_settings::<serde_json::Value>(SourceSettings {
|
||||
source_name: &settings.source_name,
|
||||
source_type: Some(&settings.source_type),
|
||||
source_settings: &settings.source_settings,
|
||||
})
|
||||
.await?;
|
||||
|
||||
// TODO: GDI+ only on windows?
|
||||
|
||||
// Freetype2 properties
|
||||
|
||||
let props = client.get_text_freetype2_properties(TEXT_SOURCE).await?;
|
||||
client
|
||||
.set_text_freetype2_properties((&props).into())
|
||||
.await?;
|
||||
|
||||
// Special sources
|
||||
|
||||
client.get_special_sources().await?;
|
||||
|
||||
// Filters
|
||||
const FILTER1: &str = "Scroll-Test1";
|
||||
const FILTER2: &str = "Scroll-Test2";
|
||||
|
||||
client.get_source_filters(TEXT_SOURCE).await?;
|
||||
client
|
||||
.add_filter_to_source(AddFilter {
|
||||
source_name: TEXT_SOURCE,
|
||||
filter_name: FILTER1,
|
||||
filter_type: "scroll_filter",
|
||||
filter_settings: &json! {{
|
||||
"limit_cx": false,
|
||||
"limit_cy": false,
|
||||
"speed_x": 50.0
|
||||
}},
|
||||
})
|
||||
.await?;
|
||||
client
|
||||
.get_source_filter_info::<serde_json::Value>(TEXT_SOURCE, FILTER1)
|
||||
.await?;
|
||||
client
|
||||
.add_filter_to_source(AddFilter {
|
||||
source_name: TEXT_SOURCE,
|
||||
filter_name: FILTER2,
|
||||
filter_type: "scroll_filter",
|
||||
filter_settings: &json! {{
|
||||
"limit_cx": false,
|
||||
"limit_cy": false,
|
||||
"speed_x": 20.0
|
||||
}},
|
||||
})
|
||||
.await?;
|
||||
|
||||
client
|
||||
.reorder_source_filter(ReorderFilter {
|
||||
source_name: TEXT_SOURCE,
|
||||
filter_name: FILTER1,
|
||||
new_index: 1,
|
||||
})
|
||||
.await?;
|
||||
client
|
||||
.move_source_filter(MoveFilter {
|
||||
source_name: TEXT_SOURCE,
|
||||
filter_name: FILTER1,
|
||||
movement_type: obws::requests::MovementType::Up,
|
||||
})
|
||||
.await?;
|
||||
client
|
||||
.set_source_filter_settings(SourceFilterSettings {
|
||||
source_name: TEXT_SOURCE,
|
||||
filter_name: FILTER1,
|
||||
filter_settings: &json! {{
|
||||
"limit_cx": false,
|
||||
"limit_cy": false,
|
||||
"speed_x": -100.0
|
||||
}},
|
||||
})
|
||||
.await?;
|
||||
client
|
||||
.set_source_filter_visibility(SourceFilterVisibility {
|
||||
source_name: TEXT_SOURCE,
|
||||
filter_name: FILTER1,
|
||||
filter_enabled: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
client
|
||||
.remove_filter_from_source(TEXT_SOURCE, FILTER1)
|
||||
.await?;
|
||||
client
|
||||
.remove_filter_from_source(TEXT_SOURCE, FILTER2)
|
||||
.await?;
|
||||
|
||||
// Audio monitor type
|
||||
|
||||
let source = client
|
||||
.get_special_sources()
|
||||
.await?
|
||||
.desktop_1
|
||||
.context("desktop audio device required for tests")?;
|
||||
|
||||
let original = client.get_audio_monitor_type(&source).await?;
|
||||
client
|
||||
.set_audio_monitor_type(&source, MonitorType::MonitorAndOutput)
|
||||
.await?;
|
||||
client.set_audio_monitor_type(&source, original).await?;
|
||||
|
||||
// Take source screenshot
|
||||
|
||||
client
|
||||
.take_source_screenshot(SourceScreenshot {
|
||||
source_name: TEXT_SOURCE,
|
||||
embed_picture_format: Some("png"),
|
||||
width: Some(10),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue