Skip to content

Commit fe29f97

Browse files
committed
rename _convert to maybe_encode
* add tests for maybe_encode * ignore CRLF/LF differences
1 parent ab01585 commit fe29f97

13 files changed

Lines changed: 204 additions & 17 deletions

src/fileio.jl

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function loadfile(T, file::File)
99
end
1010

1111
function loadfile(T, file::TextFile)
12-
replace(read(file.filename, String), "\r"=>"") # ignore CRLF/LF difference
12+
_ignore_crlf(read(file.filename, String))
1313
end
1414

1515
function loadfile(::Type{<:Number}, file::File{format"TXT"})
@@ -24,7 +24,7 @@ function savefile(file::TextFile, content)
2424
write(file.filename, string(content))
2525
end
2626

27-
function query_extended(filename)
27+
function query_extended(filename::AbstractString)
2828
file, ext = splitext(filename)
2929
# TODO: make this less hacky
3030
if uppercase(ext) == ".SHA256"
@@ -38,20 +38,30 @@ function query_extended(filename)
3838
res
3939
end
4040

41+
# Some target formats are not supported by FileIO and thus require an encoding/compression process
42+
# before saving. For other formats, we should trust IO backends and make as few changes as possible.
43+
# Otherwise, reference becomes unfaithful. The encoding process helps making the actual data matches
44+
# the reference data, which is load from reference file via IO backends.
45+
#
46+
# TODO: split `maybe_encode` to `maybe_preprocess` and `maybe_encode`
4147
"""
42-
_convert(T::Type{<:DataFormat}, x; kw...) -> out
48+
maybe_encode(T::Type{<:DataFormat}, x; kw...) -> out
4349
44-
Convert `x` to a validate content for file data format `T`.
50+
If needed, encode `x` to a valid content that matches format `T`.
51+
52+
If there is no known method to encode `x`, then it directly return `x` without warning.
4553
"""
46-
_convert(::Type{<:DataFormat}, x; kw...) = x
54+
maybe_encode(::Type{<:DataFormat}, x; kw...) = x
55+
maybe_encode(::Type{<:DataFormat}, x::AbstractString; kw...) = _ignore_crlf(x)
56+
maybe_encode(::Type{<:DataFormat}, x::AbstractArray{<:AbstractString}; kw...) = _join(x)
4757

