diff --git a/Cargo.lock b/Cargo.lock index 9495c53..c2eaad3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2095,9 +2095,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "jni" @@ -2787,9 +2787,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1c2c742266c2f1041c914ba65355a83ae8747b05f208319784083583494b4b" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" [[package]] name = "pathdiff" @@ -3009,9 +3009,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "e9d89e5dba24725ae5678020bf8f1357a9aa7ff10736b551adbcd3f8d17d766f" dependencies = [ "unicode-ident", ] @@ -3024,9 +3024,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "556d0f47a940e895261e77dc200d5eadfc6ef644c179c6f5edfc105e3a2292c8" dependencies = [ "proc-macro2", ] @@ -3291,15 +3291,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "4378ea89513870b6e2303ec50618e97da0fa43cdd9ded83ad3b6bad2693c08c6" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "safe_arch" @@ -3405,9 +3405,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "3bfa246f936730408c0abee392cc1a50b118ece708c7f630516defd64480c7d8" [[package]] name = "serde" @@ -3431,18 +3431,18 @@ dependencies = [ [[package]] name = "serde_ignored" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b3da7eedd967647a866f67829d1c79d184d7c4521126e9cc2c46a9585c6d21" +checksum = "51212eb6171778353d78ef5860fdffe29ac17f03a69375d2dc14fbb5754d54a4" dependencies = [ "serde", ] [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "8778cc0b528968fe72abec38b5db5a20a70d148116cd9325d2bc5f5180ca3faf" dependencies = [ "itoa", "ryu", @@ -3800,9 +3800,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +checksum = "09ee3a69cd2c7e06684677e5629b3878b253af05e4714964204279c6bc02cf0b" dependencies = [ "proc-macro2", "quote", @@ -3904,18 +3904,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -4730,9 +4730,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" diff --git a/src/constants.rs b/src/constants.rs index ec500cc..a4e0404 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -293,3 +293,24 @@ pub const ARG_COPYRIGHT: &str = r#"Gupax is licensed under GPLv3. For more information, see link below: "#; + +//---------------------------------------------------------------------------------------------------- TESTS +#[cfg(test)] +mod test { + #[test] + fn gupax_version_is_semver() { + assert_eq!(crate::GUPAX_VERSION.len(), 6); + } + + #[test] + fn app_ratio_is_4_by_3() { + assert_eq!(format!("{:.3}", crate::APP_MIN_WIDTH/crate::APP_MIN_HEIGHT), "1.333"); + assert_eq!(format!("{:.3}", crate::APP_MAX_WIDTH/crate::APP_MAX_HEIGHT), "1.333"); + assert_eq!(format!("{:.3}", crate::APP_DEFAULT_WIDTH/crate::APP_DEFAULT_HEIGHT), "1.333"); + } + + #[test] + fn git_commit_is_41_chars() { + assert_eq!(crate::COMMIT.len(), 41); + } +} diff --git a/src/disk.rs b/src/disk.rs index 3c7e26d..a115128 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -151,74 +151,10 @@ impl State { let max_threads = num_cpus::get(); let current_threads = if max_threads == 1 { 1 } else { max_threads / 2 }; Self { - gupax: Gupax { - simple: true, - auto_update: true, - auto_p2pool: false, - auto_xmrig: false, - ask_before_quit: true, - save_before_quit: true, - #[cfg(not(target_os = "macos"))] - update_via_tor: true, - #[cfg(target_os = "macos")] // Arti library has issues on macOS - update_via_tor: false, - p2pool_path: DEFAULT_P2POOL_PATH.to_string(), - xmrig_path: DEFAULT_XMRIG_PATH.to_string(), - absolute_p2pool_path: into_absolute_path(DEFAULT_P2POOL_PATH.to_string()).unwrap(), - absolute_xmrig_path: into_absolute_path(DEFAULT_XMRIG_PATH.to_string()).unwrap(), - selected_width: APP_DEFAULT_WIDTH as u16, - selected_height: APP_DEFAULT_HEIGHT as u16, - ratio: Ratio::Width, - tab: Tab::About, - }, - p2pool: P2pool { - simple: true, - mini: true, - auto_ping: true, - auto_select: true, - out_peers: 10, - in_peers: 10, - log_level: 3, - node: crate::NodeEnum::C3pool, - arguments: String::new(), - address: String::with_capacity(96), - name: "Local Monero Node".to_string(), - ip: "localhost".to_string(), - rpc: "18081".to_string(), - zmq: "18083".to_string(), - selected_index: 0, - selected_name: "Local Monero Node".to_string(), - selected_ip: "localhost".to_string(), - selected_rpc: "18081".to_string(), - selected_zmq: "18083".to_string(), - }, - xmrig: Xmrig { - simple: true, - pause: 0, - simple_rig: String::with_capacity(30), - arguments: String::with_capacity(300), - address: String::with_capacity(96), - name: "Local P2Pool".to_string(), - rig: GUPAX_VERSION_UNDERSCORE.to_string(), - ip: "localhost".to_string(), - port: "3333".to_string(), - selected_index: 0, - selected_name: "Local P2Pool".to_string(), - selected_ip: "localhost".to_string(), - selected_rig: GUPAX_VERSION_UNDERSCORE.to_string(), - selected_port: "3333".to_string(), - api_ip: "localhost".to_string(), - api_port: "18088".to_string(), - tls: false, - keepalive: false, - current_threads, - max_threads, - }, - version: Arc::new(Mutex::new(Version { - gupax: GUPAX_VERSION.to_string(), - p2pool: P2POOL_VERSION.to_string(), - xmrig: XMRIG_VERSION.to_string(), - })), + gupax: Gupax::default(), + p2pool: P2pool::default(), + xmrig: Xmrig::with_threads(max_threads, current_threads), + version: Arc::new(Mutex::new(Version::default())), } } @@ -237,6 +173,14 @@ impl State { } } + // Conver [State] to [String] + pub fn to_string(&self) -> Result { + match toml::ser::to_string(self) { + Ok(s) => Ok(s), + Err(e) => { error!("State | Couldn't serialize default file: {}", e); Err(TomlError::Serialize(e)) }, + } + } + // Combination of multiple functions: // 1. Attempt to read file from path into [String] // |_ Create a default file if not found @@ -261,7 +205,10 @@ impl State { Ok(s) => Ok(s), Err(_) => { warn!("State | Attempting merge..."); - Self::merge(string, path) + match Self::merge(&string) { + Ok(mut new) => { Self::save(&mut new, path)?; Ok(new) }, + Err(e) => Err(e), + } }, } } @@ -271,10 +218,7 @@ impl State { pub fn create_new(path: &PathBuf) -> Result { info!("State | Creating new default..."); let new = Self::new(); - let string = match toml::ser::to_string(&new) { - Ok(o) => o, - Err(e) => { error!("State | Couldn't serialize default file: {}", e); return Err(TomlError::Serialize(e)) }, - }; + let string = Self::to_string(&new)?; fs::write(path, string)?; info!("State | Write ... OK"); Ok(new) @@ -302,18 +246,12 @@ impl State { // Take [String] as input, merge it with whatever the current [default] is, // leaving behind old keys+values and updating [default] with old valid ones. - // Automatically overwrite current file. - pub fn merge(old: String, path: &PathBuf) -> Result { - let default = match toml::ser::to_string(&Self::new()) { - Ok(string) => { info!("State | Default TOML parse ... OK"); string }, - Err(err) => { error!("State | Couldn't parse default TOML into string"); return Err(TomlError::Serialize(err)) }, - }; - let mut new: Self = match Figment::new().merge(Toml::string(&old)).merge(Toml::string(&default)).extract() { + pub fn merge(old: &str) -> Result { + let default = toml::ser::to_string(&Self::new()).unwrap(); + let new: Self = match Figment::from(Toml::string(&default)).merge(Toml::string(&old)).extract() { Ok(new) => { info!("State | TOML merge ... OK"); new }, Err(err) => { error!("State | Couldn't merge default + old TOML"); return Err(TomlError::Merge(err)) }, }; - // Attempt save - Self::save(&mut new, path)?; Ok(new) } } @@ -705,3 +643,322 @@ pub struct Version { pub p2pool: String, pub xmrig: String, } + +//---------------------------------------------------------------------------------------------------- [State] Defaults +impl Default for Gupax { + fn default() -> Self { + Self { + simple: true, + auto_update: true, + auto_p2pool: false, + auto_xmrig: false, + ask_before_quit: true, + save_before_quit: true, + #[cfg(not(target_os = "macos"))] + update_via_tor: true, + #[cfg(target_os = "macos")] // Arti library has issues on macOS + update_via_tor: false, + p2pool_path: DEFAULT_P2POOL_PATH.to_string(), + xmrig_path: DEFAULT_XMRIG_PATH.to_string(), + absolute_p2pool_path: into_absolute_path(DEFAULT_P2POOL_PATH.to_string()).unwrap(), + absolute_xmrig_path: into_absolute_path(DEFAULT_XMRIG_PATH.to_string()).unwrap(), + selected_width: APP_DEFAULT_WIDTH as u16, + selected_height: APP_DEFAULT_HEIGHT as u16, + ratio: Ratio::Width, + tab: Tab::About, + } + } +} +impl Default for P2pool { + fn default() -> Self { + Self { + simple: true, + mini: true, + auto_ping: true, + auto_select: true, + out_peers: 10, + in_peers: 10, + log_level: 3, + node: crate::NodeEnum::C3pool, + arguments: String::new(), + address: String::with_capacity(96), + name: "Local Monero Node".to_string(), + ip: "localhost".to_string(), + rpc: "18081".to_string(), + zmq: "18083".to_string(), + selected_index: 0, + selected_name: "Local Monero Node".to_string(), + selected_ip: "localhost".to_string(), + selected_rpc: "18081".to_string(), + selected_zmq: "18083".to_string(), + } + } +} +impl Xmrig { + fn with_threads(max_threads: usize, current_threads: usize) -> Self { + let mut xmrig = Self::default(); + Self { + max_threads, + current_threads, + ..xmrig + } + } +} +impl Default for Xmrig { + fn default() -> Self { + Self { + simple: true, + pause: 0, + simple_rig: String::with_capacity(30), + arguments: String::with_capacity(300), + address: String::with_capacity(96), + name: "Local P2Pool".to_string(), + rig: GUPAX_VERSION_UNDERSCORE.to_string(), + ip: "localhost".to_string(), + port: "3333".to_string(), + selected_index: 0, + selected_name: "Local P2Pool".to_string(), + selected_ip: "localhost".to_string(), + selected_rig: GUPAX_VERSION_UNDERSCORE.to_string(), + selected_port: "3333".to_string(), + api_ip: "localhost".to_string(), + api_port: "18088".to_string(), + tls: false, + keepalive: false, + current_threads: 1, + max_threads: 1, + } + } +} +impl Default for Version { + fn default() -> Self { + Self { + gupax: GUPAX_VERSION.to_string(), + p2pool: P2POOL_VERSION.to_string(), + xmrig: XMRIG_VERSION.to_string(), + } + } +} + +//---------------------------------------------------------------------------------------------------- TESTS +#[cfg(test)] +mod test { + #[test] + fn serde_default_state() { + let state = crate::State::new(); + let string = crate::State::to_string(&state).unwrap(); + crate::State::from_str(&string).unwrap(); + } + #[test] + fn serde_default_node() { + let node = crate::Node::new_vec(); + let string = crate::Node::to_string(&node).unwrap(); + crate::Node::from_str_to_vec(&string).unwrap(); + } + #[test] + fn serde_default_pool() { + let pool = crate::Pool::new_vec(); + let string = crate::Pool::to_string(&pool).unwrap(); + crate::Pool::from_str_to_vec(&string).unwrap(); + } + + #[test] + fn serde_custom_state() { + let state = r#" + [gupax] + simple = true + auto_update = true + auto_p2pool = false + auto_xmrig = false + ask_before_quit = true + save_before_quit = true + update_via_tor = true + p2pool_path = "p2pool/p2pool" + xmrig_path = "xmrig/xmrig" + absolute_p2pool_path = "/home/hinto/p2pool/p2pool" + absolute_xmrig_path = "/home/hinto/xmrig/xmrig" + selected_width = 1280 + selected_height = 960 + tab = "About" + ratio = "Width" + + [p2pool] + simple = true + mini = true + auto_ping = true + auto_select = true + out_peers = 10 + in_peers = 450 + log_level = 3 + node = "Seth" + arguments = "" + address = "44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW" + name = "Local Monero Node" + ip = "192.168.1.123" + rpc = "18089" + zmq = "18083" + selected_index = 0 + selected_name = "Local Monero Node" + selected_ip = "192.168.1.123" + selected_rpc = "18089" + selected_zmq = "18083" + + [xmrig] + simple = true + pause = 0 + simple_rig = "" + arguments = "" + tls = false + keepalive = false + max_threads = 32 + current_threads = 16 + address = "" + api_ip = "localhost" + api_port = "18088" + name = "linux" + rig = "Gupax" + ip = "192.168.1.122" + port = "3333" + selected_index = 1 + selected_name = "linux" + selected_rig = "Gupax" + selected_ip = "192.168.1.122" + selected_port = "3333" + + [version] + gupax = "v1.0.0" + p2pool = "v2.5" + xmrig = "v6.18.0" + "#; + let state = crate::State::from_str(state).unwrap(); + crate::State::to_string(&state).unwrap(); + } + + #[test] + fn serde_custom_node() { + let node = r#" + ['Local Monero Node'] + ip = "localhost" + rpc = "18081" + zmq = "18083" + + ['asdf-_. ._123'] + ip = "localhost" + rpc = "11" + zmq = "1234" + + ['aaa bbb'] + ip = "192.168.2.333" + rpc = "1" + zmq = "65535" + "#; + let node = crate::Node::from_str_to_vec(node).unwrap(); + crate::Node::to_string(&node).unwrap(); + } + + #[test] + fn serde_custom_pool() { + let pool = r#" + ['Local P2Pool'] + rig = "Gupax_v1.0.0" + ip = "localhost" + port = "3333" + + ['aaa xx .. -'] + rig = "Gupax" + ip = "192.168.22.22" + port = "1" + + [' a'] + rig = "Gupax_v1.0.0" + ip = "127.0.0.1" + port = "65535" + "#; + let pool = crate::Pool::from_str_to_vec(pool).unwrap(); + crate::Pool::to_string(&pool).unwrap(); + } + + // Make sure we keep the user's old values that are still + // valid but discard the ones that don't exist anymore. + #[test] + fn merge_state() { + let bad_state = r#" + [gupax] + SETTING_THAT_DOESNT_EXIST_ANYMORE = 123123 + simple = false + auto_update = true + auto_p2pool = false + auto_xmrig = false + ask_before_quit = true + save_before_quit = true + update_via_tor = true + p2pool_path = "p2pool/p2pool" + xmrig_path = "xmrig/xmrig" + absolute_p2pool_path = "" + absolute_xmrig_path = "" + selected_width = 0 + selected_height = 0 + tab = "About" + ratio = "Width" + + [p2pool] + SETTING_THAT_DOESNT_EXIST_ANYMORE = "String" + simple = true + mini = true + auto_ping = true + auto_select = true + out_peers = 10 + in_peers = 450 + log_level = 6 + node = "Seth" + arguments = "" + address = "44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW" + name = "Local Monero Node" + ip = "localhost" + rpc = "18081" + zmq = "18083" + selected_index = 0 + selected_name = "Local Monero Node" + selected_ip = "localhost" + selected_rpc = "18081" + selected_zmq = "18083" + + [xmrig] + SETTING_THAT_DOESNT_EXIST_ANYMORE = true + simple = true + pause = 0 + simple_rig = "" + arguments = "" + tls = false + keepalive = false + max_threads = 32 + current_threads = 16 + address = "" + api_ip = "localhost" + api_port = "18088" + name = "Local P2Pool" + rig = "Gupax_v1.0.0" + ip = "localhost" + port = "3333" + selected_index = 0 + selected_name = "Local P2Pool" + selected_rig = "Gupax_v1.0.0" + selected_ip = "localhost" + selected_port = "3333" + + [version] + gupax = "v1.0.0" + p2pool = "v2.5" + xmrig = "v6.18.0" + "#.to_string(); + let merged_state = crate::State::merge(&bad_state).unwrap(); + let merged_state = crate::State::to_string(&merged_state).unwrap(); + println!("{}", merged_state); + assert!(merged_state.contains("simple = false")); + assert!(merged_state.contains("in_peers = 450")); + assert!(merged_state.contains("log_level = 6")); + assert!(merged_state.contains(r#"node = "Seth""#)); + assert!(!merged_state.contains("SETTING_THAT_DOESNT_EXIST_ANYMORE")); + assert!(merged_state.contains("44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW")); + } +} diff --git a/src/helper.rs b/src/helper.rs index d126d9b..4484948 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -1713,3 +1713,179 @@ impl Hashrate { } } } + +//---------------------------------------------------------------------------------------------------- TESTS +#[cfg(test)] +mod test { + #[test] + fn calc_payouts_and_xmr_from_output_p2pool() { + use crate::helper::{PubP2poolApi,P2poolRegex}; + use std::sync::{Arc,Mutex}; + let public = Arc::new(Mutex::new(PubP2poolApi::new())); + let output_parse = Arc::new(Mutex::new(String::from( + r#"You received a payout of 5.000000000001 XMR in block 1111 + You received a payout of 5.000000000001 XMR in block 1112 + You received a payout of 5.000000000001 XMR in block 1113"# + ))); + let output_pub = Arc::new(Mutex::new(String::new())); + let elapsed = std::time::Duration::from_secs(60); + let regex = P2poolRegex::new(); + PubP2poolApi::update_from_output(&public, &output_parse, &output_pub, elapsed, ®ex); + let public = public.lock().unwrap(); + println!("{:#?}", public); + assert_eq!(public.payouts, 3); + assert_eq!(public.payouts_hour, 180.0); + assert_eq!(public.payouts_day, 4320.0); + assert_eq!(public.payouts_month, 129600.0); + assert_eq!(public.xmr, 15.000000000003); + assert_eq!(public.xmr_hour, 900.00000000018); + assert_eq!(public.xmr_day, 21600.00000000432); + assert_eq!(public.xmr_month, 648000.0000001296); + } + + #[test] + fn serde_priv_p2pool_api() { + let data = + r#"{ + "hashrate_15m": 12, + "hashrate_1h": 11111, + "hashrate_24h": 468967, + "total_hashes": 2019283840922394082390, + "shares_found": 289037, + "average_effort": 915.563, + "current_effort": 129.297, + "connections": 123, + "incoming_connections": 96 + }"#; + use crate::helper::PrivP2poolApi; + let priv_api = PrivP2poolApi::str_to_priv_p2pool_api(data).unwrap(); + let json = serde_json::ser::to_string_pretty(&priv_api).unwrap(); + println!("{}", json); + let data_after_ser = +r#"{ + "hashrate_15m": 12, + "hashrate_1h": 11111, + "hashrate_24h": 468967, + "shares_found": 289037, + "average_effort": 915.563, + "current_effort": 129.297, + "connections": 123 +}"#; + assert_eq!(data_after_ser, json) + } + + #[test] + fn serde_priv_xmrig_api() { + let data = + r#"{ + "id": "6226e3sd0cd1a6es", + "worker_id": "hinto", + "uptime": 123, + "restricted": true, + "resources": { + "memory": { + "free": 123, + "total": 123123, + "resident_set_memory": 123123123 + }, + "load_average": [10.97, 10.58, 10.47], + "hardware_concurrency": 12 + }, + "features": ["api", "asm", "http", "hwloc", "tls", "opencl", "cuda"], + "results": { + "diff_current": 123, + "shares_good": 123, + "shares_total": 123, + "avg_time": 123, + "avg_time_ms": 123, + "hashes_total": 123, + "best": [123, 123, 123, 13, 123, 123, 123, 123, 123, 123], + "error_log": [] + }, + "algo": "rx/0", + "connection": { + "pool": "localhost:3333", + "ip": "127.0.0.1", + "uptime": 123, + "uptime_ms": 123, + "ping": 0, + "failures": 0, + "tls": null, + "tls-fingerprint": null, + "algo": "rx/0", + "diff": 123, + "accepted": 123, + "rejected": 123, + "avg_time": 123, + "avg_time_ms": 123, + "hashes_total": 123, + "error_log": [] + }, + "version": "6.18.0", + "kind": "miner", + "ua": "XMRig/6.18.0 (Linux x86_64) libuv/2.0.0-dev gcc/10.2.1", + "cpu": { + "brand": "blah blah blah", + "family": 1, + "model": 2, + "stepping": 0, + "proc_info": 123, + "aes": true, + "avx2": true, + "x64": true, + "64_bit": true, + "l2": 123123, + "l3": 123123, + "cores": 12, + "threads": 24, + "packages": 1, + "nodes": 1, + "backend": "hwloc/2.8.0a1-git", + "msr": "ryzen_19h", + "assembly": "ryzen", + "arch": "x86_64", + "flags": ["aes", "vaes", "avx", "avx2", "bmi2", "osxsave", "pdpe1gb", "sse2", "ssse3", "sse4.1", "popcnt", "cat_l3"] + }, + "donate_level": 0, + "paused": false, + "algorithms": ["cn/1", "cn/2", "cn/r", "cn/fast", "cn/half", "cn/xao", "cn/rto", "cn/rwz", "cn/zls", "cn/double", "cn/ccx", "cn-lite/1", "cn-heavy/0", "cn-heavy/tube", "cn-heavy/xhv", "cn-pico", "cn-pico/tlo", "cn/upx2", "rx/0", "rx/wow", "rx/arq", "rx/graft", "rx/sfx", "rx/keva", "argon2/chukwa", "argon2/chukwav2", "argon2/ninja", "astrobwt", "astrobwt/v2", "ghostrider"], + "hashrate": { + "total": [111.11, 111.11, 111.11], + "highest": 111.11, + "threads": [ + [111.11, 111.11, 111.11] + ] + }, + "hugepages": true + }"#; + use crate::helper::PrivXmrigApi; + let priv_api = serde_json::from_str::(&data).unwrap(); + let json = serde_json::ser::to_string_pretty(&priv_api).unwrap(); + println!("{}", json); + let data_after_ser = +r#"{ + "worker_id": "hinto", + "resources": { + "load_average": [ + 10.97, + 10.58, + 10.47 + ] + }, + "connection": { + "pool": "localhost:3333", + "diff": 123, + "accepted": 123, + "rejected": 123 + }, + "hashrate": { + "total": [ + 111.11, + 111.11, + 111.11 + ] + } +}"#; + assert_eq!(data_after_ser, json) + } +} diff --git a/src/main.rs b/src/main.rs index c5160a7..9937cc2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -471,6 +471,7 @@ pub struct ErrorState { msg: String, // What message to display? ferris: ErrorFerris, // Which ferris to display? buttons: ErrorButtons, // Which buttons to display? + quit_twice: bool, // This indicates the user tried to quit on the [ask_before_quit] screen } impl Default for ErrorState { @@ -486,6 +487,7 @@ impl ErrorState { msg: "Unknown Error".to_string(), ferris: ErrorFerris::Oops, buttons: ErrorButtons::Okay, + quit_twice: false, } } @@ -506,6 +508,7 @@ impl ErrorState { msg: msg.into(), ferris, buttons, + quit_twice: false, }; } @@ -524,6 +527,7 @@ impl ErrorState { msg: String::new(), ferris: ErrorFerris::Sudo, buttons: ErrorButtons::Sudo, + quit_twice: false, }; SudoState::reset(state) } @@ -914,14 +918,16 @@ fn main() { impl eframe::App for App { fn on_close_event(&mut self) -> bool { - // If we're already on the [ask_before_quit] screen and - // the user tries to exit again, exit. - if self.error_state.buttons == ErrorButtons::StayQuit { - if self.state.gupax.save_before_quit { self.save_before_quit(); } - true - // Else, set up the [ask_before_quit] screen (if enabled). - } else if self.state.gupax.ask_before_quit { + if self.state.gupax.ask_before_quit { + // If we're already on the [ask_before_quit] screen and + // the user tried to exit again, exit. + if self.error_state.quit_twice { + if self.state.gupax.save_before_quit { self.save_before_quit(); } + return true + } + // Else, set the error self.error_state.set("", ErrorFerris::Oops, ErrorButtons::StayQuit); + self.error_state.quit_twice = true; false // Else, just quit. } else { @@ -1553,3 +1559,23 @@ impl eframe::App for App { }); } } + +//---------------------------------------------------------------------------------------------------- TESTS +#[cfg(test)] +mod test { + #[test] + fn build_app() { + let mut app = crate::App::new(std::time::Instant::now()); + crate::init_auto(&mut app); + } + + #[test] + fn build_regex() { + crate::Regexes::new(); + } + + #[test] + fn build_images() { + crate::Images::new(); + } +} diff --git a/src/node.rs b/src/node.rs index f3b2ea9..eb4ef1f 100644 --- a/src/node.rs +++ b/src/node.rs @@ -58,6 +58,7 @@ pub const NODE_IPS: [&str; 17] = [ ]; pub const COMMUNITY_NODE_LENGTH: usize = NODE_IPS.len(); +pub const COMMUNITY_NODE_MAX_CHARS: usize = 14; #[derive(Copy,Clone,Eq,PartialEq,Debug,Deserialize,Serialize)] pub enum NodeEnum { @@ -65,7 +66,17 @@ pub enum NodeEnum { Plowsof2,Rino,Feather1,Feather2,Seth,SupportXmr,SupportXmrIr,XmrVsBeast, } +impl Default for NodeEnum { + fn default() -> Self { + Self::new() + } +} + impl NodeEnum { + fn new() -> Self { + ip_to_enum(NODE_IPS[0]) + } + fn get_index(&self) -> usize { match self { C3pool => 0, @@ -280,7 +291,7 @@ impl Ping { pub fn new() -> Self { Self { nodes: NodeData::new_vec(), - fastest: NodeEnum::C3pool, + fastest: NodeEnum::new(), pinging: false, msg: "No ping in progress".to_string(), prog: 0.0, @@ -417,3 +428,23 @@ impl Ping { node_vec.lock().unwrap().push(NodeData { id: ip_to_enum(ip), ip, ms, color, }); } } + +//---------------------------------------------------------------------------------------------------- TESTS +#[cfg(test)] +mod test { + #[test] + fn validate_node_ips() { + for ip in crate::NODE_IPS { + assert!(ip.len() < 255); + assert!(ip.is_ascii()); + assert!(ip.ends_with(":18081") || ip.ends_with(":18089")); + } + } + + #[test] + fn spacing() { + for ip in crate::NODE_IPS { + assert!(crate::format_enum(crate::ip_to_enum(ip)).len() == crate::COMMUNITY_NODE_MAX_CHARS); + } + } +}