Compare commits

...

2 Commits

Author SHA1 Message Date
703ae38814 data_to_text basic testing 2025-06-06 09:14:32 -04:00
f93131622f Start of data_to_text family 2025-06-05 14:19:45 -04:00
15 changed files with 345 additions and 75 deletions

2
Cargo.lock generated
View File

@ -372,7 +372,7 @@ dependencies = [
[[package]]
name = "trevors_utilities"
version = "0.1.0"
version = "0.1.0-20250606"
dependencies = [
"clap",
"env_logger",

View File

@ -1,6 +1,6 @@
[package]
name = "trevors_utilities"
version = "0.1.0"
version = "0.1.0-20250606"
edition = "2024"
[dependencies]

26
README.md Normal file
View File

@ -0,0 +1,26 @@
# Trevors Utilities
## Overview
Trevors Utilities is a collection of code that Trevor has found useful.
All code has the following criteria:
- Traits as appropriate, implemented for concrete types
- All code must have 100% code coverage to be part of a 'tagged release'
- All code must have 100% documentation coverage with examples of inputs and outputs
## Code Areas
### Number System Conversion
A variety of methods for converting numbers between systems. Functionality includes
- Joining u8 values into u16 values with low and high bytes
- Splitting u16 values into the high and low u8 values
- Swapping endianness of u16 values
- Converting an array of bool values into the equivalent value in u8 with bits in MSB order
### Test Compression
Methods for compressing and decompressing test results using deflate compression.

View File

@ -0,0 +1 @@
q

View File

@ -0,0 +1 @@
0x71 q

View File

@ -0,0 +1 @@
the quick brown fox jumped over the lazy dog

View File

@ -0,0 +1,3 @@
0x74 0x68 0x65 0x20 0x71 0x75 0x69 0x63 0x6b 0x20 0x62 0x72 0x6f 0x77 0x6e 0x20 the quick brown
0x66 0x6f 0x78 0x20 0x6a 0x75 0x6d 0x70 0x65 0x64 0x20 0x6f 0x76 0x65 0x72 0x20 fox jumped over
0x74 0x68 0x65 0x20 0x6c 0x61 0x7a 0x79 0x20 0x64 0x6f 0x67 the lazy dog

10
src/bin/gen_test.rs Normal file
View File

@ -0,0 +1,10 @@
use trevors_utilities::data_to_text::DataToText;
fn main() {
let text = "q";
let displayed_hex = DataToText::data_to_text(text.as_bytes());
println!("RAW: [{text}]");
println!("FORMATTED:\n{displayed_hex}");
}

45
src/data_to_text.rs Normal file
View File

@ -0,0 +1,45 @@
pub struct DataToText {}
impl DataToText {
/// data_to_text_window
///
/// Convert a block of u8 data to text for user display skipping specified bytes
pub fn data_to_text_window(to_convert: &[u8], offset: usize, length: usize) -> String {
DataToText::data_to_text(&to_convert[offset..(offset + length)])
}
/// data_to_text
///
/// Convert a block of u8 data to text for user display
pub fn data_to_text(to_convert: &[u8]) -> String {
let mut result = String::new();
let mut hex_line = String::new();
let mut ascii_line = String::new();
for (idx, byte) in to_convert.iter().enumerate() {
hex_line += &format!("0x{:02x} ", byte);
let ascii_char = if byte.is_ascii_graphic() || byte.is_ascii_whitespace() {
*byte as char
} else {
'.'
};
ascii_line.push(ascii_char);
if idx % 16 == 15 {
// Write full line
result += &format!("{:<96} {}\n", hex_line, ascii_line);
hex_line.clear();
ascii_line.clear();
}
}
// Final line if there are leftover bytes
if !hex_line.is_empty() {
result += &format!("{:<96} {}\n", hex_line, ascii_line);
}
result
}
}

View File

@ -1,2 +1,3 @@
pub mod test_compression;
pub mod number_system_conversion;
pub mod test_compression;
pub mod data_to_text;

View File

@ -94,7 +94,24 @@ impl NumberSystemConversion {
(high as u16) << 8 | low as u16
}
pub fn join_bytes_u16_from_u8(high: u8, low: u8) -> u16 {
low as u16 | ((high as u16) << 8)
/// clear_high_bits
///
/// Clear the 8 MSB of the value
///
/// ex: 0xffff -> 0x00ff
/// 0xabcd -> 0x00cd
pub fn clear_high_bits(to_clear: u16) -> u16 {
to_clear & 0x00ff
}
/// clear_low_bits
///
/// Clear the 8 LSB of the value
///
/// ex: 0xffff -> 0xff00
/// 0xabcd -> 0xab00
pub fn clear_low_bits(to_clear: u16) -> u16 {
to_clear & 0xff00
}
}

View File

@ -1,10 +1,11 @@
use flate2::Compression;
use flate2::read::DeflateDecoder;
use flate2::write::DeflateEncoder;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::path::Path;
use flate2::Compression;
use flate2::read::DeflateDecoder;
use flate2::write::DeflateEncoder;
use std::string::String;
pub struct TestCompression {}
@ -17,20 +18,24 @@ impl TestCompression {
encoder.finish().expect("Failed to finish compression")
}
pub fn decompress_to_string(compressed_data: &[u8]) -> Result<String, std::io::Error> {
pub fn decompress_to_string(compressed_data: &[u8]) -> String {
let mut decoder = DeflateDecoder::new(compressed_data);
let mut decompressed = String::new();
decoder.read_to_string(&mut decompressed)?;
Ok(decompressed)
decoder
.read_to_string(&mut decompressed)
.expect("Unablew to decompress data to string");
decompressed
}
/// Load a pre-compressed test result to a string for comparison in an assert macro
pub fn load_compressed_file_to_string(source: &Path) -> String {
let mut compressed_file = File::open(&source).expect(format!("Unable to open compressed file at [{}]", source.display()).as_str());
let mut compressed_file = File::open(&source)
.expect(format!("Unable to open compressed file at [{}]", source.display()).as_str());
let mut compressed_data = Vec::new();
compressed_file.read_to_end(&mut compressed_data)
compressed_file
.read_to_end(&mut compressed_data)
.expect(format!("Unable to read compressed data from [{}]", source.display()).as_str());
TestCompression::decompress_to_string(&compressed_data).unwrap()
TestCompression::decompress_to_string(&compressed_data)
}
/// Save a str to a specified file as compressed data
@ -40,13 +45,17 @@ impl TestCompression {
.truncate(true) // optional: clear existing contents
.create(true)
.open(target_file)
.expect(&format!("Unable to open target file [{}] for writing", target_file.display()));
.expect(&format!(
"Unable to open target file [{}] for writing",
target_file.display()
));
let compressed_data = TestCompression::compress_string(source);
compressed_file
.write_all(&compressed_data)
.expect(&format!("Unable to write compressed data to [{}]", target_file.display()));
compressed_file.write_all(&compressed_data).expect(&format!(
"Unable to write compressed data to [{}]",
target_file.display()
));
}
pub fn compress_file<P: AsRef<Path>>(source: P, destination: P) -> io::Result<()> {
@ -68,13 +77,50 @@ impl TestCompression {
pub fn compress_string_to_file(text: &str, target: &Path) {
let compressed_text = TestCompression::compress_string(text);
let mut writer = File::create(target).expect("Unable to create target file.");
writer.write_all(&compressed_text).expect("Unable to write compressed data");
writer
.write_all(&compressed_text)
.expect("Unable to write compressed data");
}
pub fn decompress_file_to_string(source: &Path) -> String {
let mut compressed_file = File::open(source).expect("Unable to open source to compress.");
let mut compressed_data = Vec::new();
compressed_file.read_to_end(&mut compressed_data).expect("Unable to read compressed data");
TestCompression::decompress_to_string(&compressed_data).unwrap()
compressed_file
.read_to_end(&mut compressed_data)
.expect("Unable to read compressed data");
TestCompression::decompress_to_string(&compressed_data)
}
pub fn compress_file_to_array(file_to_compress: &Path) -> Vec<u8> {
let mut read_buffer = String::new();
let mut file = File::open(file_to_compress).unwrap();
file.read_to_string(&mut read_buffer)
.expect("Unable to read data to compress");
TestCompression::compress_string(&read_buffer)
}
pub fn decompress_file_to_array(to_decompress: &Path) -> Vec<u8> {
let mut read_buffer = Vec::new();
let mut file = File::open(to_decompress).expect(
format!(
"Unable to open file to decompress [{}]",
to_decompress.display()
)
.as_str(),
);
file.read_to_end(&mut read_buffer)
.expect(format!("Unable to read from [{}]", to_decompress.display()).as_str());
read_buffer
}
// let file = File::open(input_path)
// .map_err(|e| std::io::Error::new(e.kind(), format!("Unable to open input path [{}]: {}", input_path.display(), e)));
//
// let mut decoder = DeflateDecoder::new(file.unwrap());
// let mut return_vec = Vec::new();
// decoder.read_to_end(&mut return_vec)
// .map_err(|e| std::io::Error::new(e.kind(), format!("Unable to decompress data for [{}]: {}", input_path.display(), e)));
//
// debug!("Decompressed file from path: {}", input_path.display());
// return_vec
}

48
tests/data_to_text.rs Normal file
View File

@ -0,0 +1,48 @@
use trevors_utilities::data_to_text::DataToText;
fn read_bin(source: &str) -> Vec<u8> {
let full_path = format!("/home/tmerritt/Projects/trevors_utilities/resources/data_to_text/{}.bin", source);
println!("FULL PATH BIN: [{}]", full_path);
std::fs::read(full_path).unwrap()
}
fn read_display(source: &str) -> String {
let full_path = format!("/home/tmerritt/Projects/trevors_utilities/resources/data_to_text/{}.display", source);
println!("FULL PATH DIS: [{}]", full_path);
std::fs::read_to_string(full_path).unwrap()
}
#[test]
fn data_to_text() {
let data_to_display: &[u8] = &[
0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
0x74, 0x20, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x20,
];
let expected_data: &str = "0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f 0x70 0x71 0x72 0x73 0x74 0x20 fghijklmnopqrst \n0x76 0x77 0x78 0x79 0x7a 0x7b 0x7c 0x7d 0x7e 0x7f 0x20 vwxyz{|}~. \n";
let actual_data = DataToText::data_to_text(data_to_display);
assert_eq!(actual_data, expected_data);
}
#[test]
fn quickbrownfoxtest() {
let test_params = vec![
"quickbrownfox",
"q"
];
for stub in test_params {
// load the content from quickbrownfox.bin
let bin_data = read_bin(stub);
// format it for display
let formatted = DataToText::data_to_text(&bin_data);
// load the content from quickbrownfox.display
let expected = read_display(stub);
assert_eq!(
formatted, expected
);
}
}

View File

@ -7,38 +7,54 @@ fn byte_to_bool_and_bool_to_byte() {
// **** NOTE THAT THIS VISUALLY IS BACKWARDS AS INDEX 0 IS FIRST AND LSB IS LAST ****
let test_params: BTreeMap<u8, [bool; 8]> = BTreeMap::from([
(0xff, [true, true, true, true, true, true, true, true]),
(0x00, [false, false, false, false, false, false, false, false]),
(0xa0, [false, false, false, false, false, true, false, true])
(0x00, [
false, false, false, false, false, false, false, false,
]),
(0xa0, [false, false, false, false, false, true, false, true]),
]);
for (src, dst) in test_params {
assert_eq!(
dst,
NumberSystemConversion::byte_to_bool(src),
"Unable to convert [{}] to [{:?}]", src, dst
"Unable to convert [{}] to [{:?}]",
src,
dst
);
assert_eq!(
src,
NumberSystemConversion::bool_to_byte(dst),
"Unable to convert [{:?}] to [{}]", dst, src
"Unable to convert [{:?}] to [{}]",
dst,
src
);
assert_eq!(
src,
NumberSystemConversion::bool_to_byte(
NumberSystemConversion::byte_to_bool(src))
NumberSystemConversion::bool_to_byte(NumberSystemConversion::byte_to_bool(src))
);
}
}
#[test]
fn swap_endianness_u16() {
let test_params: Vec<(u16, u16)> = vec![(0xabcd, 0xcdab), (0x0000, 0x0000), (0xffff, 0xffff), (0x00ff, 0xff00)];
let test_params: Vec<(u16, u16)> = vec![
(0xabcd, 0xcdab),
(0x0000, 0x0000),
(0xffff, 0xffff),
(0x00ff, 0xff00),
];
for (src, dst) in test_params {
assert_eq!(src, NumberSystemConversion::swap_endian_u16(dst));
assert_eq!(src, NumberSystemConversion::swap_endian_u16(NumberSystemConversion::swap_endian_u16(src)));
assert_eq!(
src,
NumberSystemConversion::swap_endian_u16(NumberSystemConversion::swap_endian_u16(src))
);
assert_eq!(dst, NumberSystemConversion::swap_endian_u16(src));
assert_eq!(dst, NumberSystemConversion::swap_endian_u16(NumberSystemConversion::swap_endian_u16(dst)));
assert_eq!(
dst,
NumberSystemConversion::swap_endian_u16(NumberSystemConversion::swap_endian_u16(dst))
);
}
}
@ -47,16 +63,20 @@ fn swap_endinness_u32() {
let test_params: Vec<(u32, u32)> = vec![
(0xabcdef01, 0x01efcdab),
(0x12345678, 0x78563412),
(0xbadbeef0, 0xf0eedbba)
(0xbadbeef0, 0xf0eedbba),
];
for (src, dst) in test_params {
assert_eq!(src, NumberSystemConversion::swap_endian_u32(dst));
assert_eq!(src, NumberSystemConversion::swap_endian_u32(
NumberSystemConversion::swap_endian_u32(src)));
assert_eq!(
src,
NumberSystemConversion::swap_endian_u32(NumberSystemConversion::swap_endian_u32(src))
);
assert_eq!(dst, NumberSystemConversion::swap_endian_u32(src));
assert_eq!(dst, NumberSystemConversion::swap_endian_u32(
NumberSystemConversion::swap_endian_u32(dst)));
assert_eq!(
dst,
NumberSystemConversion::swap_endian_u32(NumberSystemConversion::swap_endian_u32(dst))
);
}
}
@ -66,19 +86,15 @@ fn split_bytes_u16_join_bytes_u16() {
(0x0000, (0x00, 0x00)),
(0xabcd, (0xab, 0xcd)),
(0xffff, (0xff, 0xff)),
(0xbeef, (0xbe, 0xef))
(0xbeef, (0xbe, 0xef)),
]);
for (joined, (high, low)) in test_params {
assert_eq!(
(high, low),
NumberSystemConversion::split_bytes_u16(joined));
assert_eq!((high, low), NumberSystemConversion::split_bytes_u16(joined));
assert_eq!(
joined,
NumberSystemConversion::join_bytes_u16_from_u8(high, low));
assert_eq!(
joined,
NumberSystemConversion::join_bytes_u16(high as u16, low as u16));
NumberSystemConversion::join_bytes_u16(high as u16, low as u16)
);
}
}
@ -88,12 +104,47 @@ fn combine_u8_to_u16() {
((0xff, 0x00), 0x00ff),
((0x00, 0xff), 0xff00),
((0xbe, 0xef), 0xefbe),
((0xef, 0xbe), 0xbeef)
((0xef, 0xbe), 0xbeef),
];
for ((low, high), base) in test_params {
assert_eq!(
base, NumberSystemConversion::combine_u8_to_u16(low, high)
)
assert_eq!(base, NumberSystemConversion::combine_u8_to_u16(low, high))
}
}
#[test]
fn clear_high_bits() {
let test_params = vec![
(0b1111_1111_0101_1010, 0b0000_0000_0101_1010),
(0b1111_1111_1111_1111, 0b0000_0000_1111_1111),
(0b0000_0000_1111_1111, 0b0000_0000_1111_1111),
(0b0101_1010_1001_0110, 0b0000_0000_1001_0110)
];
for (src, dst) in test_params {
let result = NumberSystemConversion::clear_high_bits(src);
println!("Rolled {src:016b} to {result:016b} / {dst:016b}");
assert_eq!(
NumberSystemConversion::clear_high_bits(src),
dst);
}
}
#[test]
fn clear_low_bits() {
let test_params = vec![
(0b1111_1111_0101_1010, 0b1111_1111_0000_0000),
(0b1111_1111_1111_1111, 0b1111_1111_0000_0000),
(0b0000_0000_1111_1111, 0b0000_0000_0000_0000),
(0b0101_1010_1001_0110, 0b0101_1010_0000_0000)
];
for (src, dst) in test_params {
let result = NumberSystemConversion::clear_low_bits(src);
println!("Rolled {src:08b} to {result:08b} / {dst:08b}");
assert_eq!(
NumberSystemConversion::clear_low_bits(src),
dst);
}
}

