Skip to content

Commit 9d54be4

Browse files
authored
Merge pull request #68 from sourceryinstitute/fixes-for-favpro
Improve testing of: - Block partitioning - Bin type - Command Line processing
2 parents de6a957 + c38c53e commit 9d54be4

File tree

5 files changed

+179
-24
lines changed

5 files changed

+179
-24
lines changed

.github/workflows/CI.yml

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,51 @@ jobs:
99
env:
1010
FC: gfortran
1111
GCC_V: 13
12+
OC_V: 2.10.2
1213

1314
steps:
1415
- name: Checkout code
15-
uses: actions/checkout@v3
16+
uses: actions/checkout@v4
1617

1718
- name: Install fpm
18-
uses: fortran-lang/setup-fpm@v4
19+
uses: fortran-lang/setup-fpm@v5
1920
with:
2021
github-token: ${{ secrets.GITHUB_TOKEN }}
2122

22-
- name: Get Time
23-
id: time
24-
uses: nanzm/[email protected]
25-
with:
26-
format: 'YYYY-MM'
27-
2823
- name: Setup cache for opencoarrays
2924
id: cache-opencoarrays
30-
uses: actions/cache@v3
25+
uses: actions/cache@v4
3126
with:
32-
path: "OpenCoarrays-2.10.1/"
33-
key: ${{ steps.time.outputs.time }}
27+
path: "~/apps/OpenCoarrays/"
28+
key: OC-${{ env.OC_V }}
3429

35-
- name: Install GFortran, OpenCoarrays
30+
- name: Install GFortran
3631
run: |
3732
sudo apt update
38-
sudo apt install -y build-essential gfortran-${GCC_V} g++-${GCC_V} pkg-config make
33+
sudo apt install -y build-essential gfortran-${GCC_V} g++-${GCC_V} cmake mpich
3934
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_V} 100 \
4035
--slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${GCC_V} \
4136
--slave /usr/bin/g++ g++ /usr/bin/g++-${GCC_V}
42-
if [ ! -d OpenCoarrays-2.10.1 ] ; then wget -P . https://github.com/sourceryinstitute/OpenCoarrays/releases/download/2.10.1/OpenCoarrays-2.10.1.tar.gz && tar -xf OpenCoarrays-2.10.1.tar.gz && cd OpenCoarrays-2.10.1 && TERM=xterm ./install.sh -y; fi
37+
38+
- name: Maybe install opencoarrays
39+
if: steps.cache-opencoarrays.outputs.cache-hit != 'true'
40+
run: |
41+
wget https://github.com/sourceryinstitute/OpenCoarrays/releases/download/${OC_V}/OpenCoarrays-${OC_V}.tar.gz
42+
tar xzvf OpenCoarrays-${OC_V}.tar.gz
43+
cd OpenCoarrays-${OC_V}
44+
mkdir -p "~/apps/OpenCoarrays/${OC_V}"
45+
cmake -S . -B build -DCMAKE_INSTALL_PREFIX="~/apps/OpenCoarrays/${OC_V}"
46+
cmake --build build -t install -j
47+
echo "${HOME}/apps/OpenCoarrays/${OC_V}/bin" >> $GITHUB_PATH
48+
49+
- name: Add to PATH if cache hit
50+
if: steps.cache-opencoarrays.outputs.cache-hit == 'true'
51+
run: echo "${HOME}/apps/OpenCoarrays/${OC_V}/bin" >> $GITHUB_PATH
4352

4453
- name: Build, run, and test
4554
run: |
46-
source OpenCoarrays-2.10.1/prerequisites/installations/opencoarrays/2.10.1/setup.sh
47-
fpm test --compiler caf --runner "cafrun -n 2"
55+
which caf
56+
caf --show
57+
# Build the example executable to test the commandline without caf first, since you cannot spawn new MPI jobs from within an MPI job
58+
fpm run --example get-flag-value -- --input-file some_file_name
59+
fpm test --compiler caf --runner "cafrun -n 2"

test/bin_test.f90

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,15 @@ function results() result(test_results)
2929
descriptions => &
3030
[ character(len=len(longest_description)) :: &
3131
"partitioning items nearly evenly across bins", &
32-
"partitioning all item across all bins without item loss" &
32+
"partitioning all item across all bins without item loss", &
33+
"partitioning items such that each bin only has one works", &
34+
"partitioning items such that some bins only have one works" &
3335
], &
3436
outcomes => &
3537
[ verify_block_partitioning(), &
36-
verify_all_items_partitioned() &
38+
verify_all_items_partitioned(), &
39+
verify_all_singleton_bins_are_ok(), &
40+
verify_some_singleton_bins_are_ok() &
3741
] &
3842
)
3943
call assert(size(descriptions) == size(outcomes), "bin_test_m(results): size(descriptions) == size(outcomes)")
@@ -73,4 +77,39 @@ function verify_all_items_partitioned() result(test_passes)
7377

7478
end function
7579

