Skip to content

Commit 70bb3f1

Browse files
committed
Add zip compression with tests
1 parent c1fb65f commit 70bb3f1

File tree

2 files changed

+132
-2
lines changed

2 files changed

+132
-2
lines changed

src/stdlib_io_zip.f90

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,48 @@
11
module stdlib_io_zip
22
use stdlib_filesystem, only: exists, run, temp_dir
3+
use stdlib_string_type, only: string_type, char
34
implicit none
45
private
56

6-
public :: unzip, default_unzip_dir, zip_contents
7+
public :: zip, unzip, default_unzip_dir, zip_contents
78

89
character(*), parameter :: default_unzip_dir = temp_dir//'/unzipped_files'
910
character(*), parameter :: zip_contents = default_unzip_dir//'/zip_contents.txt'
11+
character(*), parameter :: default_zip_dir = temp_dir//'.'
1012

1113
contains
1214

15+
subroutine zip(output_file, files, stat, msg)
16+
character(*), intent(in) :: output_file
17+
type(string_type), intent(in) :: files(:)
18+
integer, intent(out), optional :: stat
19+
character(len=:), allocatable, intent(out), optional :: msg
20+
21+
integer :: run_stat, i
22+
character(:), allocatable :: files_str
23+
24+
if (present(stat)) stat = 0
25+
run_stat = 0
26+
27+
if (trim(output_file) == '') then
28+
if (present(stat)) stat = 1
29+
if (present(msg)) msg = "Output file name is empty."
30+
return
31+
end if
32+
33+
files_str = ''
34+
do i = 1, size(files)
35+
files_str = files_str//' '//char(files(i))
36+
end do
37+
38+
call run('zip '//output_file//files_str, run_stat)
39+
if (run_stat /= 0) then
40+
if (present(stat)) stat = run_stat
41+
if (present(msg)) msg = "Error creating zip file '"//output_file//"'."
42+
return
43+
end if
44+
end
45+
1346
subroutine unzip(filename, outputdir, stat, msg)
1447
character(len=*), intent(in) :: filename
1548
character(len=*), intent(in), optional :: outputdir

test/io/test_zip.f90

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module test_zip
22
use stdlib_io_zip
3+
use stdlib_string_type, only : string_type, char
34
use testdrive, only : new_unittest, unittest_type, error_type, check, test_failed
45
implicit none
56
private
@@ -22,7 +23,12 @@ subroutine collect_zip(testsuite)
2223
new_unittest("unzip_zip_has_txt_file", unzip_zip_has_txt_file), &
2324
new_unittest("unzip_npz_array_empty_0_file", unzip_npz_array_empty_0_file), &
2425
new_unittest("unzip_two_files", unzip_two_files), &
25-
new_unittest("unzip_compressed_npz", unzip_compressed_npz) &
26+
new_unittest("unzip_compressed_npz", unzip_compressed_npz), &
27+
new_unittest("zip_nonexistent_file", zip_nonexistent_file, should_fail=.true.), &
28+
new_unittest("zip_invalid_file", zip_invalid_file, should_fail=.true.), &
29+
new_unittest("zip_empty_file", zip_empty_file), &
30+
new_unittest("zip_invalid_output_file", zip_invalid_output_file, should_fail=.true.), &
31+
new_unittest("zip_two_files", zip_two_files) &
2632
]
2733
end
2834

@@ -150,6 +156,97 @@ subroutine unzip_compressed_npz(error)
150156
call check(error, stat, "Listing the contents of a compressed npz file should not fail.")
151157
end
152158

159+
subroutine zip_nonexistent_file(error)
160+
type(error_type), allocatable, intent(out) :: error
161+
162+
integer :: stat
163+
character(*), parameter :: output_file = "temp.zip"
164+
character(*), parameter :: input_file = "nonexistent"
165+
type(string_type), allocatable :: files(:)
166+
167+
files = [string_type(input_file)]
168+
169+
call zip(output_file, files, stat)
170+
call check(error, stat, "Compressing a non-existent file should fail.")
171+
end
172+
173+
subroutine zip_invalid_file(error)
174+
type(error_type), allocatable, intent(out) :: error
175+
176+
integer :: stat
177+
character(*), parameter :: output_file = "temp.zip"
178+
character(*), parameter :: input_file = "."
179+
type(string_type), allocatable :: files(:)
180+
181+
files = [string_type(input_file)]
182+
183+
call zip(output_file, files, stat)
184+
call check(error, stat, "Compressing an invalid file should fail.")
185+
end
186+
187+
subroutine zip_empty_file(error)
188+
type(error_type), allocatable, intent(out) :: error
189+
190+
integer :: stat, unit
191+
character(*), parameter :: output_file = "temp.zip"
192+
character(*), parameter :: input_file = "abc.txt"
193+
type(string_type), allocatable :: files(:)
194+
195+
files = [string_type(input_file)]
196+
197+
open(newunit= unit, file=input_file)
198+
close(unit)
199+
200+
call zip(output_file, files, stat)
201+
call check(error, stat, "Compressing a valid empty file should not fail.")
202+
203+
call delete_file(input_file)
204+
call delete_file(output_file)
205+
end
206+
207+
subroutine zip_invalid_output_file(error)
208+
type(error_type), allocatable, intent(out) :: error
209+
210+
integer :: stat, unit
211+
character(*), parameter :: output_file = " "
212+
character(*), parameter :: input_file = "abc.txt"
213+
type(string_type), allocatable :: files(:)
214+
215+
files = [string_type(input_file)]
216+
217+
open(newunit=unit, file=input_file)
218+
close(unit)
219+
220+
call zip(output_file, files, stat)
221+
call check(error, stat, "Providing an empty output file should fail.")
222+
223+
call delete_file(input_file)
224+
end
225+
226+
subroutine zip_two_files(error)
227+
type(error_type), allocatable, intent(out) :: error
228+
229+
integer :: stat, unit
230+
character(*), parameter :: output_file = "temp.zip"
231+
character(*), parameter :: input_file_1 = "abc.txt"
232+
character(*), parameter :: input_file_2 = "def.txt"
233+
type(string_type), allocatable :: files(:)
234+
235+
files = [string_type(input_file_1), string_type(input_file_2)]
236+
237+
open(newunit=unit, file=input_file_1)
238+
close(unit)
239+
open(newunit=unit, file=input_file_2)
240+
close(unit)
241+
242+
call zip(output_file, files, stat)
243+
call check(error, stat, "Compressing two valid files should not fail.")
244+
245+
call delete_file(input_file_1)
246+
call delete_file(input_file_2)
247+
call delete_file(output_file)
248+
end
249+
153250
!> Makes sure that we find the file when running both `ctest` and `fpm test`.
154251
function get_path(file) result(path)
155252
character(*), intent(in) :: file

0 commit comments

Comments
 (0)