View File

@ -1,4 +1,3 @@
use std::fs::File;
use std::io::{Read, Write};
use tempfile::NamedTempFile;
use trevors_utilities::test_compression::TestCompression;
@ -12,12 +11,8 @@ fn smoke() {
fn compression_round_trip() {
let to_compress = "The quick brown fox jumps over the lazy dog.";
;
let decompressed_text =
TestCompression::decompress_to_string(
&TestCompression::compress_string(
to_compress
)).unwrap();
TestCompression::decompress_to_string(&TestCompression::compress_string(to_compress));
assert_eq!(to_compress, decompressed_text);
}
@ -27,21 +22,23 @@ fn file_compression_round_trip() {
let string_to_compress = "The quick brown fox jumps over the lazy dog.";
let compressed_string = TestCompression::compress_string(string_to_compress);
let mut temp_target = NamedTempFile::new().unwrap();
let mut temp_reader = temp_target.reopen();
let temp_reader = temp_target.reopen();
// ...write the compressed version to a file...
temp_target.write_all(&compressed_string).expect("Unable to write compressed file for test");
temp_target
.write_all(&compressed_string)
.expect("Unable to write compressed file for test");
//
// ...decompress from file...
let mut compressed_read = Vec::new();
temp_reader.unwrap().read_to_end(&mut compressed_read).expect("Unable to read compressed data for test");
let decompresed = TestCompression::decompress_to_string(&compressed_read).expect("Unable to decompress string for test");
temp_reader
.unwrap()
.read_to_end(&mut compressed_read)
.expect("Unable to read compressed data for test");
let decompresed = TestCompression::decompress_to_string(&compressed_read);
//
// ...verify its the same.
assert_eq!(
string_to_compress,
decompresed
);
assert_eq!(string_to_compress, decompresed);
}
#[test]
@ -51,7 +48,9 @@ fn file_compression_reader() {
// ...write it to the temp file...
let mut temp_file = NamedTempFile::new().expect("Unable to get temp file for test");
temp_file.write_all(&*TestCompression::compress_string(to_compress)).expect("Unable to write compressed data to temp file for test");
temp_file
.write_all(&*TestCompression::compress_string(to_compress))
.expect("Unable to write compressed data to temp file for test");
let temp2_path = temp_file.path();
let uncompressed_text = TestCompression::load_compressed_file_to_string(temp2_path);
@ -67,11 +66,11 @@ fn file_compression_writer() {
// read back the compressed text
let mut read_buffer = Vec::new();
temp_file.read_to_end(&mut read_buffer).expect("Unable to read compressed data back.");
let decompressed_text = TestCompression::decompress_to_string(&read_buffer).expect("Unable to decompress text");
assert_eq!(
decompressed_text,
to_compress);
temp_file
.read_to_end(&mut read_buffer)
.expect("Unable to read compressed data back.");
let decompressed_text = TestCompression::decompress_to_string(&read_buffer);
assert_eq!(decompressed_text, to_compress);
}
#[test]
@ -80,18 +79,22 @@ fn compress_file() {
let test_text = "The quick brown fox jumps over the lazy dog.";
let mut original_file = NamedTempFile::new().unwrap();
let compressed_file = NamedTempFile::new().unwrap();
original_file.write(test_text.as_bytes()).expect("Unable to write original file");
original_file
.write(test_text.as_bytes())
.expect("Unable to write original file");
// run the method being tested
TestCompression::compress_file(original_file.path(), compressed_file.path()).expect("Unable to compress temp file.");
TestCompression::compress_file(original_file.path(), compressed_file.path())
.expect("Unable to compress temp file.");
// verify the data in the new file matches what we expect
let compressed_text = TestCompression::compress_string(test_text);
let mut reader_file = compressed_file.reopen().unwrap();
let mut result_text = Vec::new();
reader_file.read_to_end(&mut result_text).expect("Unable to read compressed data back to verify");
let decompressed_text = TestCompression::decompress_to_string(&result_text).unwrap();
reader_file
.read_to_end(&mut result_text)
.expect("Unable to read compressed data back to verify");
let decompressed_text = TestCompression::decompress_to_string(&result_text);
assert_eq!(decompressed_text, test_text);
}
@ -105,15 +108,32 @@ fn compress_string_to_file() {
TestCompression::compress_string_to_file(to_compress, target_file.path());
let mut file_contents = Vec::new();
duplicate.read_to_end(&mut file_contents).expect("Unable to read compressed file.");
duplicate
.read_to_end(&mut file_contents)
.expect("Unable to read compressed file.");
assert_eq!(file_contents, compressed_text);
}
#[test]
fn decompress_file_to_string() {
let to_compress = "The quick brown fox jumps over the lazy dog.";
let mut temp = NamedTempFile::new().unwrap();
let temp = NamedTempFile::new().unwrap();
TestCompression::compress_string_to_file(to_compress, temp.path());
let result = TestCompression::decompress_file_to_string(temp.path());
assert_eq!(to_compress, result);
}
#[test]
fn compress_file_to_array() {
let to_compress = "The quick brown fox jumps over the lazy dog.";
let mut temp_file = NamedTempFile::new().expect("Unable to get temp file for test");
temp_file.write_all(to_compress.as_bytes()).unwrap();
let result = TestCompression::compress_file_to_array(temp_file.path());
let decompressed_result = TestCompression::decompress_to_string(&result);
assert_eq!(to_compress, decompressed_result);
}
#[test]
fn decompress_to_array() {}