Skip to content

Commit

Permalink
Merge pull request #129 from Alexhuszagh/miri
Browse files Browse the repository at this point in the history
Add more comprehensive miri coverage.
  • Loading branch information
Alexhuszagh authored Sep 13, 2024
2 parents 5c50a33 + 783a18d commit 462a2a2
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 98 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added fuzzing and miri code safety analysis to our CI pipelines.
- Removed requirement of `alloc` in `no_std` ennvironments without the `write` feature.
- Make multi-digit optimizations in integer parsing optional.
- Much higher miri coverage including for proptests.
- Much higher miri coverage including for proptests and our corner cases from the golang test suite.

### Changed

Expand Down
9 changes: 6 additions & 3 deletions ci/miri.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ fi
# Test the write-float correctness tests.
cd "${home}"
cd lexical-write-float/etc/correctness
cargo run $FEATURES --release --bin shorter_interval
cargo run $FEATURES --release --bin random
cargo run $FEATURES --release --bin simple_random -- --iterations 1000000
cargo +nightly miri run $FEATURES --release --bin test-parse-golang
# NOTE: This is **extraordinarily slow, mostly because of how the data is parsed
# as TOML which makes loading it take forever.
if [ -z $COMPREHENSIVE ]; then
cargo +nightly miri run $FEATURES --release --bin test-parse-unittests
fi
104 changes: 55 additions & 49 deletions lexical-parse-float/etc/correctness/test-parse-golang/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,7 @@
// See https://unlicense.org/

use lexical_parse_float::FromLexical;

use std::io::prelude::*;
use std::path::PathBuf;
use std::{env, fs, io};

// PATH

/// Return the `target/debug` or `target/release` directory path.
pub fn build_dir() -> PathBuf {
env::current_exe()
.expect("unittest executable path")
.parent()
.expect("debug/release directory")
.to_path_buf()
}

/// Return the `target` directory path.
pub fn target_dir() -> PathBuf {
build_dir().parent().expect("target directory").to_path_buf()
}

/// Return the project directory path.
pub fn project_dir() -> PathBuf {
target_dir().parent().expect("project directory").to_path_buf()
}

/// Return the `data` directory path.
pub fn data_dir() -> PathBuf {
let mut dir = project_dir();
dir.push("test-parse-golang");
dir.push("parse-number-fxx-test-data");
dir.push("data");
dir
}
use std::collections::HashMap;

