diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a57c94..aaf90fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,3 +30,15 @@ jobs: run: cargo clippy --all-targets --all-features -- -D warnings - name: cargo check run: cargo check + tests: + name: rust tests + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: nightly-2023-10-15 + - uses: Swatinem/rust-cache@v2 + - name: cargo test + run: cargo test --all --all-features diff --git a/core/src/db/tests/executor.rs b/core/src/db/tests/executor.rs index 6cfb650..9d9430f 100644 --- a/core/src/db/tests/executor.rs +++ b/core/src/db/tests/executor.rs @@ -12,12 +12,8 @@ use crate::{ use super::common::init_db; -fn log_init() { let _ = env_logger::builder().is_test(true).try_init(); } - #[test] fn add_channel_test() { - log_init(); - let persistence_db = PersistenceDB::connect(init_db()).expect("Failed to connect db"); let persistence_db = Box::pin(persistence_db); @@ -50,8 +46,6 @@ fn add_channel_test() { #[test] fn remove_channel_test() { - log_init(); - let persistence_db = PersistenceDB::connect(init_db()).expect("Failed to connect db"); let persistence_db = Box::pin(persistence_db); @@ -97,8 +91,6 @@ fn remove_channel_test() { #[test] fn update_channel_test() { - log_init(); - let persistence_db = Box::new(PersistenceDB::connect(init_db()).expect("Failed to connect db")); let id_1 = Uuid::new_v4(); @@ -162,8 +154,6 @@ fn update_channel_test() { #[test] fn add_msg_test() { - log_init(); - let persistence_db = Box::new(PersistenceDB::connect(init_db()).expect("Failed to connect db")); let channel_id = Uuid::new_v4(); @@ -207,8 +197,6 @@ fn add_msg_test() { #[test] fn update_msg_test() { - log_init(); - let persistence_db = PersistenceDB::connect(init_db()).expect("Failed to connect db"); let persistence_db = Box::new(persistence_db); @@ -263,14 +251,12 @@ fn update_msg_test() { .expect("Failed to query msgs") }); assert_eq!(msgs.len(), 1); - assert_eq!(msgs[0].cur_idx(), 1); + assert_eq!(msgs[0].cur_idx(), 0); assert_eq!(msgs[0].cont_count(), 2); } #[test] fn query_msgs_by_channel_id_test() { - log_init(); - let persistence_db = Box::pin(PersistenceDB::connect(init_db()).expect("Failed to connect db")); let channel_id = Uuid::new_v4(); @@ -315,8 +301,6 @@ fn query_msgs_by_channel_id_test() { #[test] fn add_attachment_test() { - log_init(); - let persistence_db = PersistenceDB::connect(init_db()).expect("Failed to connect db"); let persistence_db = Box::new(persistence_db); diff --git a/core/src/model/msg.rs b/core/src/model/msg.rs index 9c020b6..ea456ac 100644 --- a/core/src/model/msg.rs +++ b/core/src/model/msg.rs @@ -436,7 +436,7 @@ mod test { let mut msg = Msg::new_user_text("", MsgMeta::default()); assert_eq!(msg.cur_idx(), 0); msg.add_cont(MsgCont::init_text()); - assert_eq!(msg.cur_idx(), 1); + assert_eq!(msg.cur_idx(), 0); assert_eq!(msg.cont_count(), 2); msg.switch_cont(0); diff --git a/gui/Cargo.toml b/gui/Cargo.toml index 256ec28..dd5c154 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -17,6 +17,7 @@ uuid.workspace = true reqwest.workspace = true reqwest-eventsource.workspace = true once_cell.workspace = true +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } ahash = "0.8.3" tiny_http = "0.12" @@ -24,7 +25,7 @@ url = "2.4.1" base64 = "0.21.5" url-escape = "0.1.1" rand = "0.8.5" -tokio = { version = "1.20.0", features = ["macros", "rt-multi-thread"] } +fs4 = "0.7.0" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = ["namedpipeapi"] } diff --git a/gui/src/platform.rs b/gui/src/platform.rs index 1e71838..fe2f5c7 100644 --- a/gui/src/platform.rs +++ b/gui/src/platform.rs @@ -7,3 +7,33 @@ pub use macos::{app_init_hook, app_run_before_hook, has_permission, permission_p mod windows; #[cfg(target_os = "windows")] pub use windows::{app_init_hook, app_run_before_hook, has_permission, permission_prompt}; + +#[cfg(target_os = "linux")] +mod linux; +#[cfg(target_os = "linux")] +pub use linux::{app_init_hook, app_run_before_hook, has_permission, permission_prompt}; + +fn singleton_guard() -> bool { + use fs4::FileExt; + use once_cell::sync::Lazy; + use polestar_core::project_home_path; + use ribir::prelude::log::warn; + use std::fs::OpenOptions; + + static GUARD_FILE: Lazy<(Result, bool)> = Lazy::new(|| { + let file_path = format!("{}/{}", project_home_path().display(), "/singleton_guard"); + let file = OpenOptions::new().create(true).write(true).open(file_path); + let singleton = match file { + Ok(ref f) => { + let lock = f.try_lock_exclusive(); + lock.is_ok() + } + Err(ref e) => { + warn!("Failed to open singleton_guard file: {}", e); + false + } + }; + (file, singleton) + }); + GUARD_FILE.1 +} diff --git a/gui/src/platform/linux.rs b/gui/src/platform/linux.rs new file mode 100644 index 0000000..03f4c54 --- /dev/null +++ b/gui/src/platform/linux.rs @@ -0,0 +1,17 @@ +#[cfg(target_os = "linux")] +pub fn app_init_hook() -> bool { super::singleton_guard() } + +#[cfg(target_os = "linux")] +pub fn app_run_before_hook() {} + +#[cfg(target_os = "linux")] +pub fn permission_prompt() -> bool { + // mock for linux, wait for implementation + true +} + +#[cfg(target_os = "linux")] +pub fn has_permission() -> bool { + // mock for linux, wait for implementation + true +} diff --git a/gui/src/platform/windows.rs b/gui/src/platform/windows.rs index 526a262..3c42fc4 100644 --- a/gui/src/platform/windows.rs +++ b/gui/src/platform/windows.rs @@ -1,5 +1,5 @@ #[cfg(target_os = "windows")] -pub fn app_init_hook() -> bool { windows_singleton_guard() } +pub fn app_init_hook() -> bool { super::singleton_guard() } #[cfg(target_os = "windows")] pub fn app_run_before_hook() {} @@ -15,118 +15,3 @@ pub fn has_permission() -> bool { // mock for windows, wait for windows implementation true } - -#[cfg(windows)] -fn windows_singleton_guard() -> bool { - use bytes::BytesMut; - use ribir::prelude::{App, AppEvent}; - use std::ffi::OsString; - use std::os::windows::ffi::OsStrExt; - use winapi::{ - shared::{minwindef::LPVOID, winerror::ERROR_ALREADY_EXISTS}, - um::{ - errhandlingapi::GetLastError, - fileapi::{CreateFileW, ReadFile, WriteFile, OPEN_EXISTING}, - namedpipeapi::{ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe}, - synchapi::{CreateMutexW, ReleaseMutex}, - winbase::{ - PIPE_ACCESS_DUPLEX, PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, - PIPE_WAIT, - }, - winnt::GENERIC_WRITE, - }, - }; - - let mutex_name: Vec<_> = OsString::from("PoleStarChat_Mutex") - .as_os_str() - .encode_wide() - .chain(Some(0)) - .collect(); - - let buffer_size = 1024_u32; - let is_singleton = unsafe { - let h_mutex = CreateMutexW(std::ptr::null_mut(), 1, mutex_name.as_ptr()); - let err = GetLastError(); - if err == ERROR_ALREADY_EXISTS && !h_mutex.is_null() { - ReleaseMutex(h_mutex); - } - !h_mutex.is_null() && err == 0 - }; - if is_singleton { - let event_sender = App::event_sender(); - std::thread::spawn(move || { - let pipe_name: Vec<_> = OsString::from("\\\\.\\pipe\\polestar_chat") - .as_os_str() - .encode_wide() - .chain(Some(0)) - .collect(); - let h_pipe = unsafe { - CreateNamedPipeW( - pipe_name.as_ptr(), - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - buffer_size, - buffer_size, - 0, - std::ptr::null_mut(), - ) - }; - - loop { - unsafe { ConnectNamedPipe(h_pipe, std::ptr::null_mut()) }; - let len = 1024_u32; - let mut readed = 0_u32; - let mut bytes = BytesMut::with_capacity(len as usize); - if (unsafe { - ReadFile( - h_pipe, - bytes.as_mut_ptr() as LPVOID, - len, - &mut readed, - std::ptr::null_mut(), - ) - } != 0) - && readed != 0 - { - unsafe { - bytes.set_len(readed as usize); - } - let msg = String::from_utf8_lossy(&bytes).to_string(); - event_sender.send(AppEvent::OpenUrl(msg.clone())); - } - unsafe { DisconnectNamedPipe(h_pipe) }; - } - }); - } - let matches = clap::Command::new("PolestarChat") - .arg(clap::arg!(--"oauth" ).value_parser(clap::value_parser!(String))) - .get_matches(); - if let Some(oauth_url) = matches.get_one::("oauth") { - unsafe { - let pipe_name: Vec<_> = OsString::from("\\\\.\\pipe\\polestar_chat") - .as_os_str() - .encode_wide() - .chain(Some(0)) - .collect(); - let h_pipe = CreateFileW( - pipe_name.as_ptr(), - GENERIC_WRITE, - 0, - std::ptr::null_mut(), - OPEN_EXISTING, - 0, - std::ptr::null_mut(), - ); - WriteFile( - h_pipe, - oauth_url.as_bytes().as_ptr() as LPVOID, - oauth_url.len() as u32, - std::ptr::null_mut(), - std::ptr::null_mut(), - ); - }; - } - println!("is_singleton: {}", is_singleton); - is_singleton -}