1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
import("core.project.config")
-- helper function to count table entries
function count_table(t)
local count = 0
for _ in pairs(t) do
count = count + 1
end
return count
end
-- helper function to get all package keys from lock data
function get_package_keys(lockdata)
local keys = {}
for key, _ in pairs(lockdata) do
if key ~= "__meta__" then
table.insert(keys, key)
end
end
return keys
end
-- helper function to verify lock file basic structure
function verify_lock_file_structure(lockdata)
assert(lockdata, "lock file should be loadable")
assert(lockdata.__meta__, "lock file should have metadata")
assert(lockdata.__meta__.version == "1.0", "lock file version should be 1.0")
-- find any platform entry
local found_platform = nil
for key, value in pairs(lockdata) do
if key ~= "__meta__" then
found_platform = key
break
end
end
assert(found_platform, "no platform entries found in lock file")
return lockdata[found_platform]
end
-- helper function to count zlib entries
function count_zlib_entries(plat_entries)
local zlib_count = 0
for key, _ in pairs(plat_entries) do
if key:find("zlib") then
zlib_count = zlib_count + 1
end
end
return zlib_count
end
-- helper function to remove installed packages to trigger reinstallation
function remove_zlib_packages()
os.execv("xmake", {"lua", "private.xrepo", "remove", "--all", "-y", "zlib"})
end
-- helper function to remove existing lock file
function remove_lock_file(scriptdir)
local lockfile = path.join(scriptdir, "xmake-requires.lock")
os.tryrm(lockfile)
end
-- test lock file generation and basic content validation
function test_lock_file_generation(scriptdir)
local lockfile = path.join(scriptdir, "xmake-requires.lock")
assert(os.isfile(lockfile), "xmake-requires.lock should be generated")
-- load and verify lock file content
local lockdata = io.load(lockfile)
local plat_entries = verify_lock_file_structure(lockdata)
-- check zlib entries and repo info
local found_zlib = false
for key, package_data in pairs(plat_entries) do
if key:find("zlib#") then
found_zlib = true
assert(package_data.version, "zlib should have version")
assert(package_data.repo, "zlib should have repo info")
assert(package_data.repo.url, "zlib repo should have url")
assert(package_data.repo.commit, "zlib repo should have commit")
end
end
-- we should have three zlib entries (version constraint, specific version, shared config)
local zlib_count = count_zlib_entries(plat_entries)
assert(zlib_count == 3, "should find 3 zlib entries in lock file, found: " .. zlib_count)
assert(found_zlib, "should find zlib entry in lock file")
-- verify specific versions are locked correctly
local found_old_version = false
for key, package_data in pairs(plat_entries) do
if key:find("zlib 1.2.13#") then
found_old_version = true
assert(package_data.version == "v1.2.13", "zlib 1.2.13 should be locked to v1.2.13")
end
end
assert(found_old_version, "should find zlib 1.2.13 entry with v1.2.13")
print("✓ lock file generation test passed")
end
-- test lock file stability across rebuilds
function test_lock_file_stability(scriptdir, t)
local lockfile = path.join(scriptdir, "xmake-requires.lock")
-- get the lock file content and mtime after first build
local lockdata_after_first = io.load(lockfile)
local mtime_after_first = os.mtime(lockfile)
local keys_after_first = get_package_keys(lockdata_after_first)
-- remove installed packages using xmake lua private.xrepo to trigger reinstallation
remove_zlib_packages()
-- rebuild and verify lock file content doesn't change
t:build()
local lockdata_after_second = io.load(lockfile)
local mtime_after_second = os.mtime(lockfile)
local keys_after_second = get_package_keys(lockdata_after_second)
-- compare lock file content
assert(lockdata_after_first.__meta__.version == lockdata_after_second.__meta__.version, "lock metadata version should not change")
assert(count_table(lockdata_after_first) == count_table(lockdata_after_second), "lock file structure should not change")
-- compare platform-specific entries using the same platform for consistency
local plat_entries_after_first = verify_lock_file_structure(lockdata_after_first)
local plat_entries_after_second = verify_lock_file_structure(lockdata_after_second)
-- ensure we're comparing the same platform entries by finding the common platform
local first_platform_key = nil
for key, value in pairs(lockdata_after_first) do
if key ~= "__meta__" then
first_platform_key = key
break
end
end
assert(first_platform_key, "no platform found in first lock file")
assert(lockdata_after_second[first_platform_key], "platform " .. first_platform_key .. " not found in second lock file")
plat_entries_after_first = lockdata_after_first[first_platform_key]
plat_entries_after_second = lockdata_after_second[first_platform_key]
assert(count_table(plat_entries_after_first) == count_table(plat_entries_after_second), "platform entries count should not change")
-- verify package key order stability (keys should be in same order)
assert(#keys_after_first == #keys_after_second, "package keys count should be the same")
for i, key in ipairs(keys_after_first) do
assert(keys_after_second[i] == key, "package key order should be stable at index " .. i .. ": " .. key)
end
-- verify each package entry is identical
for key, first_data in pairs(plat_entries_after_first) do
local second_data = plat_entries_after_second[key]
assert(second_data, "package entry should exist: " .. key)
assert(first_data.version == second_data.version, "version should not change for: " .. key .. ", first: " .. (first_data.version or "nil") .. ", second: " .. (second_data.version or "nil"))
assert(first_data.repo.url == second_data.repo.url, "repo url should not change for: " .. key .. ", first: " .. (first_data.repo.url or "nil") .. ", second: " .. (second_data.repo.url or "nil"))
assert(first_data.repo.commit == second_data.repo.commit, "repo commit should not change for: " .. key .. ", first: " .. (first_data.repo.commit or "nil") .. ", second: " .. (second_data.repo.commit or "nil"))
assert(first_data.repo.branch == second_data.repo.branch, "repo branch should not change for: " .. key .. ", first: " .. (first_data.repo.branch or "nil") .. ", second: " .. (second_data.repo.branch or "nil"))
end
-- verify mtime doesn't change (lock file should only be written when content actually changes)
-- With copy_if_different, the file should not be rewritten if content is identical
local time_diff = math.abs(mtime_after_second - mtime_after_first)
print("lock file mtime difference: " .. time_diff .. "s")
-- mtime should be exactly the same when content is identical
assert(time_diff == 0, "lock file mtime should not change when content is identical, diff: " .. time_diff .. "s")
print("✓ lock file stability test passed")
end
function main(t)
-- freebsd ci is slower
if is_host("bsd", "solaris", "haiku") then
return
end
-- only for x86/x64, because it will take too long time on ci with arm/mips
if os.subarch():startswith("x") or os.subarch() == "i386" then
-- get script directory from context filename
local scriptdir = path.directory(t.filename)
-- remove existing lock file before test
remove_lock_file(scriptdir)
-- remove installed packages using xmake lua private.xrepo to trigger reinstallation
remove_zlib_packages()
-- build project and generate requires lock
t:build()
-- test requires lock file generation and content
test_lock_file_generation(scriptdir)
-- test building with existing lock file
t:build()
-- test lock file stability across rebuilds
test_lock_file_stability(scriptdir, t)
print("requires lock test passed!")
end
end
|