Skip to content

Commit 52c02c8

Browse files
authored
Merge pull request #14 from Image-Analysis-Hub/citests
Add automatic tests when the version is changed Fix a bug on windows loading .pkl file Tried to fix missing setuptools on one setup (not reproduced) test do python 3.10 and 3.12, ubuntu, mac and windows
2 parents 94a914c + a123cda commit 52c02c8

16 files changed

Lines changed: 258 additions & 65 deletions
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Cross-Platform Tests
2+
3+
## activate tests when change the version, or a test
4+
on:
5+
push:
6+
#branches: [main]
7+
paths:
8+
- 'pyproject.toml'
9+
- 'tox.ini'
10+
- 'requirements*.txt'
11+
- '.github/workflows/cross-platform.yml'
12+
13+
jobs:
14+
test:
15+
runs-on: ${{ matrix.os }}
16+
strategy:
17+
matrix:
18+
os: [ubuntu-latest, windows-latest, macos-latest]
19+
#os: [ubuntu-latest, macos-latest]
20+
#os: [debian-latest]
21+
python-version: ["3.10", "3.12"]
22+
#python-version: ["3.12"]
23+
24+
steps:
25+
- uses: actions/checkout@v4
26+
27+
- name: Set up Python ${{ matrix.python-version }}
28+
uses: actions/setup-python@v5
29+
with:
30+
python-version: ${{ matrix.python-version }}
31+
32+
# these libraries enable testing on Qt on linux
33+
- uses: tlambert03/setup-qt-libs@v1
34+
35+
# strategy borrowed from vispy for installing opengl libs on windows
36+
- name: Install Windows OpenGL
37+
if: runner.os == 'Windows'
38+
run: |
39+
git clone --depth 1 https://github.com/pyvista/gl-ci-helpers.git
40+
powershell gl-ci-helpers/appveyor/install_opengl.ps1
41+
42+
- name: Install dependencies
43+
run: |
44+
python -m pip install --upgrade pip
45+
python -m pip install setuptools tox tox-gh-actions
46+
47+
# this runs the platform-specific tests declared in tox.ini
48+
- name: Test with tox
49+
uses: GabrielBB/xvfb-action@v1
50+
with:
51+
run: python -m tox
52+
env:
53+
PLATFORM: ${{ matrix.platform }}
54+

pyproject.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ pyqt5 = [
5353
"PyQtWebEngine",
5454
]
5555

56+
testing = [
57+
"tox",
58+
"pytest",
59+
"pytest-cov",
60+
"pytest-qt",
61+
"napari",
62+
"pyqt5",
63+
]
64+
5665
[project.urls]
5766
"Bug Tracker" = "https://github.com/Image-Analysis-Hub/Epicure/issues"
5867
"Documentation" = "https://image-analysis-hub.github.io/Epicure/"

src/epicure/Utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,7 @@ def get_border_cells( img ):
10941094
labels += list( np.unique( img[ :, (height-2): ] ) ) ## bottom border
10951095
labels += list( np.unique( img[ 0:2,] ) ) ## left border
10961096
labels += list( np.unique( img[ (width-2):,] ) ) ## right border
1097+
labels = list( np.unique(labels) )
10971098
return labels
10981099

10991100
def count_neighbors( label_img, label ):
@@ -1175,6 +1176,13 @@ def shortcut_click_match( shortcut, event ):
11751176
return False
11761177
return True
11771178

1179+
def is_windows():
1180+
""" Is running on windows or not """
1181+
try:
1182+
return platform.lower().startswith("win")
1183+
except:
1184+
return False
1185+
11781186
def is_darwin():
11791187
""" Test if OS is MacOS or not """
11801188
try:

src/epicure/_version.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@
1717
__version_tuple__: VERSION_TUPLE
1818
version_tuple: VERSION_TUPLE
1919

20-
__version__ = version = '0.1.dev193+g631d088.d20260324'
21-
__version_tuple__ = version_tuple = (0, 1, 'dev193', 'g631d088.d20260324')
20+
__version__ = version = '1.5.4.dev47+g2d83614.d20260513'
21+
__version_tuple__ = version_tuple = (1, 5, 4, 'dev47', 'g2d83614.d20260513')

