Skip to content

Commit 2e5fd3d

Browse files
authored
Merge pull request #31 from diffpy/update-objcryst-upstream-sync
Sync objcryst upstream and add standalone unit-test workflow
2 parents ed4fc9c + 3935a96 commit 2e5fd3d

9 files changed

Lines changed: 278 additions & 2 deletions

File tree

.github/workflows/unit-tests.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Unit tests
2+
3+
on:
4+
pull_request:
5+
workflow_dispatch:
6+
7+
concurrency:
8+
group: unit-tests-${{ github.ref }}
9+
cancel-in-progress: true
10+
11+
jobs:
12+
unit-tests:
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
include:
17+
- label: ubuntu-x64
18+
os: ubuntu-latest
19+
arch: x64
20+
make_cmd: make
21+
- label: macos-arm64
22+
os: macos-14
23+
arch: arm64
24+
make_cmd: gmake
25+
runs-on: ${{ matrix.os }}
26+
timeout-minutes: 45
27+
name: ${{ matrix.label }}
28+
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
with:
33+
fetch-depth: 0
34+
submodules: recursive
35+
36+
- name: Set up Miniconda
37+
uses: conda-incubator/setup-miniconda@v3
38+
with:
39+
activate-environment: test-env
40+
auto-activate-base: false
41+
python-version: "3.13"
42+
43+
- name: Install build dependencies (Ubuntu)
44+
if: startsWith(matrix.os, 'ubuntu-')
45+
shell: bash -el {0}
46+
run: |
47+
conda install -y -c conda-forge scons compilers boost
48+
49+
- name: Install build dependencies (macOS)
50+
if: startsWith(matrix.os, 'macos-')
51+
shell: bash -el {0}
52+
run: |
53+
brew install make
54+
conda install -y -c conda-forge scons compilers boost
55+
56+
- name: Build libobjcryst
57+
shell: bash -el {0}
58+
run: |
59+
sed -i.bak '/if gfb != afb:/,+1d' site_scons/libobjcrystbuildutils.py
60+
PREFIX="$CONDA_PREFIX" python -m SCons -Q lib
61+
62+
- name: Locate built library
63+
shell: bash -el {0}
64+
run: |
65+
libpath="$(find "$PWD/build" -type f \( -name 'libObjCryst.so' -o -name 'libObjCryst.dylib' \) | head -n 1)"
66+
if [ -z "$libpath" ]; then
67+
echo "No built libObjCryst library found under build/"
68+
exit 1
69+
fi
70+
echo "LIBOBJCRYST_PATH=$libpath" >> "$GITHUB_ENV"
71+
echo "Using LIBOBJCRYST_PATH=$libpath"
72+
73+
- name: Run standalone unit tests
74+
shell: bash -el {0}
75+
run: |
76+
CONDA_PREFIX="$CONDA_PREFIX" ${{ matrix.make_cmd }} -C test LIBOBJCRYST="$LIBOBJCRYST_PATH"

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,7 @@ tags
3131

3232
# source distribution tarball
3333
libobjcryst-*.tar.gz
34+
35+
# standalone unit-test artifacts
36+
test/bin/
37+
test/obj/

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Release notes
22

3+
## Version 2026.2.0, - 2026-08-07
4+
5+
### Changed
6+
- sync objcryst submodule to upstream `vincefn/objcryst` commit `4091cd9`, including:
7+
- CIF parser robustness fix for truncated input
8+
- fix for single-crystal simulation crashes
9+
- refinement guards for missing data/phase and empty MonteCarloObj
10+
- libobjcryst standalone workflow to build and run the upstream non-GUI unit-test suite
11+
312
## Version 2026.1, - 2026-02-05
413

514
### Changed

