6 Commits

Author SHA1 Message Date
eeeck 995e89b903 Merge pull request #13 from erickahmed/dependabot/github_actions/actions/checkout-6
build(deps): bump actions/checkout from 5 to 6
2025-11-28 09:44:58 +01:00
dependabot[bot] 09f95c9127 build(deps): bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 01:33:14 +00:00
eeeck 768aa3d212 Merge pull request #11 from erickahmed/alert-autofix-4
Potential fix for code scanning alert no. 4: Workflow does not contain permissions
2025-11-16 10:43:41 +01:00
eeeck bbbf3c7a46 Merge pull request #12 from erickahmed/alert-autofix-3
Potential fix for code scanning alert no. 3: Workflow does not contain permissions
2025-11-16 10:42:30 +01:00
eeeck 6b54370a4e Potential fix for code scanning alert no. 3: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: Erick <engineering@erickahmed.com>
2025-11-16 10:41:56 +01:00
eeeck 7759df28e0 Potential fix for code scanning alert no. 4: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: Erick <engineering@erickahmed.com>
2025-11-16 10:40:51 +01:00
4 changed files with 242 additions and 64 deletions
+6 -3
View File
@@ -1,8 +1,11 @@
name: OCCT FFI Unit Tests name: OCCT FFI Unit Tests
permissions:
contents: read
packages: write
on: on:
push: push:
branches: [master, dev**] branches: [master, dev-occt**, dev-ffi**]
paths: paths:
- "src/occt/**" - "src/occt/**"
- "src/ffi/**" - "src/ffi/**"
@@ -41,7 +44,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
@@ -73,7 +76,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
+47 -5
View File
@@ -86,24 +86,66 @@ mod ffi_point {
pub use ffi_point::Point; pub use ffi_point::Point;
// FFI API tests // Basic tests
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::os::raw::c_double; use std::os::raw::c_double;
#[test] #[test]
fn types_compatibility() { fn point_debug_precision() {
let p = Point::new(1.123456789, 2.987654321, -3.555555555).expect("Failed to create point");
let debug_str = format!("{:?}", p);
let coords = p.coordinates();
assert!(debug_str.contains(&coords.0.to_string()));
assert!(debug_str.contains(&coords.1.to_string()));
assert!(debug_str.contains(&coords.2.to_string()));
}
#[test]
fn point_null_pointer_safety() {
let result = Point::new(1.0, 2.0, 3.0);
assert!(result.is_ok());
let p = result.unwrap();
let coords = p.coordinates();
assert_eq!(coords, (1.0, 2.0, 3.0));
}
#[test]
fn point_drop_impl_safety() {
{
let p = Point::new(1.0, 2.0, 3.0).expect("Failed to create point");
// p goes out of scope here and should be dropped
}
let p2 = Point::new(4.0, 5.0, 6.0).expect("Failed to create point after drop");
assert_eq!(p2.coordinates(), (4.0, 5.0, 6.0));
}
#[test]
fn point_ffi_repr_c() {
use std::mem; use std::mem;
// PointShape should be repr(C) and zero-sized in Rust
assert_eq!(mem::size_of::<ffi_point::PointShape>(), 0); assert_eq!(mem::size_of::<ffi_point::PointShape>(), 0);
// c_double should match f64
assert_eq!(mem::size_of::<c_double>(), mem::size_of::<f64>()); assert_eq!(mem::size_of::<c_double>(), mem::size_of::<f64>());
assert_eq!(mem::align_of::<c_double>(), mem::align_of::<f64>()); assert_eq!(mem::align_of::<c_double>(), mem::align_of::<f64>());
} }
#[test] #[test]
fn basic_lifecycle() { fn point_coordinates_after_multiple_uses() {
let p = Point::new(1.0, 2.0, 3.0).expect("FFI creation failed"); let p = Point::new(1.1, 2.2, 3.3).expect("Failed to create point");
assert_eq!(p.coordinates(), (1.0, 2.0, 3.0));
let _coords1 = p.coordinates();
let _debug1 = format!("{:?}", p);
let _coords2 = p.coordinates();
let _debug2 = format!("{:?}", p);
let final_coords = p.coordinates();
assert_eq!(final_coords, (1.1, 2.2, 3.3));
} }
} }
+116 -32
View File
@@ -4,10 +4,11 @@
*/ */
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <catch2/benchmark/catch_benchmark.hpp>
#include <catch2/catch_approx.hpp> #include <catch2/catch_approx.hpp>
#include "point.hpp" #include "point.hpp"
TEST_CASE("Point lifecycle and coordinate operations", "[point]") { TEST_CASE("Test point creation, query and deletion behaviour", "[point]") {
auto check_coords = [](const point_shape_t* p, const double expected[3]) { auto check_coords = [](const point_shape_t* p, const double expected[3]) {
double x, y, z; double x, y, z;
coord_point(p, &x, &y, &z); coord_point(p, &x, &y, &z);
@@ -16,34 +17,65 @@ TEST_CASE("Point lifecycle and coordinate operations", "[point]") {
REQUIRE(z == Catch::Approx(expected[2])); REQUIRE(z == Catch::Approx(expected[2]));
}; };
SECTION("Basic creation and coordinate retrieval") { SECTION("Accept integer coordinates") {
double coords[3] = {3, -7, 2};
point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
delete_point(p);
}
SECTION("Accept float coordinates") {
double coords[3] = {3.2352, 7.124662, -2.5}; double coords[3] = {3.2352, 7.124662, -2.5};
point_shape_t* p = make_point(coords[0], coords[1], coords[2]); point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords); check_coords(p, coords);
delete_point(p); delete_point(p);
} }
SECTION("Extreme double values") { SECTION("XYZ Origin") {
double coords[3] = { double coords[3] = {0, 0, 0};
std::numeric_limits<double>::max(),
std::numeric_limits<double>::lowest(),
std::numeric_limits<double>::min()
};
point_shape_t* p = make_point(coords[0], coords[1], coords[2]); point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords); check_coords(p, coords);
delete_point(p); delete_point(p);
} }
SECTION("Special floating-point values") { SECTION("Large magnitude values") {
double coords[3] = { double coords[3] = {6.5186415e7, 9.48156654e8, -6.516515e6};
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::infinity(),
-std::numeric_limits<double>::infinity()
};
point_shape_t* p = make_point(coords[0], coords[1], coords[2]); point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
delete_point(p);
}
SECTION("Multiple points") {
const double points[5][3] = {
{3, 7, 2},
{3, 7, 2},
{-5, 3.7, 8},
{-3, -7, -2},
{0, 0, 1}
};
for (const auto& c : points) {
point_shape_t* p = make_point(c[0], c[1], c[2]);
check_coords(p, c);
delete_point(p);
}
}
SECTION("Extreme double limits") {
double coords[3] = {std::numeric_limits<double>::max(),
std::numeric_limits<double>::lowest(),
std::numeric_limits<double>::min()};
point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords);
delete_point(p);
}
SECTION("NaN and Infinity") {
double coords[3] = {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity()};
point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
double x, y, z; double x, y, z;
coord_point(p, &x, &y, &z); coord_point(p, &x, &y, &z);
REQUIRE(std::isnan(x)); REQUIRE(std::isnan(x));
REQUIRE(std::isinf(y)); REQUIRE(std::isinf(y));
REQUIRE(std::isinf(z)); REQUIRE(std::isinf(z));
@@ -53,35 +85,87 @@ TEST_CASE("Point lifecycle and coordinate operations", "[point]") {
delete_point(p); delete_point(p);
} }
SECTION("Multiple independent instances") { SECTION("Denormal values") {
const double points[3][3] = { double coords[3] = {std::numeric_limits<double>::denorm_min(),
{3, 7, 2}, -std::numeric_limits<double>::denorm_min(),
{-5, 3.7, 8}, 0.0};
{-3, -7, -2}
};
for (const auto& coords : points) {
point_shape_t* p = make_point(coords[0], coords[1], coords[2]); point_shape_t* p = make_point(coords[0], coords[1], coords[2]);
check_coords(p, coords); check_coords(p, coords);
delete_point(p); delete_point(p);
} }
}
SECTION("Null pointer safety") { SECTION("Clear single point") {
point_shape_t* null_p = nullptr; point_shape_t* p = make_point(1.0, 2.0, 3.0);
delete_point(null_p);
double x, y, z; double x, y, z;
coord_point(null_p, &x, &y, &z); coord_point(p, &x, &y, &z);
REQUIRE(x == Catch::Approx(1.0));
REQUIRE(y == Catch::Approx(2.0));
REQUIRE(z == Catch::Approx(3.0));
delete_point(p);
}
SECTION("Clear multiple points") {
const int num_points = 5;
point_shape_t* p[num_points];
for (int i = 0; i < num_points; ++i) {
p[i] = make_point(i * 1.0, i * 2.0, i * 3.0);
}
for (int i = 0; i < num_points; ++i) {
delete_point(p[i]);
}
}
SECTION("Clear already null shape") {
point_shape_t* null_p = nullptr;
REQUIRE(null_p == nullptr);
delete_point(null_p);
}
SECTION("Memory efficiency - multiple clear calls") {
for (int i = 0; i < 100; ++i) {
point_shape_t* tmp_p = make_point(1.0, 1.0, 1.0);
delete_point(tmp_p);
}
}
SECTION("Pointer validity after operations") {
point_shape_t* p = make_point(1.0, 2.0, 3.0);
REQUIRE(p != nullptr);
double x, y, z;
coord_point(p, &x, &y, &z);
delete_point(p);
} }
} }
TEST_CASE("Point memory management", "[point]") { TEST_CASE("Benchmark point operations") {
SECTION("Rapid allocation/deallocation cycle") { SECTION("make_point performance") {
for (int i = 0; i < 50; ++i) { point_shape_t* p = nullptr;
point_shape_t* p = make_point(1.0, 2.0, 3.0); BENCHMARK("Create point") {
REQUIRE(p != nullptr); return p = make_point(1.0, 2.0, 3.0);
};
delete_point(p); delete_point(p);
} }
SECTION("coord_point performance") {
point_shape_t* p = make_point(1.0, 2.0, 3.0);
double x, y, z;
BENCHMARK("Extract coordinates") {
coord_point(p, &x, &y, &z);
};
delete_point(p);
}
SECTION("delete_point performance") {
BENCHMARK("Delete point") {
point_shape_t* p = make_point(1.0, 2.0, 3.0);
delete_point(p);
};
} }
} }
+70 -21
View File
@@ -5,59 +5,108 @@
use arc_core::ffi::point::Point; use arc_core::ffi::point::Point;
// Safe wrapper tests
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn creation_and_query() { fn point_creation() {
let p = Point::new(1.0, 2.0, -3.0).expect("Safe creation failed"); let p = Point::new(1.0, 2.0, -3.0).expect("Failed to create point");
assert_eq!(p.coordinates(), (1.0, 2.0, -3.0)); let coords = p.coordinates();
assert_eq!(coords, (1.0, 2.0, -3.0));
} }
#[test] #[test]
fn manual_deletion() { fn point_query() {
let p = Point::new(1.0, 2.0, 3.0).expect("Creation failed"); let p = Point::new(5.5, 3.15, -0.001).expect("Failed to create point");
p.delete().expect("Manual deletion failed"); let coords = p.coordinates();
assert_eq!(coords, (5.5, 3.15, -0.001));
} }
#[test] #[test]
fn automatic_cleanup() { fn point_deletion() {
let p = Point::new(1.0, 2.0, 3.0).expect("Failed to create point");
let result = p.delete();
assert!(result.is_ok(), "Deletion should succeed for valid point");
}
#[test]
fn point_creation_extreme_values() {
let cases = [
(f64::MAX, f64::MIN, 0.0),
(0.0, 0.0, 0.0),
(-f64::MAX, -f64::MIN, f64::EPSILON),
];
for (x, y, z) in cases {
let p = Point::new(x, y, z).expect("Failed to create point with extreme values");
let coords = p.coordinates();
assert_eq!(coords, (x, y, z));
p.delete().expect("Failed to delete point");
}
}
#[test]
fn point_coordinates_immutability() {
let p = Point::new(1.5, 2.5, 3.5).expect("Failed to create point");
let coords1 = p.coordinates();
let coords2 = p.coordinates();
let coords3 = p.coordinates();
assert_eq!(coords1, coords2);
assert_eq!(coords2, coords3);
assert_eq!(coords1, (1.5, 2.5, 3.5));
}
#[test]
fn point_automatic_cleanup_on_drop() {
let coords = { let coords = {
let p = Point::new(7.0, 8.0, 9.0).expect("Creation failed"); let p = Point::new(7.0, 8.0, 9.0).expect("Failed to create point");
p.coordinates() p.coordinates()
}; };
assert_eq!(coords, (7.0, 8.0, 9.0)); assert_eq!(coords, (7.0, 8.0, 9.0));
// Verify we can still create points after automatic cleanup
let p2 = Point::new(10.0, 11.0, 12.0).expect("Failed to create point after drop");
assert_eq!(p2.coordinates(), (10.0, 11.0, 12.0));
} }
#[test] #[test]
fn multiple_instances() { fn point_multiple_instances_independent() {
let p1 = Point::new(1.0, 1.0, 1.0).expect("Creation failed"); let p1 = Point::new(1.0, 1.0, 1.0).expect("Failed to create point 1");
let p2 = Point::new(2.0, 2.0, 2.0).expect("Creation failed"); let p2 = Point::new(2.0, 2.0, 2.0).expect("Failed to create point 2");
let p3 = Point::new(3.0, 3.0, 3.0).expect("Failed to create point 3");
assert_eq!(p1.coordinates(), (1.0, 1.0, 1.0)); assert_eq!(p1.coordinates(), (1.0, 1.0, 1.0));
assert_eq!(p2.coordinates(), (2.0, 2.0, 2.0)); assert_eq!(p2.coordinates(), (2.0, 2.0, 2.0));
assert_eq!(p3.coordinates(), (3.0, 3.0, 3.0));
p2.delete().expect("Failed to delete p2");
p1.delete().expect("Failed to delete p1");
p3.delete().expect("Failed to delete p3");
} }
#[test] #[test]
fn debug_format() { fn point_debug_format() {
let p = Point::new(1.1, 2.2, 3.3).expect("Creation failed"); let p = Point::new(1.1, 2.2, 3.3).expect("Failed to create point");
let debug_output = format!("{:?}", p); let debug_output = format!("{:?}", p);
assert!(debug_output.starts_with("Point(")); assert!(debug_output.contains("Point"));
assert!(debug_output.contains("1.1")); assert!(debug_output.contains("1.1"));
assert!(debug_output.contains("2.2")); assert!(debug_output.contains("2.2"));
assert!(debug_output.contains("3.3")); assert!(debug_output.contains("3.3"));
} }
#[test] #[test]
fn boundary_values() { fn point_rapid_creation_deletion_cycle_stress_test() {
let cases = [(0.0, 0.0, 0.0), (f64::MAX, f64::MIN, 0.0)]; for i in 0..100 {
let p = Point::new(i as f64, i as f64, i as f64)
for (x, y, z) in cases { .expect(&format!("Failed to create point in iteration {}", i));
let p = Point::new(x, y, z).expect("Creation with boundary values failed"); assert_eq!(p.coordinates(), (i as f64, i as f64, i as f64));
assert_eq!(p.coordinates(), (x, y, z)); p.delete()
.expect(&format!("Failed to delete point in iteration {}", i));
} }
} }
} }