src/epicure/epicuring.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,10 @@ def load_epicure_data(self, epiname):
804804
"""Load saved infos from file"""
805805
infile = open(epiname, "rb")
806806
try:
807+
if ut.is_windows():
808+
import pathlib
809+
pathlib.PosixPath = pathlib.WindowsPath
810+
#epidata = pickle.load( infile, encoding="utf8" )
807811
epidata = pickle.load( infile )
808812
#print(epidata)
809813
if "EpiMetaData" in epidata.keys():
@@ -815,9 +819,10 @@ def load_epicure_data(self, epiname):
815819
self.load_epicure_data_old(epidata, infile)
816820
except Exception as e:
817821
if self.verbose > 1:
818-
print(f"Line 619 - {type(e)} {e} - Could not read EpiCure data file {epiname}")
822+
print(f" {type(e)} {e} - Could not read EpiCure data file {epiname}")
819823
else:
820824
ut.show_warning(f"Could not read EpiCure data file {epiname}")
825+
print(f" {type(e)} {e} - Could not read EpiCure data file {epiname}")
821826

822827
def read_epidata(self, epidata):
823828
"""Read the dict of saved state and initialize all instances with it"""

src/epicure/inspecting.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,11 @@ def add_event_type(self, ind, sid, feature):
537537
if self.event_types.get(feature) is None:
538538
self.event_types[feature] = []
539539
self.event_types[feature].append(sid)
540-
self.events.properties["score"][ind] = self.events.properties["score"][ind] + 1
540+
score = self.events.properties["score"].copy()
541+
score[ind] = score[ind] + 1
542+
self.events.properties["score"] = score
543+
self.events.properties["score"].flags.writeable = True
544+
#self.events.properties()
541545