4858
# plain TXT
49-
_convert(::Type{DataFormat{:TXT}}, x; kw...) = replace(string(x), "\r"=>"") # ignore CRLF/LF difference
50-
_convert(::Type{DataFormat{:TXT}}, x::Number; kw...) = x
51-
function _convert(::Type{DataFormat{:TXT}}, x::AbstractArray{<:AbstractString}; kw...)
52-
return join(x, '\n')
53-
end
54-
function _convert(
59+
maybe_encode(::Type{DataFormat{:TXT}}, x; kw...) = _ignore_crlf(string(x))
60+
maybe_encode(::Type{DataFormat{:TXT}}, x::AbstractArray{<:AbstractString}; kw...) = _join(x) # ambiguity patch
61+
maybe_encode(::Type{DataFormat{:TXT}}, x::AbstractString; kw...) = _ignore_crlf(x) # ambiguity patch
62+
maybe_encode(::Type{DataFormat{:TXT}}, x::Number; kw...) = x # TODO: Change this to string(x) ?
63+
64+
function maybe_encode(
5565
::Type{DataFormat{:TXT}}, img::AbstractArray{<:Colorant};
5666
size = (20,40), kw...)
5767

@@ -65,11 +75,19 @@ function _convert(
6575
end
6676

6777
# SHA256
68-
_convert(::Type{DataFormat{:SHA256}}, x; kw...) = bytes2hex(sha256(string(x)))
69-
function _convert(::Type{DataFormat{:SHA256}}, img::AbstractArray{<:Colorant}; kw...)
78+
maybe_encode(::Type{DataFormat{:SHA256}}, x; kw...) = _sha256(string(x))
79+
maybe_encode(::Type{DataFormat{:SHA256}}, x::AbstractString) = _sha256(_ignore_crlf(x))
80+
maybe_encode(::Type{DataFormat{:SHA256}}, x::AbstractArray{<:AbstractString}) = _sha256(_join(x))
81+
function maybe_encode(::Type{DataFormat{:SHA256}}, img::AbstractArray{<:Colorant}; kw...)
7082
# encode image into SHA256
71-
size_str = bytes2hex(sha256(reinterpret(UInt8,[map(Int64,size(img))...])))
72-
img_str = bytes2hex(sha256(reinterpret(UInt8,vec(rawview(channelview(img))))))
83+
size_str = _sha256(reinterpret(UInt8,[map(Int64,size(img))...]))
84+
img_str = _sha256(reinterpret(UInt8,vec(rawview(channelview(img)))))
7385

7486
return size_str * img_str
7587
end
88+
89+
90+
# Helpers
91+
_join(x::AbstractArray{<:AbstractString}) = mapreduce(_ignore_crlf, (x,y)->x*"\n"*y, x)
92+
_sha256(x) = bytes2hex(sha256(x))
93+
_ignore_crlf(x::AbstractString) = replace(x, "\r"=>"")

src/test_reference.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ function test_reference(
107107
rendermode = default_rendermode(F, raw_actual)
108108
end
109109

110-
actual = _convert(F, raw_actual; kw...)
110+
actual = maybe_encode(F, raw_actual; kw...)
111111
# preprocessing when reference file doesn't exists
112112
if !isfile(path)
113113
@info("Reference file for \"$filename\" does not exist. It will be created")

test/fileio.jl

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
refdir = joinpath(refroot, "fileio")
2+
3+
@testset "query" begin
4+
check_types = [
5+
# text types
6+
("textfile_with_no_extension", format"TXT"),
7+
("textfile.txt", format"TXT"),
8+
("textfile.unknown", format"TXT"),
9+
("textfile.sha256", format"SHA256"),
10+
11+
# image types
12+
("imagefile.jpg", format"JPEG"),
13+
("imagefile.jpeg", format"JPEG"),
14+
("imagefile.png", format"PNG"),
15+
("imagefile.tif", format"TIFF"),
16+
("imagefile.tiff", format"TIFF"),
17+
18+
# dataframe types
19+
("dataframe_file.csv", format"CSV")
20+
]
21+
for (file, fmt) in check_types
22+
@test ReferenceTests.query_extended(file) == File{fmt}(file)
23+
@test ReferenceTests.query_extended(abspath(file)) == File{fmt}(abspath(file))
24+
end
25+
end
26+
27+
@testset "maybe_encode" begin
28+
@testset "string" begin
29+
str1 = "Hello world"
30+
str1_sha256 = "64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c"
31+
str2 = "Hello\n world"
32+
str2_sha256 = "60b65ab310480818c4289227f2ec68f1714743db8571b4cb190e100c0085be3d" # bytes2hex(SHA.sha256(str2))
33+
str2_crlf = "Hello\n\r world"
34+
str3 = "Hello\nworld"
35+
str3_sha256 = "46e0ea795802f17d0b340983ca7d7068c94d7d9172ee4daea37a1ab1168649ec" # bytes2hex(SHA.sha256(str3))
36+
str3_arr1 = ["Hello", "world"]
37+
str3_arr2 = ["Hello" "world"]
38+
str4 = "Hello\n world1\nHello\n world2"
39+
str4_sha256 = "c7dc8b82c3a6fed4afa0c8790a0586b73df0e4f35524efe6810e5d78b6b6a611" # bytes2hex(SHA.sha256(str4))
40+
str4_arr = ["Hello\n\r world1", "Hello\n world2"]
41+
42+
# string as plain text
43+
for fmt in (format"TXT", format"UNKNOWN")
44+
# convert should respect whitespaces
45+
@test str1 == ReferenceTests.maybe_encode(fmt, str1)
46+
@test str2 == ReferenceTests.maybe_encode(fmt, str2)
47+
# but ignore CRLF/LF differences
48+
@test str2 == ReferenceTests.maybe_encode(fmt, str2_crlf)
49+
# string arrays are treated as multi-line strings, even for UNKNOWN format
50+
@test str3 == ReferenceTests.maybe_encode(fmt, str3)
51+
@test str3 == ReferenceTests.maybe_encode(fmt, str3_arr1)
52+
@test str3 == ReferenceTests.maybe_encode(fmt, str3_arr2)
53+
# string arrays should ignore CRLF/LF differences, too
54+
@test str4 == ReferenceTests.maybe_encode(fmt, str4_arr)
55+
end
56+
57+
# string as SHA256 should also ignore CRLF/LF differences
58+
fmt = format"SHA256"
59+
@test str1_sha256 == ReferenceTests.maybe_encode(fmt, str1)
60+
@test str2_sha256 == ReferenceTests.maybe_encode(fmt, str2)
61+
# but ignore CRLF/LF differences
62+
@test str2_sha256 == ReferenceTests.maybe_encode(fmt, str2_crlf)
63+
# string arrays are treated as multi-line strings, even for UNKNOWN format
64+
@test str3_sha256 == ReferenceTests.maybe_encode(fmt, str3)
65+
@test str3_sha256 == ReferenceTests.maybe_encode(fmt, str3_arr1)
66+
@test str3_sha256 == ReferenceTests.maybe_encode(fmt, str3_arr2)
67+
# string arrays should ignore CRLF/LF differences, too
68+
@test str4_sha256 == ReferenceTests.maybe_encode(fmt, str4_arr)
69+
end
70+
71+
@testset "numbers" begin
72+
for num in (0x01, 1, 1.0f0, 1.0)
73+
for fmt in (format"TXT", format"UNKNOWN")
74+
@test num === ReferenceTests.maybe_encode(fmt, num)
75+
end
76+
fmt = format"SHA256"
77+
@test ReferenceTests.maybe_encode(fmt, num) == ReferenceTests.maybe_encode(fmt, string(num))
78+
end
79+
80+
81+
for (fmt, a, ref) in [
82+
# if target is TXT, convert it to string
83+
(format"TXT", [1, 2], "[1, 2]"),
84+
(format"TXT", [1,2], "[1, 2]"),
85+
(format"TXT", [1;2], "[1, 2]"),
86+
(format"TXT", [1 2], "[1 2]"),
87+
(format"TXT", [1 2; 3 4], "[1 2; 3 4]"),
88+
# if target is Unknown, make no change
89+
(format"UNKNOWN", [1, 2], [1, 2]),
90+
(format"UNKNOWN", [1,2], [1, 2]),
91+
(format"UNKNOWN", [1;2], [1, 2]),
92+
(format"UNKNOWN", [1 2], [1 2]),
93+
(format"UNKNOWN", [1 2; 3 4], [1 2; 3 4]),
94+
]
95+
@test ref == ReferenceTests.maybe_encode(fmt, a)
96+
end
97+
98+
for a in [[1, 2], [1 2], [1 2; 3 4]]
99+
fmt = format"SHA256"
100+
@test ReferenceTests.maybe_encode(fmt, a) == ReferenceTests.maybe_encode(fmt, string(a))
101+
end
102+
103+
end
104+
105+
@testset "image" begin
106+
gray_1d = Gray{N0f8}.(0.0:0.1:0.9)
107+
rgb_1d = RGB.(gray_1d)
108+
gray_2d = Gray{N0f8}.(reshape(0.0:0.1:0.9, 2, 5))
109+
rgb_2d = RGB.(gray_2d)
110+
gray_3d = Gray{N0f8}.(reshape(0.0:0.02:0.95, 2, 4, 6))
111+
rgb_3d = RGB.(gray_3d)
112+
113+
# any common image types
114+
for img in (gray_1d, gray_2d, gray_3d, rgb_1d, rgb_2d, rgb_3d)
115+
for fmt in (format"JPEG", format"PNG", format"TIFF", format"UNKNOWN")
116+
@test img === ReferenceTests.maybe_encode(fmt, img)
117+
end
118+
end
119+
120+
# image as text file
121+
fmt = format"TXT"
122+
# TODO: support n-D image encoding
123+
# @test_reference joinpath(refdir, "gray_1d_as_txt.txt") ReferenceTests.maybe_encode(fmt, gray_1d)
124+
# @test_reference joinpath(refdir, "rgb_1d_as_txt.txt") ReferenceTests.maybe_encode(fmt, rgb_1d)
125+
@test_reference joinpath(refdir, "gray_2d_as_txt.txt") ReferenceTests.maybe_encode(fmt, gray_2d)
126+
@test_reference joinpath(refdir, "rgb_2d_as_txt.txt") ReferenceTests.maybe_encode(fmt, rgb_2d)
127+
# @test_reference joinpath(refdir, "gray_3d_as_txt.txt") ReferenceTests.maybe_encode(fmt, gray_3d)
128+
# @test_reference joinpath(refdir, "rgb_3d_as_txt.txt") ReferenceTests.maybe_encode(fmt, rgb_3d)
129+
130+
# image as SHA256
131+
fmt = format"SHA256"
132+
for (file, img) in [
133+
("gray_1d", gray_1d),
134+
("gray_2d", gray_2d),
135+
("gray_3d", gray_3d),
136+
("rgb_1d", rgb_1d),
137+
("rgb_2d", rgb_2d),
138+
("rgb_3d", rgb_3d)
139+
]
140+
reffile = joinpath(refdir, "$(file)_as_sha256.txt")
141+
@test_reference reffile ReferenceTests.maybe_encode(fmt, img)
142+
end
143+
end
144+
145+
# dataframe
146+
@testset "dataframe" begin
147+
df = DataFrame(v1=[1,2,3], v2=["a","b","c"])
148+
149+
@test string(df) == ReferenceTests.maybe_encode(format"TXT", df)
150+
for fmt in (format"CSV", format"UNKNOWN")
151+
@test df === ReferenceTests.maybe_encode(fmt, df)
152+
end
153+
154+
fmt = format"SHA256"
155+
@test_reference joinpath(refdir, "dataframe_as_sha256.txt") ReferenceTests.maybe_encode(fmt, df)
156+
157+
end
158+
end
159+
160+
# TODO: savefile & loadfile
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2cf7c4edcafc27a5eb1b74fb0af704edc0d9bbef91a1b55d3b7350fa4b54cd18
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a111f275cc2e7588000001d300a31e76336d15b9d314cd1a1d8f3d3556975eed10ef43c7fcace84c4d0d54b8e92c0c9be2d14a6bf3dd7647254a3cc0c4a04297
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
26cfbb315c316a0b15516434f90284e5011dcb58503fe39eb036bf669bd8233d10ef43c7fcace84c4d0d54b8e92c0c9be2d14a6bf3dd7647254a3cc0c4a04297
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
▀▀▀▀▀
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
72307e420b5460c03a1c167060ed336407c26ea74aabf8fab76dd8e9dbe8cbe4baf0f53196e8d5270c0b0b2da82bbbb4676edbb0ebf84ec0dcbd8c0bf4d9af68
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a111f275cc2e7588000001d300a31e76336d15b9d314cd1a1d8f3d3556975eedebd6b0ad29dd5402ce5745bb5b48d4c59b7f8da0cdf8d2f287befd9094f6ac89
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
26cfbb315c316a0b15516434f90284e5011dcb58503fe39eb036bf669bd8233debd6b0ad29dd5402ce5745bb5b48d4c59b7f8da0cdf8d2f287befd9094f6ac89

0 commit comments

Comments
 (0)