mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-08 02:07:56 -06:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1e421bbbb | |||
| 3a9381a56c | |||
| 90bdfbd2ae | |||
| a230d15ffc | |||
| 60bbd1f5d6 | |||
| 43ce8a4e01 |
37
.github/workflows/coverage.yaml
vendored
37
.github/workflows/coverage.yaml
vendored
@@ -42,14 +42,39 @@ jobs:
|
|||||||
|
|
||||||
- uses: taiki-e/install-action@cargo-llvm-cov
|
- uses: taiki-e/install-action@cargo-llvm-cov
|
||||||
- uses: taiki-e/install-action@nextest
|
- uses: taiki-e/install-action@nextest
|
||||||
|
- uses: taiki-e/install-action@just
|
||||||
|
|
||||||
- name: Generate coverage report
|
- name: Generate coverage report
|
||||||
run: |
|
run: |
|
||||||
cargo llvm-cov --no-fail-fast --lcov --output-path lcov.info nextest
|
just coverage
|
||||||
|
|
||||||
|
- name: Download Coveralls CLI
|
||||||
|
run: |
|
||||||
|
# use GitHub Releases URL instead of coveralls.io because they can't maintain their own files; it 404s
|
||||||
|
curl -L https://github.com/coverallsapp/coverage-reporter/releases/download/v0.6.15/coveralls-linux-x86_64.tar.gz | tar -xz -C /usr/local/bin
|
||||||
|
|
||||||
- name: Upload coverage to Coveralls
|
- name: Upload coverage to Coveralls
|
||||||
uses: coverallsapp/github-action@v2
|
env:
|
||||||
with:
|
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
||||||
files: ./lcov.info
|
run: |
|
||||||
format: lcov
|
if [ ! -f "lcov.info" ]; then
|
||||||
allow-empty: false
|
echo "Error: lcov.info file not found. Coverage generation may have failed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in {1..10}; do
|
||||||
|
echo "Attempt $i: Uploading coverage to Coveralls..."
|
||||||
|
if coveralls -n report lcov.info; then
|
||||||
|
echo "Successfully uploaded coverage report."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $i -lt 10 ]; then
|
||||||
|
delay=$((2**i))
|
||||||
|
echo "Attempt $i failed. Retrying in $delay seconds..."
|
||||||
|
sleep $delay
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Failed to upload coverage report after 10 attempts."
|
||||||
|
exit 1
|
||||||
|
|||||||
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,8 +1,17 @@
|
|||||||
|
# IDE, Other files
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
rust-sdl2-emscripten/
|
||||||
|
|
||||||
|
# Build files
|
||||||
target/
|
target/
|
||||||
dist/
|
dist/
|
||||||
emsdk/
|
emsdk/
|
||||||
.idea
|
|
||||||
rust-sdl2-emscripten/
|
# Site build f iles
|
||||||
assets/site/build.css
|
|
||||||
tailwindcss-*
|
tailwindcss-*
|
||||||
|
assets/site/build.css
|
||||||
|
|
||||||
|
# Coverage reports
|
||||||
lcov.info
|
lcov.info
|
||||||
|
coverage.html
|
||||||
|
|||||||
29
Justfile
29
Justfile
@@ -1,16 +1,33 @@
|
|||||||
set shell := ["bash", "-c"]
|
set shell := ["bash", "-c"]
|
||||||
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
|
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
|
||||||
|
|
||||||
coverage_exclude_pattern := "app.rs|audio.rs"
|
# Regex to exclude files from coverage report, double escapes for Justfile + CLI
|
||||||
|
# You can use src\\\\..., but the filename alone is acceptable too
|
||||||
|
coverage_exclude_pattern := "src\\\\app.rs|audio.rs|src\\\\error.rs|platform\\\\emscripten.rs"
|
||||||
|
|
||||||
|
# !!! --ignore-filename-regex should be used on both reports & coverage testing
|
||||||
|
# !!! --remap-path-prefix prevents the absolute path from being used in the generated report
|
||||||
|
|
||||||
|
# Generate HTML report (for humans, source line inspection)
|
||||||
|
html: coverage
|
||||||
|
cargo llvm-cov report \
|
||||||
|
--remap-path-prefix \
|
||||||
|
--ignore-filename-regex "{{ coverage_exclude_pattern }}" \
|
||||||
|
--html \
|
||||||
|
--open
|
||||||
|
|
||||||
|
# Display report (for humans)
|
||||||
|
report-coverage: coverage
|
||||||
|
cargo llvm-cov report \
|
||||||
|
--remap-path-prefix \
|
||||||
|
--ignore-filename-regex "{{ coverage_exclude_pattern }}"
|
||||||
|
|
||||||
|
# Run & generate report (for CI)
|
||||||
coverage:
|
coverage:
|
||||||
# Run & generate report
|
|
||||||
cargo llvm-cov \
|
cargo llvm-cov \
|
||||||
|
--lcov \
|
||||||
|
--remap-path-prefix \
|
||||||
--ignore-filename-regex "{{ coverage_exclude_pattern }}" \
|
--ignore-filename-regex "{{ coverage_exclude_pattern }}" \
|
||||||
--output-path lcov.info \
|
--output-path lcov.info \
|
||||||
--profile coverage \
|
--profile coverage \
|
||||||
--no-fail-fast nextest
|
--no-fail-fast nextest
|
||||||
|
|
||||||
# Display report
|
|
||||||
cargo llvm-cov report \
|
|
||||||
--ignore-filename-regex "{{ coverage_exclude_pattern }}"
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ analyzer = "nextest"
|
|||||||
|
|
||||||
[jobs.coverage]
|
[jobs.coverage]
|
||||||
command = [
|
command = [
|
||||||
"just", "coverage"
|
"just", "report-coverage"
|
||||||
]
|
]
|
||||||
need_stdout = true
|
need_stdout = true
|
||||||
ignored_lines = [
|
ignored_lines = [
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::entity::traversal::Position;
|
use crate::entity::{graph::NodeId, traversal::Position};
|
||||||
|
|
||||||
/// Trait for entities that can participate in collision detection.
|
/// Trait for entities that can participate in collision detection.
|
||||||
pub trait Collidable {
|
pub trait Collidable {
|
||||||
@@ -19,7 +19,7 @@ pub trait Collidable {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CollisionSystem {
|
pub struct CollisionSystem {
|
||||||
/// Maps node IDs to lists of entity IDs that are at that node
|
/// Maps node IDs to lists of entity IDs that are at that node
|
||||||
node_entities: HashMap<usize, Vec<EntityId>>,
|
node_entities: HashMap<NodeId, Vec<EntityId>>,
|
||||||
/// Maps entity IDs to their current positions
|
/// Maps entity IDs to their current positions
|
||||||
entity_positions: HashMap<EntityId, Position>,
|
entity_positions: HashMap<EntityId, Position>,
|
||||||
/// Next available entity ID
|
/// Next available entity ID
|
||||||
@@ -62,7 +62,7 @@ impl CollisionSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets all entity IDs at a specific node
|
/// Gets all entity IDs at a specific node
|
||||||
pub fn entities_at_node(&self, node: usize) -> &[EntityId] {
|
pub fn entities_at_node(&self, node: NodeId) -> &[EntityId] {
|
||||||
self.node_entities.get(&node).map(|v| v.as_slice()).unwrap_or(&[])
|
self.node_entities.get(&node).map(|v| v.as_slice()).unwrap_or(&[])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ fn positions_overlap(a: &Position, b: &Position) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets all nodes that an entity is currently at or between.
|
/// Gets all nodes that an entity is currently at or between.
|
||||||
fn get_nodes(pos: &Position) -> SmallVec<[usize; 2]> {
|
fn get_nodes(pos: &Position) -> SmallVec<[NodeId; 2]> {
|
||||||
let mut nodes = SmallVec::new();
|
let mut nodes = SmallVec::new();
|
||||||
match pos {
|
match pos {
|
||||||
Position::AtNode(node) => nodes.push(*node),
|
Position::AtNode(node) => nodes.push(*node),
|
||||||
|
|||||||
@@ -192,14 +192,15 @@ impl Graph {
|
|||||||
|
|
||||||
// Check if the edge already exists in this direction or to the same target
|
// Check if the edge already exists in this direction or to the same target
|
||||||
if let Some(err) = adjacency_list.edges().find_map(|e| {
|
if let Some(err) = adjacency_list.edges().find_map(|e| {
|
||||||
|
if !replace {
|
||||||
// If we're not replacing the edge, we don't want to replace an edge that already exists in this direction
|
// If we're not replacing the edge, we don't want to replace an edge that already exists in this direction
|
||||||
if !replace && e.direction == direction {
|
if e.direction == direction {
|
||||||
Some(Err("Edge already exists in this direction."))
|
return Some(Err("Edge already exists in this direction."));
|
||||||
} else if e.target == to {
|
} else if e.target == to {
|
||||||
Some(Err("Edge already exists."))
|
return Some(Err("Edge already exists."));
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
}) {
|
}) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,37 +49,13 @@ use glam::UVec2;
|
|||||||
use sdl2::render::{Canvas, RenderTarget};
|
use sdl2::render::{Canvas, RenderTarget};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::texture::sprite::{AtlasTile, SpriteAtlas};
|
use crate::{
|
||||||
|
error::{GameError, TextureError},
|
||||||
|
texture::sprite::{AtlasTile, SpriteAtlas},
|
||||||
|
};
|
||||||
|
|
||||||
/// A text texture that renders characters from the atlas.
|
/// Converts a character to its tile name in the atlas.
|
||||||
pub struct TextTexture {
|
fn char_to_tile_name(c: char) -> Option<String> {
|
||||||
char_map: HashMap<char, AtlasTile>,
|
|
||||||
scale: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextTexture {
|
|
||||||
/// Creates a new text texture with the given atlas and scale.
|
|
||||||
pub fn new(scale: f32) -> Self {
|
|
||||||
Self {
|
|
||||||
char_map: HashMap::new(),
|
|
||||||
scale,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Maps a character to its atlas tile, handling special characters.
|
|
||||||
fn get_char_tile(&mut self, atlas: &SpriteAtlas, c: char) -> Option<AtlasTile> {
|
|
||||||
if let Some(tile) = self.char_map.get(&c) {
|
|
||||||
return Some(*tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tile_name = self.char_to_tile_name(c)?;
|
|
||||||
let tile = atlas.get_tile(&tile_name)?;
|
|
||||||
self.char_map.insert(c, tile);
|
|
||||||
Some(tile)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a character to its tile name in the atlas.
|
|
||||||
fn char_to_tile_name(&self, c: char) -> Option<String> {
|
|
||||||
let name = match c {
|
let name = match c {
|
||||||
// Letters A-Z
|
// Letters A-Z
|
||||||
'A'..='Z' | '0'..='9' => format!("text/{c}.png"),
|
'A'..='Z' | '0'..='9' => format!("text/{c}.png"),
|
||||||
@@ -96,6 +72,51 @@ impl TextTexture {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Some(name)
|
Some(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A text texture that renders characters from the atlas.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TextTexture {
|
||||||
|
char_map: HashMap<char, AtlasTile>,
|
||||||
|
scale: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TextTexture {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
scale: 1.0,
|
||||||
|
char_map: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextTexture {
|
||||||
|
/// Creates a new text texture with the given scale.
|
||||||
|
pub fn new(scale: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
scale,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_char_map(&self) -> &HashMap<char, AtlasTile> {
|
||||||
|
&self.char_map
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tile(&mut self, c: char, atlas: &mut SpriteAtlas) -> Result<Option<&mut AtlasTile>> {
|
||||||
|
if self.char_map.contains_key(&c) {
|
||||||
|
return Ok(self.char_map.get_mut(&c));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(tile_name) = char_to_tile_name(c) {
|
||||||
|
let tile = atlas
|
||||||
|
.get_tile(&tile_name)
|
||||||
|
.ok_or(GameError::Texture(TextureError::AtlasTileNotFound(tile_name)))?;
|
||||||
|
self.char_map.insert(c, tile);
|
||||||
|
Ok(self.char_map.get_mut(&c))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders a string of text at the given position.
|
/// Renders a string of text at the given position.
|
||||||
@@ -108,13 +129,16 @@ impl TextTexture {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut x_offset = 0;
|
let mut x_offset = 0;
|
||||||
let char_width = (8.0 * self.scale) as u32;
|
let char_width = (8.0 * self.scale) as u32;
|
||||||
let char_height = (8.0 * self.scale) as u32;
|
let char_height = self.text_height();
|
||||||
|
|
||||||
for c in text.chars() {
|
for c in text.chars() {
|
||||||
if let Some(mut tile) = self.get_char_tile(atlas, c) {
|
// Get the tile from the char_map, or insert it if it doesn't exist
|
||||||
|
if let Some(tile) = self.get_tile(c, atlas)? {
|
||||||
|
// Render the tile if it exists
|
||||||
let dest = sdl2::rect::Rect::new((position.x + x_offset) as i32, position.y as i32, char_width, char_height);
|
let dest = sdl2::rect::Rect::new((position.x + x_offset) as i32, position.y as i32, char_width, char_height);
|
||||||
tile.render(canvas, atlas, dest)?;
|
tile.render(canvas, atlas, dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always advance x_offset for all characters (including spaces)
|
// Always advance x_offset for all characters (including spaces)
|
||||||
x_offset += char_width;
|
x_offset += char_width;
|
||||||
}
|
}
|
||||||
@@ -138,7 +162,7 @@ impl TextTexture {
|
|||||||
let mut width = 0;
|
let mut width = 0;
|
||||||
|
|
||||||
for c in text.chars() {
|
for c in text.chars() {
|
||||||
if self.char_to_tile_name(c).is_some() {
|
if char_to_tile_name(c).is_some() || c == ' ' {
|
||||||
width += char_width;
|
width += char_width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
tests/common/mod.rs
Normal file
39
tests/common/mod.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use pacman::{
|
||||||
|
asset::{get_asset_bytes, Asset},
|
||||||
|
texture::sprite::SpriteAtlas,
|
||||||
|
};
|
||||||
|
use sdl2::{
|
||||||
|
image::LoadTexture,
|
||||||
|
render::{Canvas, Texture, TextureCreator},
|
||||||
|
video::{Window, WindowContext},
|
||||||
|
Sdl,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn setup_sdl() -> Result<(Canvas<Window>, TextureCreator<WindowContext>, Sdl), String> {
|
||||||
|
let sdl_context = sdl2::init()?;
|
||||||
|
let video_subsystem = sdl_context.video()?;
|
||||||
|
let window = video_subsystem
|
||||||
|
.window("test", 800, 600)
|
||||||
|
.position_centered()
|
||||||
|
.hidden()
|
||||||
|
.build()
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let canvas = window.into_canvas().build().map_err(|e| e.to_string())?;
|
||||||
|
let texture_creator = canvas.texture_creator();
|
||||||
|
Ok((canvas, texture_creator, sdl_context))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_atlas(canvas: &mut sdl2::render::Canvas<sdl2::video::Window>) -> SpriteAtlas {
|
||||||
|
let texture_creator = canvas.texture_creator();
|
||||||
|
let atlas_bytes = get_asset_bytes(Asset::Atlas).unwrap();
|
||||||
|
let atlas_json = get_asset_bytes(Asset::AtlasJson).unwrap();
|
||||||
|
|
||||||
|
let texture = texture_creator.load_texture_bytes(&atlas_bytes).unwrap();
|
||||||
|
let texture: Texture<'static> = unsafe { std::mem::transmute(texture) };
|
||||||
|
|
||||||
|
let mapper: pacman::texture::sprite::AtlasMapper = serde_json::from_slice(&atlas_json).unwrap();
|
||||||
|
|
||||||
|
SpriteAtlas::new(texture, mapper)
|
||||||
|
}
|
||||||
@@ -86,6 +86,71 @@ fn test_graph_edge_permissions() {
|
|||||||
assert_eq!(edge.permissions, EdgePermissions::GhostsOnly);
|
assert_eq!(edge.permissions, EdgePermissions::GhostsOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_add_connected_node() {
|
||||||
|
let mut graph = Graph::new();
|
||||||
|
let node1 = graph.add_node(Node {
|
||||||
|
position: glam::Vec2::new(0.0, 0.0),
|
||||||
|
});
|
||||||
|
|
||||||
|
let node2 = graph
|
||||||
|
.add_connected(
|
||||||
|
node1,
|
||||||
|
Direction::Right,
|
||||||
|
Node {
|
||||||
|
position: glam::Vec2::new(16.0, 0.0),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(graph.node_count(), 2);
|
||||||
|
let edge = graph.find_edge(node1, node2);
|
||||||
|
assert!(edge.is_some());
|
||||||
|
assert_eq!(edge.unwrap().direction, Direction::Right);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_error_on_negative_edge_distance() {
|
||||||
|
let mut graph = Graph::new();
|
||||||
|
let node1 = graph.add_node(Node {
|
||||||
|
position: glam::Vec2::new(0.0, 0.0),
|
||||||
|
});
|
||||||
|
let node2 = graph.add_node(Node {
|
||||||
|
position: glam::Vec2::new(16.0, 0.0),
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = graph.add_edge(node1, node2, false, Some(-1.0), Direction::Right, EdgePermissions::All);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_error_on_duplicate_edge_without_replace() {
|
||||||
|
let mut graph = create_test_graph();
|
||||||
|
let result = graph.add_edge(0, 1, false, None, Direction::Right, EdgePermissions::All);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_allow_replacing_an_edge() {
|
||||||
|
let mut graph = create_test_graph();
|
||||||
|
let result = graph.add_edge(0, 1, true, Some(42.0), Direction::Right, EdgePermissions::All);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
let edge = graph.find_edge(0, 1).unwrap();
|
||||||
|
assert_eq!(edge.distance, 42.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_find_edge_between_nodes() {
|
||||||
|
let graph = create_test_graph();
|
||||||
|
let edge = graph.find_edge(0, 1);
|
||||||
|
assert!(edge.is_some());
|
||||||
|
assert_eq!(edge.unwrap().target, 1);
|
||||||
|
|
||||||
|
let non_existent_edge = graph.find_edge(0, 99);
|
||||||
|
assert!(non_existent_edge.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_traverser_basic() {
|
fn test_traverser_basic() {
|
||||||
let graph = create_test_graph();
|
let graph = create_test_graph();
|
||||||
|
|||||||
109
tests/text.rs
Normal file
109
tests/text.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
use pacman::texture::{sprite::SpriteAtlas, text::TextTexture};
|
||||||
|
|
||||||
|
use crate::common::create_atlas;
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
/// Helper function to get all characters that should be in the atlas
|
||||||
|
fn get_all_chars() -> String {
|
||||||
|
let mut chars = Vec::new();
|
||||||
|
chars.extend('A'..='Z');
|
||||||
|
chars.extend('0'..='9');
|
||||||
|
chars.extend(['!', '-', '"', '/']);
|
||||||
|
chars.into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to check if a character is in the atlas and char_map
|
||||||
|
fn check_char(text_texture: &mut TextTexture, atlas: &mut SpriteAtlas, c: char) {
|
||||||
|
// Check that the character is not in the char_map yet
|
||||||
|
assert!(
|
||||||
|
!text_texture.get_char_map().contains_key(&c),
|
||||||
|
"Character {c} should not yet be in char_map"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the tile from the atlas, which caches the tile in the char_map
|
||||||
|
let tile = text_texture.get_tile(c, atlas);
|
||||||
|
|
||||||
|
assert!(tile.is_ok(), "Failed to get tile for character {c}");
|
||||||
|
assert!(tile.unwrap().is_some(), "Tile for character {c} not found in atlas");
|
||||||
|
|
||||||
|
// Check that the tile is now cached in the char_map
|
||||||
|
assert!(
|
||||||
|
text_texture.get_char_map().contains_key(&c),
|
||||||
|
"Tile for character {c} was not cached in char_map"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chars() -> Result<(), String> {
|
||||||
|
let (mut canvas, ..) = common::setup_sdl().map_err(|e| e.to_string())?;
|
||||||
|
let mut atlas = create_atlas(&mut canvas);
|
||||||
|
let mut text_texture = TextTexture::default();
|
||||||
|
|
||||||
|
get_all_chars()
|
||||||
|
.chars()
|
||||||
|
.for_each(|c| check_char(&mut text_texture, &mut atlas, c));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_render() -> Result<(), String> {
|
||||||
|
let (mut canvas, ..) = common::setup_sdl().map_err(|e| e.to_string())?;
|
||||||
|
let mut atlas = create_atlas(&mut canvas);
|
||||||
|
let mut text_texture = TextTexture::default();
|
||||||
|
|
||||||
|
let test_strings = vec!["Hello, world!".to_string(), get_all_chars()];
|
||||||
|
|
||||||
|
for string in test_strings {
|
||||||
|
if let Err(e) = text_texture.render(&mut canvas, &mut atlas, &string, glam::UVec2::new(0, 0)) {
|
||||||
|
return Err(e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_text_width() -> Result<(), String> {
|
||||||
|
let text_texture = TextTexture::default();
|
||||||
|
|
||||||
|
let test_strings = vec!["Hello, world!".to_string(), get_all_chars()];
|
||||||
|
|
||||||
|
for string in test_strings {
|
||||||
|
let width = text_texture.text_width(&string);
|
||||||
|
let height = text_texture.text_height();
|
||||||
|
|
||||||
|
assert!(width > 0, "Width for string {string} should be greater than 0");
|
||||||
|
assert!(height > 0, "Height for string {string} should be greater than 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_text_scale() -> Result<(), String> {
|
||||||
|
let string = "ABCDEFG !-/\"";
|
||||||
|
let base_width = (string.len() * 8) as u32;
|
||||||
|
|
||||||
|
let mut text_texture = TextTexture::new(0.5);
|
||||||
|
|
||||||
|
assert_eq!(text_texture.scale(), 0.5);
|
||||||
|
assert_eq!(text_texture.text_height(), 4);
|
||||||
|
assert_eq!(text_texture.text_width(""), 0);
|
||||||
|
assert_eq!(text_texture.text_width(string), base_width / 2);
|
||||||
|
|
||||||
|
text_texture.set_scale(2.0);
|
||||||
|
assert_eq!(text_texture.scale(), 2.0);
|
||||||
|
assert_eq!(text_texture.text_height(), 16);
|
||||||
|
assert_eq!(text_texture.text_width(string), base_width * 2);
|
||||||
|
assert_eq!(text_texture.text_width(""), 0);
|
||||||
|
|
||||||
|
text_texture.set_scale(1.0);
|
||||||
|
assert_eq!(text_texture.scale(), 1.0);
|
||||||
|
assert_eq!(text_texture.text_height(), 8);
|
||||||
|
assert_eq!(text_texture.text_width(string), base_width);
|
||||||
|
assert_eq!(text_texture.text_width(""), 0);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user