542546
def first_event(self, pos, label, featurename):
543547
""" Addition of the first event (initialize all) """
@@ -549,6 +553,10 @@ def first_event(self, pos, label, featurename):
549553
features["score"] = np.array([0], dtype="uint8")
550554
pts = [pos]
551555
self.events = self.viewer.add_points( np.array(pts), properties=features, face_color="score", size = int( self.event_size.value() ), symbol="x", name="Events", scale=self.viewer.layers["Segmentation"].scale )
556+
props = self.events.properties
557+
props["label"].flags.writeable = True
558+
props["score"].flags.writeable = True
559+
props["id"].flags.writeable = True
552560
self.add_event_type(0, sid, featurename)
553561
self.events.refresh()
554562
self.update_nevents_display()
@@ -586,9 +594,13 @@ def add_event(self, pos, label, reason, symb="x", color="white", force=False, re
586594
ind = len(self.events.data)
587595
sid = self.new_event_id()
588596
self.events.add(pos)
589-
self.events.properties["label"][ind] = label
590-
self.events.properties["id"][ind] = sid
591-
self.events.properties["score"][ind] = 0
597+
props = self.events.properties
598+
props["label"].flags.writeable = True
599+
props["score"].flags.writeable = True
600+
props["id"].flags.writeable = True
601+
props["label"][ind] = label
602+
props["id"][ind] = sid
603+
props["score"][ind] = 0
592604
self.add_event_type(ind, sid, reason)
593605

594606
self.events.symbol.flags.writeable = True
@@ -772,7 +784,9 @@ def remove_event_types(self, sid):
772784

773785
def decrease_score(self, ind):
774786
""" Decrease by one score of event at index ind. Delete it if reach 0"""
775-
self.events.properties["score"][ind] = self.events.properties["score"][ind] - 1
787+
score = self.events.properties["score"]
788+
score.flags.writeable = True
789+
score[ind] = score[ind] - 1
776790
if self.events.properties["score"][ind] == 0:
777791
self.exonerate_one( ind, remove_division=False )
778792
self.update_nevents_display()

src/epicure/resources/pixi.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ platforms = ["osx-arm64", "osx-64", "linux-64", "win-64"]
1010
python = "==3.10"
1111
numpy = "<2"
1212
pip = "*"
13+
setuptools = ">=65.0"
1314

1415
[pypi-dependencies]
1516
napari-epyseg = "==0.0.14"

src/tests/test_basics.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,34 @@
66
from epicure.start_epicuring import gui_files
77

88

9-
def test_load_movie():
9+
def test_load_movie( make_napari_viewer ):
1010
""" Read a standard tif movie """
1111
test_img = os.path.join(".", "test_data", "003_crop.tif")
12-
epic = epi.EpiCure()
12+
viewer = make_napari_viewer()
13+
epic = epi.EpiCure(viewer)
1314
epic.load_movie(test_img)
1415
assert epic.img.shape == (11,189,212)
1516
return
1617

17-
def test_load_image():
18+
def test_load_image( make_napari_viewer ):
1819
""" Read a single image and a cellpose (labels) segmentation """
1920
test_img = os.path.join(".", "test_data", "static_image.tif")
2021
test_seg = os.path.join(".", "test_data", "static_image_cellpose.tif")
21-
epic = epi.EpiCure()
22+
viewer = make_napari_viewer()
23+
epic = epi.EpiCure(viewer)
2224
epic.load_movie(test_img)
2325
assert epic.img.shape == (1,507,585)
2426
epic.load_segmentation(test_seg)
2527
assert epic.seg.shape == epic.img.shape
2628
assert np.max(epic.seg) == 706
2729
return
2830

29-
def test_load_movie_with_chanel():
31+
def test_load_movie_with_chanel( make_napari_viewer ):
3032
""" Read a tif movie with 2 channels """
3133
test_img = os.path.join(".", "test_data", "area3_Composite.tif")
3234
test_seg = os.path.join(".", "test_data", "area3_Composite_epyseg.tif")
33-
epic = epi.EpiCure()
35+
viewer = make_napari_viewer()
36+
epic = epi.EpiCure( viewer )
3437
resaxis, resval = epic.load_movie(test_img)
3538
# check the dimensions are correctly loaded
3639
assert epic.img.shape == (5,2,146,228)
@@ -43,19 +46,21 @@ def test_load_movie_with_chanel():
4346
assert np.mean(epic.img)>100
4447
return
4548

46-
def test_load_segmentation():
49+
def test_load_segmentation( make_napari_viewer ):
4750
test_seg = os.path.join(".", "test_data", "003_crop_epyseg.tif")
48-
epic = epi.EpiCure()
51+
viewer = make_napari_viewer()
52+
epic = epi.EpiCure( viewer )
4953
epic.load_segmentation(test_seg)
5054
assert epic.seg.shape == (11,189,212)
5155
assert np.max(epic.seg) == 1294
5256
return
5357

54-
def test_suggest():
58+
def test_suggest( make_napari_viewer ):
5559
""" Check segmentation file name suggestion """
5660
## case 1: file doesn't exists, creates it
5761
test_img = os.path.join(".", "test_data", "003_crop.tif")
58-
epic = epi.EpiCure()
62+
viewer = make_napari_viewer()
63+
epic = epi.EpiCure(viewer)
5964
epic.load_movie(test_img)
6065
segfile = epic.suggest_segfile("epics")
6166
assert segfile is None
@@ -68,14 +73,15 @@ def test_suggest():
6873
assert segfile == absp
6974
return
7075

71-
def test_init_epic():
72-
epic = epi.EpiCure()
76+
def test_init_epic( make_napari_viewer ):
77+
viewer = make_napari_viewer()
78+
epic = epi.EpiCure(viewer)
7379
assert epic.img is None
7480
return
7581

76-
def test_load_from_layers():
82+
def test_load_from_layers( make_napari_viewer ):
7783
""" Open a new EpiCure project from opened layers """
78-
viewer = napari.Viewer(show=False)
84+
viewer = make_napari_viewer()
7985
test_img = os.path.join(".", "test_data", "003_crop.tif")
8086
img, _, _, _, _, _ = ut.open_image(test_img, get_metadata=False, verbose=0)
8187
movie_layer = viewer.add_image(img, name="TestImage")
@@ -104,6 +110,7 @@ def test_load_from_layers():
104110
#viewer.show() # manual check
105111

106112
if __name__ == "__main__":
113+
test_load_movie()
107114
test_load_from_layers()
108115
test_load_image()
109116
print("********* Test basics completed ***********")

src/tests/test_editings.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
import napari
77
from vispy import keys
88

9-
def test_epicuring_bindings():
9+
def test_epicuring_bindings( make_napari_viewer ):
1010
""" Test shortcuts for displaying layers """
1111
test_img = os.path.join(".", "test_data", "003_crop.tif")
1212
test_seg = os.path.join(".", "test_data", "003_crop_epyseg.tif")
13-
viewer = napari.Viewer(show=False)
13+
viewer = make_napari_viewer()
1414
epic = epi.EpiCure(viewer)
1515
epic.load_movie(test_img)
1616
epic.go_epicure("test_epics", test_seg)
@@ -31,10 +31,10 @@ def test_epicuring_bindings():
3131
view.canvas.events.key_press(key=keys.Key("g"), modifiers=[])
3232
assert "EpicGrid" in epic.viewer.layers
3333

34-
def test_merge():
34+
def test_merge( make_napari_viewer ):
3535
test_img = os.path.join(".", "test_data", "003_crop.tif")
3636
test_seg = os.path.join(".", "test_data", "003_crop_epyseg.tif")
37-
viewer = napari.Viewer(show=False)
37+
viewer = make_napari_viewer()
3838
epic = epi.EpiCure(viewer)
3939
epic.load_movie(test_img)
4040
epic.load_segmentation(test_seg)
@@ -55,10 +55,10 @@ def test_merge():
5555
assert val == 1296
5656

5757

58-
def test_group():
58+
def test_group( make_napari_viewer ):
5959
test_img = os.path.join(".", "test_data", "003_crop.tif")
6060
test_seg = os.path.join(".", "test_data", "003_crop_epyseg.tif")
61-
viewer = napari.Viewer(show=False)
61+
viewer = make_napari_viewer()
6262
epic = epi.EpiCure(viewer)
6363
epic.load_movie(test_img)
6464
epic.go_epicure("test_epics", test_seg)

src/tests/test_operations.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
import epicure.Utils as ut
55
import napari
66

7-
def test_get_free_label():
7+
def test_get_free_label( make_napari_viewer ):
88
## test from a skeletonized movie
99
test_mov = os.path.join(".", "test_data", "003_crop.tif")
1010
test_seg = os.path.join(".", "test_data", "003_crop_epyseg.tif")
11-
epic = epi.EpiCure()
11+
viewer = make_napari_viewer()
12+
epic = epi.EpiCure(viewer)
1213
epic.load_movie(test_mov)
1314
#epic.viewer = napari.Viewer(show=False)
1415
epic.go_epicure("epics", test_seg)
@@ -29,11 +30,11 @@ def test_get_free_label():
2930
# epic.inspecting.first_event((0,10,20), 10, "suspect")
3031
# assert "Events" in epic.viewer.layers
3132

32-
def utils_value():
33+
def utils_value(make_napari_viewer):
3334
""" Labels operations in Utils """
3435
test_mov = os.path.join(".", "test_data", "003_crop.tif")
3536
test_seg = os.path.join(".", "test_data", "003_crop_epyseg.tif")
36-
viewer = napari.Viewer(show=False)
37+
viewer = make_napari_viewer()
3738
epic = epi.EpiCure(viewer)
3839
epic.load_movie(test_mov)
3940
epic.go_epicure("epics", test_seg)
@@ -56,4 +57,4 @@ def utils_value():
5657
if __name__ == "__main__":
5758
test_get_free_label()
5859
utils_value()
59-
print("********* Test operations cure completed ***********")
60+
print("********* Test operations cure completed ***********")

0 commit comments

Comments
 (0)