site_scons/fallback_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
Update FALLBACK_VERSION when tagging a new release.
88
'''
99

10-
FALLBACK_VERSION = '2026.1.post0'
10+
FALLBACK_VERSION = '2026.2.0.post0'

src/objcryst

Submodule objcryst updated 61 files

test/Makefile

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
ROOT_DIR := $(abspath $(CURDIR)/..)
2+
OBJCRYST_TEST_DIR := $(ROOT_DIR)/src/objcryst/test
3+
UNIT_SRC_DIR := $(OBJCRYST_TEST_DIR)/unit
4+
BUILD ?= fast
5+
ARCH ?= $(shell uname -m)
6+
BUILD_DIR ?= $(ROOT_DIR)/build/$(BUILD)-$(ARCH)
7+
LIBOBJCRYST ?= $(firstword $(wildcard $(BUILD_DIR)/src/libObjCryst*.so) \
8+
$(wildcard $(BUILD_DIR)/src/libObjCryst*.dylib) \
9+
$(wildcard $(BUILD_DIR)/src/libObjCryst*.dll) \
10+
$(wildcard $(BUILD_DIR)/src/libObjCryst*.a) \
11+
$(wildcard $(BUILD_DIR)/libObjCryst*.so) \
12+
$(wildcard $(BUILD_DIR)/libObjCryst*.dylib) \
13+
$(wildcard $(BUILD_DIR)/libObjCryst*.dll) \
14+
$(wildcard $(BUILD_DIR)/libObjCryst*.a))
15+
BIN_DIR := $(CURDIR)/bin/$(BUILD)-$(ARCH)
16+
OBJ_DIR := $(CURDIR)/obj/$(BUILD)-$(ARCH)
17+
RUNNER := $(CURDIR)/run_unit_tests.sh
18+
TEST_RUNNER := $(CURDIR)/test_runner.sh
19+
20+
RM ?= rm -f
21+
MKDIR_P ?= mkdir -p
22+
23+
CXX ?= c++
24+
CXXFLAGS ?= -O2 -std=c++14
25+
CPPFLAGS += -DREAL=double \
26+
-I$(ROOT_DIR)/src \
27+
-I$(ROOT_DIR)/src/objcryst \
28+
-I$(ROOT_DIR)/src/objcryst/test/unit \
29+
-I$(BUILD_DIR)/src \
30+
-I$(BUILD_DIR)/src/objcryst \
31+
-I$(BUILD_DIR)/src/version \
32+
-I$(ROOT_DIR)/src/objcryst/ObjCryst \
33+
-I$(ROOT_DIR)/src/objcryst/cctbx/include
34+
LDFLAGS += -L$(abspath $(dir $(LIBOBJCRYST)))
35+
LDLIBS += $(abspath $(LIBOBJCRYST))
36+
ifeq ($(origin CONDA_PREFIX), undefined)
37+
CONDA_PREFIX :=
38+
endif
39+
40+
ifeq ($(shell uname -s),Darwin)
41+
RPATH_FLAG = -Wl,-rpath,$(dir $(LIBOBJCRYST))
42+
ifneq ($(strip $(CONDA_PREFIX)),)
43+
RPATH_FLAG += -Wl,-rpath,$(CONDA_PREFIX)/lib
44+
endif
45+
else
46+
RPATH_FLAG = -Wl,-rpath,$(dir $(LIBOBJCRYST))
47+
endif
48+
49+
UNIT_TESTS := unit_cell_smoke crystallography_workflow \
50+
api_spacegroup api_crystal api_molecule api_scattering \
51+
api_powderpattern api_cif api_optimization api_indexing \
52+
ground_truth
53+
54+
UNIT_BINS := $(addprefix $(BIN_DIR)/,$(UNIT_TESTS))
55+
UNIT_OBJS := $(addprefix $(OBJ_DIR)/,$(addsuffix .o,$(UNIT_TESTS)))
56+
57+
.SECONDARY: $(UNIT_OBJS)
58+
59+
.PHONY: all build run clean tidy check-layout
60+
61+
all: build run
62+
63+
build: check-layout $(UNIT_BINS) $(RUNNER)
64+
65+
run: build $(RUNNER)
66+
@cd $(UNIT_SRC_DIR) && TEST_TIMEOUT="$${FOX_TEST_TIMEOUT:-30s}" BUILD="$(BUILD)" "$(RUNNER)"
67+
68+
check-layout:
69+
@test -d "$(UNIT_SRC_DIR)" || { echo "Missing upstream unit test sources at $(UNIT_SRC_DIR)"; exit 1; }
70+
@test -f "$(TEST_RUNNER)" || { echo "Missing shared test runner at $(TEST_RUNNER)"; exit 1; }
71+
@test -n "$(LIBOBJCRYST)" || { echo "No built libObjCryst found under $(BUILD_DIR). Build libobjcryst first."; exit 1; }
72+
@test -f "$(ROOT_DIR)/src/objcryst/test/data/cif/PbSO4-COD-1528837.cif" || { echo "Missing required test data in src/objcryst/test/data"; exit 1; }
73+
74+
$(BIN_DIR) $(OBJ_DIR):
75+
@$(MKDIR_P) "$@"
76+
77+
$(OBJ_DIR)/%.o: $(UNIT_SRC_DIR)/%.cpp | $(OBJ_DIR)
78+
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
79+
80+
$(BIN_DIR)/%: $(OBJ_DIR)/%.o | $(BIN_DIR)
81+
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(RPATH_FLAG) $< -o $@ $(LDLIBS)
82+
83+
$(RUNNER):
84+
@chmod +x "$(RUNNER)"
85+
86+
tidy:
87+
@$(RM) $(UNIT_OBJS)
88+
89+
clean: tidy
90+
@$(RM) $(UNIT_BINS) "$(RUNNER)"

test/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
This directory provides an independent unit-test harness for running the
2+
upstream ObjCryst non-GUI unit tests against the libobjcryst library built in
3+
this repository.
4+
5+
Usage:
6+
7+
1. Build libobjcryst in-tree with SCons, for example:
8+
`PREFIX=/path/to/env python -m SCons -Q lib`
9+
2. Build and run the unit tests:
10+
`CONDA_PREFIX=/path/to/env make -C test`
11+
12+
The makefile expects the library under `build/<build>-<arch>/src/` and uses
13+
`build=fast` by default. Override `BUILD_DIR`, `BUILD`, `ARCH`, or `LIBOBJCRYST`
14+
when using a different in-tree layout.
15+
16+
On macOS with a conda toolchain, pass `CONDA_PREFIX` so the test binaries add
17+
the environment runtime library directory to their rpath.

test/run_unit_tests.sh

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
4+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5+
BIN_SUBDIR="${BUILD:-fast}-$(uname -m)"
6+
7+
# shellcheck source=/dev/null
8+
. "$SCRIPT_DIR/test_runner.sh"
9+
10+
TEST_TIMEOUT="${FOX_TEST_TIMEOUT:-30s}"
11+
12+
run_test "unit::unit_cell_smoke" "$SCRIPT_DIR/bin/$BIN_SUBDIR/unit_cell_smoke"
13+
run_test "unit::crystallography_workflow" "$SCRIPT_DIR/bin/$BIN_SUBDIR/crystallography_workflow"
14+
15+
run_test "unit::spacegroup" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_spacegroup" "spacegroup"
16+
run_test "unit::spacegroup-alternate-settings" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_spacegroup" "spacegroup-alternate-settings"
17+
run_test "unit::spacegroup-reflection-properties" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_spacegroup" "spacegroup-reflection-properties"
18+
run_test "unit::spacegroup-symmetry-operations" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_spacegroup" "spacegroup-symmetry-operations"
19+
run_test "unit::spacegroup-asymmetric-unit" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_spacegroup" "spacegroup-asymmetric-unit"
20+
21+
run_test "unit::scattering-power-atom" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_crystal" "scattering-power-atom"
22+
run_test "unit::crystal-atom" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_crystal" "crystal-atom"
23+
run_test "unit::crystal-scatterer-management" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_crystal" "crystal-scatterer-management"
24+
run_test "unit::unitcell-geometry" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_crystal" "unitcell-geometry"
25+
26+
run_test "unit::molecule-atoms-bonds" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_molecule" "molecule-atoms-bonds"
27+
run_test "unit::molecule-angles-dihedrals" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_molecule" "molecule-angles-dihedrals"
28+
run_test "unit::molecule-formula-loglikelihood" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_molecule" "molecule-formula-loglikelihood"
29+
30+
run_test "unit::scatteringdata-singlecrystal" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_scattering" "scatteringdata-singlecrystal"
31+
run_test "unit::scatteringdata-radiation-types" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_scattering" "scatteringdata-radiation-types"
32+
run_test "unit::diffractiondata-observed" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_scattering" "diffractiondata-observed"
33+
run_test "unit::singlecrystal-groundtruth-xray" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_scattering" "singlecrystal-groundtruth-xray"
34+
run_test "unit::singlecrystal-groundtruth-neutron" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_scattering" "singlecrystal-groundtruth-neutron"
35+
run_test "unit::singlecrystal-simulate-ungrouped" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_scattering" "singlecrystal-simulate-ungrouped"
36+
run_test "unit::singlecrystal-simulate-grouped-equal" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_scattering" "singlecrystal-simulate-grouped-equal"
37+
run_test "unit::singlecrystal-simulate-grouped-user" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_scattering" "singlecrystal-simulate-grouped-user"
38+
39+
run_test "unit::cif-import" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_cif" "cif-import"
40+
run_test "unit::cif-data-fields" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_cif" "cif-data-fields"
41+
run_test "unit::cif-coordinate-conversion" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_cif" "cif-coordinate-conversion"
42+
run_test "unit::cif-truncated-values" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_cif" "cif-truncated-values"
43+
44+
run_test "unit::refinablepar" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_optimization" "refinablepar"
45+
run_test "unit::refinableobj" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_optimization" "refinableobj"
46+
run_test "unit::optimizationobj" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_optimization" "optimizationobj"
47+
run_test "unit::optimizationobj-limits-options" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_optimization" "optimizationobj-limits-options"
48+
run_test "unit::lsqnumobj" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_optimization" "lsqnumobj"
49+
run_test "unit::lsqnumobj-residual-statistics" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_optimization" "lsqnumobj-residual-statistics"
50+
51+
run_test "unit::peaklist-simulate-volume" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_indexing" "peaklist-simulate-volume"
52+
run_test "unit::peaklist-add-remove" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_indexing" "peaklist-add-remove"
53+
run_test "unit::cellexplorer" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_indexing" "cellexplorer"
54+
run_test "unit::cellexplorer-configuration" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_indexing" "cellexplorer-configuration"
55+
run_test "unit::cellexplorer-dicvol-tetragonal" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_indexing" "cellexplorer-dicvol-tetragonal"
56+
run_test "unit::cellexplorer-dicvol-monoclinic" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_indexing" "cellexplorer-dicvol-monoclinic"
57+
58+
run_test "unit::powderpattern-background" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "powderpattern-background"
59+
run_test "unit::powderpattern-diffraction" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "powderpattern-diffraction"
60+
run_test "unit::powderpattern-diffraction-mur" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "powderpattern-diffraction-mur"
61+
run_test "unit::powderpattern-diffraction-lebail-fhklobs" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "powderpattern-diffraction-lebail-fhklobs"
62+
run_test "unit::powderpattern-import" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "powderpattern-import"
63+
run_test "unit::scatteringcorr-subclasses" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "scatteringcorr-subclasses"
64+
run_test "unit::reflectionprofile-pseudo-voigt" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "reflectionprofile-pseudo-voigt"
65+
run_test "unit::reflectionprofile-double-exponential-pv" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "reflectionprofile-double-exponential-pv"
66+
67+
run_test "unit::powder-groundtruth-xray-pv-gaussian" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "powder-groundtruth-xray-pv-gaussian"
68+
run_test "unit::powder-groundtruth-xray-pv-lorentzian" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "powder-groundtruth-xray-pv-lorentzian"
69+
run_test "unit::reflectionprofile-pv-anisotropic-direct" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "reflectionprofile-pv-anisotropic-direct"
70+
run_test "unit::powder-groundtruth-xray-anisotropic" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "powder-groundtruth-xray-anisotropic"
71+
run_test "unit::powder-groundtruth-neutron-pv-gaussian" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "powder-groundtruth-neutron-pv-gaussian"
72+
run_test "unit::powder-groundtruth-neutron-pv-lorentzian" "$SCRIPT_DIR/bin/$BIN_SUBDIR/api_powderpattern" "powder-groundtruth-neutron-pv-lorentzian"

test/test_runner.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
4+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5+
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
6+
7+
# shellcheck source=/dev/null
8+
. "$ROOT_DIR/src/objcryst/test/scripts/test_runner.sh"

0 commit comments

Comments
 (0)