1mod app;
2mod server;
3mod tests_explorer;
4
5pub use crate::app::ProvolaGuiApp;
6use crate::server::Server;
7use crossbeam_channel::{bounded, Receiver, Sender};
8use eframe::epi::backend::RepaintSignal;
9use provola_core::test::xunit::FullyQualifiedTestCaseId;
10use provola_core::test_runners::TestRunnerOpt;
11use provola_core::*;
12use provola_core::{Action, AvailableTests, Error, TestResult};
13use provola_testrunners::make_test_runner;
14use provola_testrunners::TestRunnerInfo;
15use std::path::PathBuf;
16use std::{sync::Arc, thread};
17
18struct Setup {
19 config: GuiConfig,
20 repaint_signal: Arc<dyn RepaintSignal>,
21}
22
23enum ActionMessage {
24 Setup(Setup),
25 RunAll,
26 RunSelected,
27 UpdateConfig(GuiConfig),
28 ReqAvailableTests,
29 SelectSingleTest(FullyQualifiedTestCaseId),
30}
31
32enum FeedbackMessage {
33 AvailableTests(AvailableTests),
34 Result(TestResult),
35 WatchedChanged,
36 Error(String),
37}
38
39type ActionSender = Sender<ActionMessage>;
40type ActionReceiver = Receiver<ActionMessage>;
41type FeedbackReceiver = Receiver<FeedbackMessage>;
42
43#[derive(Clone)]
47struct FeedbackSender {
48 sender: Sender<FeedbackMessage>,
49 repaint_signal: Option<Arc<dyn RepaintSignal>>,
50}
51
52impl FeedbackSender {
53 fn send(&self, msg: FeedbackMessage) {
54 if let Err(err) = self.sender.send(msg) {
55 log::debug!("Error ignored: {}", err);
57 }
58
59 if let Some(repaint_signal) = &self.repaint_signal {
60 repaint_signal.request_repaint();
61 }
62 }
63}
64
65pub fn run(opt: GuiConfig) -> Result<(), Error> {
66 let (action_s, action_r) = bounded(1000);
68 let (feedback_s, feedback_r) = bounded(1000);
69
70 let feedback_s = FeedbackSender {
71 sender: feedback_s,
72 repaint_signal: None,
73 };
74
75 let mut server = Server::new(action_r, feedback_s);
76
77 thread::spawn(move || {
78 server.run();
79 });
80
81 let app = ProvolaGuiApp::new(opt, action_s, feedback_r);
83 let native_options = eframe::NativeOptions::default();
84
85 eframe::run_native(Box::new(app), native_options)
86}
87
88#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq, Eq)]
89pub enum ActionConfig {
90 BuildTestInputOutput(Language, Source, TestDataIn, TestDataOut),
91 TestRunner(TestRunnerInfo, TestRunnerOpt),
92}
93
94#[derive(serde::Deserialize, serde::Serialize, Default, Clone, Debug, PartialEq, Eq)]
95pub struct GuiConfig {
96 pub watch_path: Option<PathBuf>,
97 pub watch: bool,
98 pub action: Option<ActionConfig>,
99}
100
101impl TryFrom<&GuiConfig> for Action {
102 type Error = Error;
103
104 fn try_from(opt: &GuiConfig) -> Result<Self, Error> {
105 let action_cfg = opt.action.as_ref().ok_or(Error::NothingToDo)?;
106
107 let action = match action_cfg {
108 ActionConfig::BuildTestInputOutput(lang, source, input, output) => {
109 Action::BuildTestInputOutput(*lang, source.clone(), input.clone(), output.clone())
110 }
111 ActionConfig::TestRunner(info, opt) => {
112 let test_runner = make_test_runner(info.clone());
113 Action::TestRunner(test_runner?, opt.clone())
114 }
115 };
116
117 Ok(action)
118 }
119}