Skip to content
This repository has been archived by the owner on Apr 2, 2022. It is now read-only.

Commit

Permalink
Add TestRunner which supports most Centroid and ConvexHull cases.
Browse files Browse the repository at this point in the history
Centroid currently fails some test against master, but I will address
this in an upcoming PR to geo.

Plus we skip a couple of tests because we don't yet have a way to handle
collections of "POINT EMPTY".

The ConvexHull cases currently fail, at least some of which is because
geo always returns a Polygon for ConvexHull, whereas JTS might return
other geometry types
  • Loading branch information
michaelkirk committed Feb 25, 2021
1 parent 9f62eca commit 9ddbe8b
Show file tree
Hide file tree
Showing 5 changed files with 470 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
Cargo.lock
24 changes: 24 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "jts-test-runner"
version = "0.1.0"
authors = ["Michael Kirk <michael.code@endoftheworl.de>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
approx = "0.4.0"
geo = "*"
include_dir = { version = "0.6", features = ["search"] }
log = "0.4.14"
serde = { version = "*", features = ["derive"] }
serde-xml-rs = "*"
wkt = { version = "0.9", features = ["geo-types", "serde"] }

[dev-dependencies]
pretty_env_logger = "*"

[patch.crates-io]
geo = { git = "https://github.com/georust/geo", branch = "mkirk/centroid-fixups" }
geo-types = { git = "https://github.com/georust/geo", branch = "mkirk/centroid-fixups" }
wkt = { git = "https://github.com/georust/wkt" }
115 changes: 115 additions & 0 deletions src/input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use geo::{Point, Geometry};
use serde::Deserialize;

use super::Result;

/// Example of the XML that these structures represent
///
/// ```xml
/// <run>
/// <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
///
/// <case>
/// <desc>AA disjoint</desc>
/// <a>
/// POLYGON(
/// (0 0, 80 0, 80 80, 0 80, 0 0))
/// </a>
/// <b>
/// POLYGON(
/// (100 200, 100 140, 180 140, 180 200, 100 200))
/// </b>
/// <test><op name="relate" arg3="FF2FF1212" arg1="A" arg2="B"> true </op>
/// </test>
/// <test> <op name="intersects" arg1="A" arg2="B"> false </op></test>
/// <test> <op name="contains" arg1="A" arg2="B"> false </op></test>
/// </case>
/// </run>
/// ```
#[derive(Debug, Deserialize)]
pub(crate) struct Run {
#[serde(rename="case")]
pub cases: Vec<Case>,
}

#[derive(Debug, Deserialize)]
pub(crate) struct Case {
pub(crate) desc: String,
// `a` seems to always be a WKT geometry, but until we can handle `POINT EMPTY` this
// will error.
// see https://github.com/georust/wkt/issues/61
//
// I also spent some time trying to have serde "try" to deserialize, skipping any
// cases that were unparseable, without throwing away the whole thing but eventually ran out of time.
// See https://github.com/serde-rs/serde/issues/1583 for a related approach
//#[serde(deserialize_with = "wkt::deserialize_geometry")]
pub(crate) a: String,
#[serde(rename = "test")]
pub(crate) tests: Vec<Test>,
}

#[derive(Debug, Deserialize)]
pub(crate) struct Test {
#[serde(rename="op")]
pub(crate) operation_input: OperationInput,
}

#[derive(Debug, Deserialize)]
pub struct CentroidInput {
pub(crate) arg1: String,

#[serde(rename = "$value")]
#[serde(deserialize_with = "wkt::deserialize_point")]
pub(crate) expected: Option<geo::Point<f64>>,
}

#[derive(Debug, Deserialize)]
pub struct ConvexHullInput {
pub(crate) arg1: String,

#[serde(rename = "$value")]
#[serde(deserialize_with = "wkt::deserialize_geometry")]
pub(crate) expected: geo::Geometry<f64>,
}

#[derive(Debug, Deserialize)]
#[serde(tag = "name")]
pub(crate) enum OperationInput {
#[serde(rename = "getCentroid")]
CentroidInput(CentroidInput),

#[serde(rename = "convexhull")]
ConvexHullInput(ConvexHullInput),

#[serde(other)]
Unsupported,
}

#[derive(Debug)]
pub(crate) enum Operation {
Centroid {
subject: Geometry<f64>,
expected: Option<Point<f64>>,
},
ConvexHull {
subject: Geometry<f64>,
expected: Geometry<f64>,
}
}

impl OperationInput {
pub(crate) fn into_operation(self, geometry: Geometry<f64>) -> Result<Operation>
{
match self {
Self::CentroidInput(centroid_input) => {
assert_eq!("A", centroid_input.arg1);
Ok(Operation::Centroid { subject: geometry.clone(), expected: centroid_input.expected })
}
Self::ConvexHullInput(convex_hull_input) => {
assert_eq!("A", convex_hull_input.arg1);
Ok(Operation::ConvexHull { subject: geometry.clone(), expected: convex_hull_input.expected })
}
Self::Unsupported => Err("This OperationInput not supported".into())
}
}
}
82 changes: 82 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#[macro_use]
extern crate log;

mod input;
use input::Operation;

mod runner;
pub use runner::TestRunner;

type Error = Box<dyn std::error::Error>;
type Result<T> = std::result::Result<T, Error>;

#[cfg(test)]
mod tests {
use super::*;

fn init_logging() {
use std::sync::Once;
static LOG_SETUP: Once = Once::new();
LOG_SETUP.call_once(|| {
pretty_env_logger::init();
});
}

#[test]
fn test_centroid() {
init_logging();
let mut runner = TestRunner::new().matching_filename_glob("*Centroid.xml");
runner.run().expect("test cases failed");

// sanity check that *something* was run
assert!(
runner.failures().len() + runner.successes().len() > 0,
"No tests were run."
);

if !runner.failures().is_empty() {
let failure_text = runner
.failures()
.iter()
.map(|failure| format!("{}", failure))
.collect::<Vec<String>>()
.join("\n");
panic!(
"{} failures / {} successes in JTS test suite:\n{}",
runner.failures().len(),
runner.successes().len(),
failure_text
);
}
}

#[test]
// several of the ConvexHull tests are currently failing
#[ignore]
fn test_all_general() {
init_logging();
let mut runner = TestRunner::new();
runner.run().expect("test cases failed");

// sanity check that *something* was run
assert!(
runner.failures().len() + runner.successes().len() > 0,
"No tests were run."
);

if !runner.failures().is_empty() {
let failure_text = runner
.failures()
.iter()
.map(|failure| format!("{}", failure))
.collect::<Vec<String>>()
.join("\n");
panic!(
"{} failures / {} successes in JTS test suite:\n{}",
runner.failures().len(),
runner.successes().len(),
failure_text
);
}
}
}
Loading

0 comments on commit 9ddbe8b

Please sign in to comment.