fn run_test(line: &str) {
// Tests have the following format:
Expand All @@ -56,22 +23,61 @@ fn run_test(line: &str) {

fn main() {
// Iterate over all .txt files in the directory.
let paths = fs::read_dir(data_dir()).expect("Please update the Git submodule");
for direntry in paths {
let path = direntry.unwrap().path();
if path.extension().unwrap() == "txt" {
// Have a data file, parse and run the tests.
let filename = path.file_name().unwrap().to_str().unwrap();
println!("Running Test: {}", filename);
let file = fs::File::open(path).unwrap();
let reader = io::BufReader::new(file);
let mut count: usize = 0;
for line in reader.lines() {
let line = line.unwrap();
run_test(&line);
count += 1;
// NOTE: Miri does not play nicely with directories so we just compile them in.
let tests: HashMap<&str, &str> = HashMap::from([
("freetype-2-7.txt", include_str!("parse-number-fxx-test-data/data/freetype-2-7.txt")),
(
"google-double-conversion.txt",
include_str!("parse-number-fxx-test-data/data/google-double-conversion.txt"),
),
("google-wuffs.txt", include_str!("parse-number-fxx-test-data/data/google-wuffs.txt")),
("ibm-fpgen.txt", include_str!("parse-number-fxx-test-data/data/ibm-fpgen.txt")),
(
"lemire-fast-double-parser.txt",
include_str!("parse-number-fxx-test-data/data/lemire-fast-double-parser.txt"),
),
(
"lemire-fast-float.txt",
include_str!("parse-number-fxx-test-data/data/lemire-fast-float.txt"),
),
(
"more-test-cases.txt",
include_str!("parse-number-fxx-test-data/data/more-test-cases.txt"),
),
(
"remyoudompheng-fptest-0.txt",
include_str!("parse-number-fxx-test-data/data/remyoudompheng-fptest-0.txt"),
),
(
"remyoudompheng-fptest-1.txt",
include_str!("parse-number-fxx-test-data/data/remyoudompheng-fptest-1.txt"),
),
(
"remyoudompheng-fptest-2.txt",
include_str!("parse-number-fxx-test-data/data/remyoudompheng-fptest-2.txt"),
),
(
"remyoudompheng-fptest-3.txt",
include_str!("parse-number-fxx-test-data/data/remyoudompheng-fptest-3.txt"),
),
(
"tencent-rapidjson.txt",
include_str!("parse-number-fxx-test-data/data/tencent-rapidjson.txt"),
),
("ulfjack-ryu.txt", include_str!("parse-number-fxx-test-data/data/ulfjack-ryu.txt")),
]);

// Unfortunately, randomize the data with miri is too expensive so we just use it normally.
for (&filename, data) in tests.iter() {
println!("Running Test: {}", filename);
for (count, line) in data.lines().enumerate() {
if cfg!(miri) && count % 10 == 0 {
println!("Running test {count} for conversion tests.");
}
run_test(line);
if cfg!(miri) && count > 3000 {
break;
}
println!("Ran {} tests.", count);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ use std::mem::transmute;
#[allow(dead_code)]
pub const SEED: [u32; 3] = [0x243f_6a88, 0x85a3_08d3, 0x1319_8a2e];
#[allow(dead_code)]
pub const ISAAC_SEED: [u8; 32] = [49, 52, 49, 53, 57, 50, 54, 53, 51, 53, 56, 57, 55, 57, 51, 50, 51, 56, 52, 54, 50, 54, 52, 51, 51, 56, 51, 50, 55, 57, 53, 48];
pub const ISAAC_SEED: [u8; 32] = [
49, 52, 49, 53, 57, 50, 54, 53, 51, 53, 56, 57, 55, 57, 51, 50, 51, 56, 52, 54, 50, 54, 52, 51,
51, 56, 51, 50, 55, 57, 53, 48,
];

pub fn validate(text: &str) {
let mut out = io::stdout();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
mod _common;

use _common::{validate, ISAAC_SEED};
use rand_isaac::Isaac64Rng;
use rand::distributions::Distribution;
use rand::distributions::uniform::Uniform;
use rand::distributions::Distribution;
use rand::{Rng, SeedableRng};
use rand_isaac::Isaac64Rng;
use std::char;

fn main() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
mod _common;

use _common::{validate, ISAAC_SEED};
use rand_isaac::Isaac64Rng;
use rand::{RngCore, SeedableRng};
use rand_isaac::Isaac64Rng;
use std::mem::transmute;

fn main() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
mod _common;

use _common::validate;
use std::u64;

fn main() {
for exp in 19..64 {
Expand Down
74 changes: 36 additions & 38 deletions lexical-parse-float/etc/correctness/test-parse-unittests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

use lexical_parse_float::FromLexical;
use serde::Deserialize;
use std::collections::HashMap;

// STRUCTS
// Derived structs for the Toml parser.

#[derive(Debug, Deserialize)]
struct StrtodTests {
negativeFormattingTests: Vec<String>,
NegativeFormattingTests: Vec<String>,
FormattingTests: Vec<FormattingTest>,
ConversionTests: Vec<ConversionTest>,
}
Expand All @@ -36,25 +37,6 @@ struct ConversionTest {

// PATH

/// Return the `target/debug` or `target/release` directory path.
pub fn build_dir() -> std::path::PathBuf {
std::env::current_exe()
.expect("unittest executable path")
.parent()
.expect("debug/release directory")
.to_path_buf()
}

/// Return the `target` directory path.
pub fn target_dir() -> std::path::PathBuf {
build_dir().parent().expect("target directory").to_path_buf()
}

/// Return the project directory path.
pub fn project_dir() -> std::path::PathBuf {
target_dir().parent().expect("project directory").to_path_buf()
}

fn run_test(string: &str, hex: &str) {
// We toggle between "inf" and "infinity" as valid Infinity identifiers.
let lower = string.to_lowercase();
Expand All @@ -71,37 +53,53 @@ fn run_test(string: &str, hex: &str) {
}

fn run_tests(tests: StrtodTests) {
let negative_tests_count = tests.negativeFormattingTests.len();
let negative_tests_count = tests.NegativeFormattingTests.len();
let formatting_tests_count = tests.FormattingTests.len();
let conversion_tests_count = tests.ConversionTests.len();
for test in tests.negativeFormattingTests {
// Unfortunately, randomize the data with miri is too expensive so we just use it normally.
let mut count = 0;
for test in tests.NegativeFormattingTests {
if cfg!(miri) && count % 10 == 0 {
println!("Running test {count} for negative formatting.");
}
assert!(f64::from_lexical(test.as_bytes()).is_err());
count += 1;
if cfg!(miri) && count > 500 {
break;
}
}
for test in tests.FormattingTests {
run_test(&test.str, &test.hex)
if cfg!(miri) && count % 10 == 0 {
println!("Running test {count} for positive formatting.");
}
run_test(&test.str, &test.hex);
count += 1;
if cfg!(miri) && count > 1500 {
break;
}
}
for test in tests.ConversionTests {
run_test(&test.str, &test.hex)
if cfg!(miri) && count % 10 == 0 {
println!("Running test {count} for conversion tests.");
}
run_test(&test.str, &test.hex);
if cfg!(miri) && count > 2500 {
break;
}
}
println!("Ran {} negative tests.", negative_tests_count);
println!("Ran {} formatting tests.", formatting_tests_count);
println!("Ran {} conversion tests.", conversion_tests_count);
println!("");
}

fn parse_tests(name: &str) -> StrtodTests {
let mut test_path = project_dir();
test_path.push("test-parse-unittests");
test_path.push(name);
let test_data = std::fs::read_to_string(test_path).unwrap();

toml::from_str(&test_data).unwrap()
println!("Ran {} conversion tests.\n", conversion_tests_count);
}

fn main() {
let filenames = ["strtod_tests.toml", "rust_parse_tests.toml"];
for filename in filenames.iter() {
// NOTE: Miri does not play nicely with directories so we just compile them in.
let tests: HashMap<&str, &str> = HashMap::from([
("strtod_tests.toml", include_str!("strtod_tests.toml")),
("rust_parse_tests.toml", include_str!("rust_parse_tests.toml")),
]);
for (&filename, &data) in tests.iter() {
println!("Running Test: {}", filename);
run_tests(parse_tests(filename));
run_tests(toml::from_str(data).unwrap());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# License: MIT

# strtod(string) method should not accept the following inputs
negativeFormattingTests = [
NegativeFormattingTests = [
"inf1", "inf+", ".E", "1.0e", "2.45+e+3", "23e.23", "e9", "+e", "e+", ".",
"e", ".7+", ".21e", "+", "", "infe", "nan(err", "nan)", "NAN(test_)_)",
"nan0", "-.e+", "-+12.34"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
# [5] http://bugs.python.org/

# strtod(string) method should not accept the following inputs
negativeFormattingTests = [
NegativeFormattingTests = [
"inf1", "inf+", ".E", "1.0e", "2.45+e+3", "23e.23", "e9", "+e", "e+", ".",
"e", ".7+", ".21e", "+", "", "infe", "nan(err", "nan)", "NAN(test_)_)",
"nan0", "-.e+", "-+12.34"
Expand Down

0 comments on commit 462a2a2

Please sign in to comment.