80+
function verify_all_singleton_bins_are_ok() result(test_passes)
81+
!! Verify that all singleton bins can be created and that the number of items equals the number of bins
82+
type(bin_t) partition
83+
logical test_passes
84+
85+
type(bin_t), allocatable :: bins(:)
86+
integer, parameter :: n_items=11, n_bins=11
87+
integer b
88+
89+
bins = [( bin_t(num_items=n_items, num_bins=n_bins, bin_number=b), b = 1,n_bins )]
90+
test_passes = sum([(bins(b)%last() - bins(b)%first() + 1, b = 1, n_bins)]) == n_items
91+
92+
end function
93+
94+
function verify_some_singleton_bins_are_ok() result(test_passes)
95+
!! Verify that some singleton bins can be created and that the number of items equals the number of bins
96+
type(bin_t) partition
97+
logical test_passes
98+
99+
type(bin_t), allocatable :: bins(:), more_bins(:)
100+
integer, parameter :: n_bins=11, n_items=(n_bins + (n_bins/2))
101+
integer b
102+
103+
bins = [( bin_t(num_items=n_items, num_bins=n_bins, bin_number=b), b = 1,n_bins )]
104+
test_passes = sum([(bins(b)%last() - bins(b)%first() + 1, b = 1, n_bins)]) == n_items
105+
106+
more_bins = [( bin_t(num_items=n_items, num_bins=n_bins, bin_number=b), b = 1,n_bins )]
107+
associate(in_bin => [(more_bins(b)%last() - more_bins(b)%first() + 1, b = 1, n_bins)])
108+
associate(remainder => mod(n_items, n_bins), items_per_bin => n_items/n_bins)
109+
test_passes = test_passes .and. &
110+
all([(in_bin(1:remainder) == items_per_bin + 1)]) .and. all([(in_bin(remainder+1:) == items_per_bin)])
111+
end associate
112+
end associate
113+
114+
end function
76115
end module bin_test_m

test/command_line_test.f90

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@ function check_flag_value() result(test_passes)
3333
integer exit_status, command_status
3434
character(len=132) command_message
3535

36+
!! Can't spawn an MPI process from an MPI process, this will fail if example is compiled with MPI and the test is too.
37+
!! This triggers a full rebuild of the library and example if using caf. This can cause problems in some circumstances.
38+
!! Try to sanitize the environment and unset FPM_FC if it is caf.
3639
call execute_command_line( &
37-
command = "fpm run --example get-flag-value -- --input-file some_file_name", &
40+
command = "if [[ ${FPM_FC:-x} == *'caf' ]] ; then unset FPM_FC ; fi ; &
41+
&fpm run --example get-flag-value -- --input-file some_file_name", &
3842
wait = .true., exitstat = exit_status, cmdstat = command_status, cmdmsg = command_message &
39-
)
43+
)
4044
test_passes = exit_status == 0
4145

4246
end function

test/data_partition_test.f90

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,19 @@ function results() result(test_results)
2727
type(test_result_t), allocatable :: test_results(:)
2828

2929
test_results = [ &
30-
test_result_t("partitioning data in nearly even blocks", verify_block_partitioning()), &
30+
test_result_t("partitioning data in nearly even blocks", verify_block_partitioning(num_particles)), &
31+
test_result_t("partitioning data in nearly even blocks when some blocks are singletons", &
32+
verify_block_partitioning(num_images()+1)), &
33+
test_result_t("bins of size 1 when set cardinality == num_images()", verify_block_partitioning_with_singletons()), &
3134
test_result_t("default image_number is this_image()", verify_default_image_number()), &
35+
test_result_t("partitioning data into contiguous bins without overlap", &
36+
verify_partitions_are_contiguous_without_overlap(num_particles)), &
37+
test_result_t("contiguous non overlapping partitions with singletons", &
38+
verify_partitions_are_contiguous_without_overlap(num_images())), &
39+
test_result_t("contiguous non overlapping partitions with some singletons", &
40+
verify_partitions_are_contiguous_without_overlap(num_images()+1)), &
3241
test_result_t("partitioning all data across all images without data loss", verify_all_particles_partitioned()), &
42+
test_result_t("no data is lost when singleton bins are used", verify_all_particles_partitioned_on_singletons()), &
3343
test_result_t("gathering a 1D real array onto all images", verify_all_gather_1D_real_array()), &
3444
test_result_t("gathering dimension 1 of 2D real array onto all images witout dim argument", &
3545
verify_all_gather_2D_real_array()), &
@@ -40,26 +50,96 @@ function results() result(test_results)
4050
]
4151
end function
4252

43-
function verify_block_partitioning() result(test_passes)
53+
function verify_testing_in_parallel() result(test_passes)
54+
!! Verify that the test is being run in parallel
55+
logical test_passes
56+
test_passes = num_images() > 1
57+
end function
58+
59+
function verify_block_partitioning(cardinality) result(test_passes)
4460
!! Verify that the data is partitioned across images evenly to
4561
!! within a difference of one datum between any two images.
62+
integer, intent(in) :: cardinality
4663
type(data_partition_t) partition
4764
logical test_passes
4865
integer my_particles
4966

