mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-05-06 07:17:27 +00:00
feat: Move ID tests into their own crate with a CLI (#177)
* fix: ID tests not in correct directory * refactor: Move game-id test logic into its own crate * id-tests: Add CLI that reads JSON input * id-tests: Update crate docs * Remove node ID test * id-tests: Don't try to parse unneeded info * id-tests: Enable cli feature by default
This commit is contained in:
parent
88bf996a5e
commit
b248a7661e
7 changed files with 144 additions and 113 deletions
|
|
@ -1,5 +1,5 @@
|
|||
[workspace]
|
||||
members = ["crates/cli", "crates/lib"]
|
||||
members = ["crates/cli", "crates/lib", "crates/id-tests"]
|
||||
|
||||
# Edition 2021, uses resolver = 2
|
||||
resolver = "2"
|
||||
|
|
|
|||
29
crates/id-tests/Cargo.toml
Normal file
29
crates/id-tests/Cargo.toml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
[package]
|
||||
name = "gamedig-id-tests"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
authors = [
|
||||
"rust-GameDig contributors [https://github.com/gamedig/rust-gamedig/contributors]",
|
||||
"node-GameDig contributors [https://github.com/gamedig/node-gamedig/contributors]",
|
||||
]
|
||||
license = "MIT"
|
||||
description = "Test if IDs match the gamedig rules"
|
||||
homepage = "https://github.com/gamedig/rust-gamedig/CONTRIBUTING.md#naming"
|
||||
repository = "https://github.com/gamedig/rust-gamedig"
|
||||
readme = "README.md"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
[features]
|
||||
cli = ["dep:serde_json", "dep:serde"]
|
||||
default = ["cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "gamedig-id-tests"
|
||||
required-features = ["cli"]
|
||||
|
||||
[dependencies]
|
||||
number_to_words = "0.1"
|
||||
roman_numeral = "0.1"
|
||||
|
||||
serde_json = { version = "1", optional = true }
|
||||
serde = { version = "1", optional = true, features = ["derive"] }
|
||||
|
|
@ -1,10 +1,7 @@
|
|||
#![cfg(all(test, feature = "game_defs"))]
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::{collections::HashMap, fs, io::Read};
|
||||
|
||||
use gamedig::GAMES;
|
||||
|
||||
use utils::*;
|
||||
mod utils;
|
||||
use utils::{extract_bracketed_suffix, split_on_switch_between_alpha_numeric};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum IDRule {
|
||||
|
|
@ -384,42 +381,9 @@ pub fn test_game_name_rules<'a, I: Iterator<Item = (&'a str, &'a str)>>(games: I
|
|||
wrong_ids
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_definitions_match_name_rules() {
|
||||
let wrong = test_game_name_rules(GAMES.entries().map(|(id, game)| (id.to_owned(), game.name)));
|
||||
assert!(wrong.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "Don't test node by default"]
|
||||
fn check_node_definitions_match_name_rules() {
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.open("./node-gamedig/games.txt")
|
||||
.unwrap();
|
||||
|
||||
let mut text = String::new();
|
||||
file.read_to_string(&mut text).unwrap();
|
||||
|
||||
let games = text
|
||||
.split('\n')
|
||||
.map(|line| line.trim())
|
||||
.filter(|line| !line.starts_with('#') && !line.is_empty())
|
||||
.filter_map(|line| {
|
||||
let parts: Vec<_> = line.splitn(3, '|').collect();
|
||||
if parts.len() > 1 {
|
||||
Some((parts[0].split(',').next().unwrap(), parts[1]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let wrong = test_game_name_rules(games);
|
||||
assert!(wrong.is_empty());
|
||||
}
|
||||
|
||||
fn test_single_game_rule(id: &str, name: &str) -> Vec<IDFail> { test_game_name_rules(std::iter::once((id, name))) }
|
||||
pub fn test_single_game_rule(id: &str, name: &str) -> Vec<IDFail> { test_game_name_rules(std::iter::once((id, name))) }
|
||||
|
||||
#[cfg(test)]
|
||||
mod id_tests {
|
||||
use super::{test_game_name_rules, test_single_game_rule};
|
||||
#[test]
|
||||
|
|
@ -485,72 +449,3 @@ mod id_tests {
|
|||
assert!(test_single_game_rule("jc3m", "Just Cause 3 - Multiplayer").is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
mod utils {
|
||||
/// Split a str when characters swap between being digits and not digits.
|
||||
pub fn split_on_switch_between_alpha_numeric(text: &str) -> Vec<String> {
|
||||
if text.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let mut parts = Vec::with_capacity(text.len());
|
||||
let mut current = Vec::with_capacity(text.len());
|
||||
|
||||
let mut iter = text.chars();
|
||||
let c = iter.next().unwrap();
|
||||
let mut last_was_numeric = c.is_ascii_digit();
|
||||
current.push(c);
|
||||
|
||||
for c in iter {
|
||||
if c.is_ascii_digit() == last_was_numeric {
|
||||
current.push(c);
|
||||
} else {
|
||||
parts.push(current.iter().collect());
|
||||
current.clear();
|
||||
current.push(c);
|
||||
last_was_numeric = !last_was_numeric;
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(current.into_iter().collect());
|
||||
|
||||
parts
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_correctly() {
|
||||
assert_eq!(
|
||||
split_on_switch_between_alpha_numeric("2D45A"),
|
||||
&["2", "D", "45", "A"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_symbol_broken_numbers() {
|
||||
let game_name = super::extract_game_parts_from_name("Darkest Hour: Europe '44-'45");
|
||||
assert_eq!(game_name.words, &["Darkest", "Hour", "Europe", "4445"]);
|
||||
}
|
||||
|
||||
/// Extract parts at end of string enclosed in brackets.
|
||||
pub fn extract_bracketed_suffix(text: &str) -> (&str, Option<&str>) {
|
||||
if let Some(text) = text.strip_suffix(')') {
|
||||
if let Some((text, extra)) = text.rsplit_once('(') {
|
||||
return (text, Some(extra));
|
||||
}
|
||||
}
|
||||
|
||||
(text, None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_brackets_correctly() {
|
||||
assert_eq!(
|
||||
extract_bracketed_suffix("no brackets here"),
|
||||
("no brackets here", None)
|
||||
);
|
||||
assert_eq!(
|
||||
extract_bracketed_suffix("Game name (with protocol here)"),
|
||||
("Game name ", Some("with protocol here"))
|
||||
);
|
||||
}
|
||||
}
|
||||
31
crates/id-tests/src/main.rs
Normal file
31
crates/id-tests/src/main.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#![cfg(feature = "cli")]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Format for input games (the same as used in node-gamedig/lib/games.js).
|
||||
type GamesInput = HashMap<String, Game>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
|
||||
struct Game {
|
||||
name: String,
|
||||
}
|
||||
|
||||
use gamedig_id_tests::test_game_name_rules;
|
||||
|
||||
fn main() {
|
||||
let games: GamesInput = if let Some(file) = std::env::args_os().skip(1).next() {
|
||||
let file = std::fs::OpenOptions::new().read(true).open(file).unwrap();
|
||||
|
||||
serde_json::from_reader(file).unwrap()
|
||||
} else {
|
||||
serde_json::from_reader(std::io::stdin().lock()).unwrap()
|
||||
};
|
||||
|
||||
let failed = test_game_name_rules(
|
||||
games
|
||||
.iter()
|
||||
.map(|(key, game)| (key.as_str(), game.name.as_str())),
|
||||
);
|
||||
|
||||
assert!(failed.is_empty());
|
||||
}
|
||||
66
crates/id-tests/src/utils.rs
Normal file
66
crates/id-tests/src/utils.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/// Split a str when characters swap between being digits and not digits.
|
||||
pub fn split_on_switch_between_alpha_numeric(text: &str) -> Vec<String> {
|
||||
if text.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let mut parts = Vec::with_capacity(text.len());
|
||||
let mut current = Vec::with_capacity(text.len());
|
||||
|
||||
let mut iter = text.chars();
|
||||
let c = iter.next().unwrap();
|
||||
let mut last_was_numeric = c.is_ascii_digit();
|
||||
current.push(c);
|
||||
|
||||
for c in iter {
|
||||
if c.is_ascii_digit() == last_was_numeric {
|
||||
current.push(c);
|
||||
} else {
|
||||
parts.push(current.iter().collect());
|
||||
current.clear();
|
||||
current.push(c);
|
||||
last_was_numeric = !last_was_numeric;
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(current.into_iter().collect());
|
||||
|
||||
parts
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_correctly() {
|
||||
assert_eq!(
|
||||
split_on_switch_between_alpha_numeric("2D45A"),
|
||||
&["2", "D", "45", "A"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_symbol_broken_numbers() {
|
||||
let game_name = super::extract_game_parts_from_name("Darkest Hour: Europe '44-'45");
|
||||
assert_eq!(game_name.words, &["Darkest", "Hour", "Europe", "4445"]);
|
||||
}
|
||||
|
||||
/// Extract parts at end of string enclosed in brackets.
|
||||
pub fn extract_bracketed_suffix(text: &str) -> (&str, Option<&str>) {
|
||||
if let Some(text) = text.strip_suffix(')') {
|
||||
if let Some((text, extra)) = text.rsplit_once('(') {
|
||||
return (text, Some(extra));
|
||||
}
|
||||
}
|
||||
|
||||
(text, None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_brackets_correctly() {
|
||||
assert_eq!(
|
||||
extract_bracketed_suffix("no brackets here"),
|
||||
("no brackets here", None)
|
||||
);
|
||||
assert_eq!(
|
||||
extract_bracketed_suffix("Game name (with protocol here)"),
|
||||
("Game name ", Some("with protocol here"))
|
||||
);
|
||||
}
|
||||
|
|
@ -38,8 +38,7 @@ phf = { version = "0.11", optional = true, features = ["macros"] }
|
|||
clap = { version = "4.1.11", optional = true, features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
number_to_words = "0.1"
|
||||
roman_numeral = "0.1"
|
||||
gamedig-id-tests = { path = "../id-tests", no-default-features = true }
|
||||
|
||||
# Examples
|
||||
[[example]]
|
||||
|
|
|
|||
11
crates/lib/tests/game_ids.rs
Normal file
11
crates/lib/tests/game_ids.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#![cfg(all(test, feature = "game_defs"))]
|
||||
|
||||
use gamedig::GAMES;
|
||||
|
||||
use gamedig_id_tests::test_game_name_rules;
|
||||
|
||||
#[test]
|
||||
fn check_definitions_match_name_rules() {
|
||||
let wrong = test_game_name_rules(GAMES.entries().map(|(id, game)| (id.to_owned(), game.name)));
|
||||
assert!(wrong.is_empty());
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue