Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/core/io/test_file_access.h
10278 views
1
/**************************************************************************/
2
/* test_file_access.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#pragma once
32
33
#include "core/io/file_access.h"
34
#include "tests/test_macros.h"
35
#include "tests/test_utils.h"
36
37
namespace TestFileAccess {
38
39
TEST_CASE("[FileAccess] CSV read") {
40
Ref<FileAccess> f = FileAccess::open(TestUtils::get_data_path("testdata.csv"), FileAccess::READ);
41
REQUIRE(f.is_valid());
42
43
Vector<String> header = f->get_csv_line(); // Default delimiter: ",".
44
REQUIRE(header.size() == 4);
45
46
Vector<String> row1 = f->get_csv_line(","); // Explicit delimiter, should be the same.
47
REQUIRE(row1.size() == 4);
48
CHECK(row1[0] == "GOOD_MORNING");
49
CHECK(row1[1] == "Good Morning");
50
CHECK(row1[2] == "Guten Morgen");
51
CHECK(row1[3] == "Bonjour");
52
53
Vector<String> row2 = f->get_csv_line();
54
REQUIRE(row2.size() == 4);
55
CHECK(row2[0] == "GOOD_EVENING");
56
CHECK(row2[1] == "Good Evening");
57
CHECK(row2[2].is_empty()); // Use case: not yet translated!
58
// https://github.com/godotengine/godot/issues/44269
59
CHECK_MESSAGE(row2[2] != "\"", "Should not parse empty string as a single double quote.");
60
CHECK(row2[3] == "\"\""); // Intentionally testing only escaped double quotes.
61
62
Vector<String> row3 = f->get_csv_line();
63
REQUIRE(row3.size() == 6);
64
CHECK(row3[0] == "Without quotes");
65
CHECK(row3[1] == "With, comma");
66
CHECK(row3[2] == "With \"inner\" quotes");
67
CHECK(row3[3] == "With \"inner\", quotes\",\" and comma");
68
CHECK(row3[4] == "With \"inner\nsplit\" quotes and\nline breaks");
69
CHECK(row3[5] == "With \\nnewline chars"); // Escaped, not an actual newline.
70
71
Vector<String> row4 = f->get_csv_line("~"); // Custom delimiter, makes inline commas easier.
72
REQUIRE(row4.size() == 3);
73
CHECK(row4[0] == "Some other");
74
CHECK(row4[1] == "delimiter");
75
CHECK(row4[2] == "should still work, shouldn't it?");
76
77
Vector<String> row5 = f->get_csv_line("\t"); // Tab separated variables.
78
REQUIRE(row5.size() == 3);
79
CHECK(row5[0] == "What about");
80
CHECK(row5[1] == "tab separated");
81
CHECK(row5[2] == "lines, good?");
82
}
83
84
TEST_CASE("[FileAccess] Get as UTF-8 String") {
85
Ref<FileAccess> f_lf = FileAccess::open(TestUtils::get_data_path("line_endings_lf.test.txt"), FileAccess::READ);
86
REQUIRE(f_lf.is_valid());
87
String s_lf = f_lf->get_as_utf8_string();
88
f_lf->seek(0);
89
String s_lf_nocr = f_lf->get_as_utf8_string(true);
90
CHECK(s_lf == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n");
91
CHECK(s_lf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n");
92
93
Ref<FileAccess> f_crlf = FileAccess::open(TestUtils::get_data_path("line_endings_crlf.test.txt"), FileAccess::READ);
94
REQUIRE(f_crlf.is_valid());
95
String s_crlf = f_crlf->get_as_utf8_string();
96
f_crlf->seek(0);
97
String s_crlf_nocr = f_crlf->get_as_utf8_string(true);
98
CHECK(s_crlf == "Hello darkness\r\nMy old friend\r\nI've come to talk\r\nWith you again\r\n");
99
CHECK(s_crlf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n");
100
101
Ref<FileAccess> f_cr = FileAccess::open(TestUtils::get_data_path("line_endings_cr.test.txt"), FileAccess::READ);
102
REQUIRE(f_cr.is_valid());
103
String s_cr = f_cr->get_as_utf8_string();
104
f_cr->seek(0);
105
String s_cr_nocr = f_cr->get_as_utf8_string(true);
106
CHECK(s_cr == "Hello darkness\rMy old friend\rI've come to talk\rWith you again\r");
107
CHECK(s_cr_nocr == "Hello darknessMy old friendI've come to talkWith you again");
108
}
109
110
TEST_CASE("[FileAccess] Get/Store floating point values") {
111
// BigEndian Hex: 0x40490E56
112
// LittleEndian Hex: 0x560E4940
113
float value = 3.1415f;
114
115
SUBCASE("Little Endian") {
116
const String file_path = TestUtils::get_data_path("floating_point_little_endian.bin");
117
const String file_path_new = TestUtils::get_data_path("floating_point_little_endian_new.bin");
118
119
Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
120
REQUIRE(f.is_valid());
121
CHECK_EQ(f->get_float(), value);
122
123
Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
124
REQUIRE(fw.is_valid());
125
fw->store_float(value);
126
fw->close();
127
128
CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
129
130
DirAccess::remove_file_or_error(file_path_new);
131
}
132
133
SUBCASE("Big Endian") {
134
const String file_path = TestUtils::get_data_path("floating_point_big_endian.bin");
135
const String file_path_new = TestUtils::get_data_path("floating_point_big_endian_new.bin");
136
137
Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
138
REQUIRE(f.is_valid());
139
f->set_big_endian(true);
140
CHECK_EQ(f->get_float(), value);
141
142
Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
143
REQUIRE(fw.is_valid());
144
fw->set_big_endian(true);
145
fw->store_float(value);
146
fw->close();
147
148
CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
149
150
DirAccess::remove_file_or_error(file_path_new);
151
}
152
}
153
154
TEST_CASE("[FileAccess] Get/Store floating point half precision values") {
155
// IEEE 754 half-precision binary floating-point format:
156
// sign exponent (5 bits) fraction (10 bits)
157
// 0 01101 0101010101
158
// BigEndian Hex: 0x3555
159
// LittleEndian Hex: 0x5535
160
float value = 0.33325195f;
161
162
SUBCASE("Little Endian") {
163
const String file_path = TestUtils::get_data_path("half_precision_floating_point_little_endian.bin");
164
const String file_path_new = TestUtils::get_data_path("half_precision_floating_point_little_endian_new.bin");
165
166
Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
167
REQUIRE(f.is_valid());
168
CHECK_EQ(f->get_half(), value);
169
170
Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
171
REQUIRE(fw.is_valid());
172
fw->store_half(value);
173
fw->close();
174
175
CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
176
177
DirAccess::remove_file_or_error(file_path_new);
178
}
179
180
SUBCASE("Big Endian") {
181
const String file_path = TestUtils::get_data_path("half_precision_floating_point_big_endian.bin");
182
const String file_path_new = TestUtils::get_data_path("half_precision_floating_point_big_endian_new.bin");
183
184
Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
185
REQUIRE(f.is_valid());
186
f->set_big_endian(true);
187
CHECK_EQ(f->get_half(), value);
188
189
Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
190
REQUIRE(fw.is_valid());
191
fw->set_big_endian(true);
192
fw->store_half(value);
193
fw->close();
194
195
CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
196
197
DirAccess::remove_file_or_error(file_path_new);
198
}
199
200
SUBCASE("4096 bytes fastlz compressed") {
201
const String file_path = TestUtils::get_data_path("exactly_4096_bytes_fastlz.bin");
202
203
Ref<FileAccess> f = FileAccess::open_compressed(file_path, FileAccess::READ, FileAccess::COMPRESSION_FASTLZ);
204
const Vector<uint8_t> full_data = f->get_buffer(4096 * 2);
205
CHECK(full_data.size() == 4096);
206
CHECK(f->eof_reached());
207
208
// Data should be empty.
209
PackedByteArray reference;
210
reference.resize_initialized(4096);
211
CHECK(reference == full_data);
212
213
f->seek(0);
214
const Vector<uint8_t> partial_data = f->get_buffer(4095);
215
CHECK(partial_data.size() == 4095);
216
CHECK(!f->eof_reached());
217
218
reference.resize_initialized(4095);
219
CHECK(reference == partial_data);
220
}
221
}
222
223
} // namespace TestFileAccess
224
225