67+
associate( me=>this_image(), partition => data_partition_t(cardinality=cardinality))
68+
associate( my_first=>partition%first(me), my_last=>partition%last(me) )
69+
my_particles = my_last - my_first + 1
70+
associate( ni=>num_images() )
71+
associate( quotient=>cardinality/ni, remainder=>mod(cardinality,ni) )
72+
test_passes = quotient + merge(1, 0, me<=remainder) == my_particles
73+
end associate
74+
end associate
75+
end associate
76+
end associate
77+
78+
end function
79+
80+
function verify_block_partitioning_with_singletons() result(test_passes)
81+
!! Verify that the data is partitioned so that each image has a bin of
82+
!! size 1.
83+
type(data_partition_t) partition, another_partition
84+
logical test_passes
85+
integer my_particles
86+
integer num_particles
87+
88+
num_particles = num_images()
89+
another_partition = data_partition_t(cardinality=num_particles)
90+
5091
associate( me=>this_image(), partition => data_partition_t(cardinality=num_particles))
5192
associate( my_first=>partition%first(me), my_last=>partition%last(me) )
5293
my_particles = my_last - my_first + 1
5394
associate( ni=>num_images() )
5495
associate( quotient=>num_particles/ni, remainder=>mod(num_particles,ni) )
55-
test_passes = quotient + merge(1, 0, me<=remainder) == my_particles
96+
test_passes = quotient == 1 &
97+
.and. remainder == 0 &
98+
.and. my_particles == 1 &
99+
.and. my_last == my_first &
100+
.and. my_first == me
56101
end associate
57102
end associate
58103
end associate
59104
end associate
60105

61106
end function
62107

108+
function verify_partitions_are_contiguous_without_overlap(cardinality) result(test_passes)
109+
!! Verify that the data is partitioned across images into contiguous bins without overlap
110+
integer, intent(in) :: cardinality
111+
logical test_passes
112+
type(data_partition_t) partition
113+
114+
associate( me=>this_image(), partition => data_partition_t(cardinality=cardinality))
115+
associate( my_first=>partition%first(me), my_last=>partition%last(me) )
116+
associate(ni => num_images())
117+
if (me > 1) then
118+
associate( your_first=>partition%first(me-1), your_last=>partition%last(me-1) )
119+
test_passes = my_first <= my_last &
120+
.and. your_first <= your_last &
121+
.and. my_first >= 1 &
122+
.and. my_last <= cardinality &
123+
.and. my_first == your_last + 1
124+
end associate
125+
else if (me == 1 .and. ni > 1) then
126+
associate( your_first=>partition%first(me+1), your_last=>partition%last(me+1) )
127+
test_passes = my_first <= my_last &
128+
.and. your_first <= your_last &
129+
.and. my_first >= 1 &
130+
.and. my_last <= cardinality &
131+
.and. my_last == your_first - 1
132+
end associate
133+
else if (ni == 1) then
134+
test_passes = my_first <= my_last &
135+
.and. my_first >= 1 &
136+
.and. my_last <= cardinality
137+
end if
138+
end associate
139+
end associate
140+
end associate
141+
end function
142+
63143
function verify_default_image_number() result(test_passes)
64144
!! Verify that the first and last functions assume image_number == this_image() if image_number is not present
65145
type(data_partition_t) partition
@@ -86,6 +166,26 @@ function verify_all_particles_partitioned() result(test_passes)
86166
end associate
87167
end function
88168

169+
function verify_all_particles_partitioned_on_singletons() result(test_passes)
170+
!! Verify that the number of particles on each image sums to the
171+
!! total number of particles distributed when the cardinality of the
172+
!! partitioned set is equal to num_images()
173+
type(data_partition_t) partition
174+
logical test_passes
175+
integer particles
176+
integer num_particles
177+
num_particles = num_images()
178+
179+
associate( me=>this_image(), partition => data_partition_t(cardinality=num_particles))
180+
associate( my_first=>partition%first(me), my_last=>partition%last(me) )
181+
particles = my_last - my_first + 1
182+
test_passes = particles == 1
183+
call co_sum(particles)
184+
test_passes = test_passes .and. num_particles == particles
185+
end associate
186+
end associate
187+
end function
188+
89189
function verify_all_gather_1D_real_array() result(test_passes)
90190
type(data_partition_t) partition
91191
logical test_passes

test/main.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ program main
2929
call test_result_test%report(passes, tests)
3030
call string_test%report(passes, tests)
3131

32-
if (.not. GitHub_CI()) call command_line_test%report(passes, tests)
32+
call command_line_test%report(passes, tests)
3333

3434
if (this_image()==1) print *, new_line('a'), "_________ In total, ",passes," of ",tests, " tests pass. _________"
3535

0 commit comments

Comments
 (0)