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
|
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
from marionette_driver import Wait
from marionette_harness import MarionetteTestCase
"""
Currently we expect our size after cleanup to be 98k for the current
database schema and page sizes and constants used in this test. We
set the threshold at 128k so the test doesn't start failing if these
control objects/structures increase somewhat in size, but should still
fail if we fail to delete all of the 5,000 1k files we create on disk
as part of the test.
"""
QM_TESTING_PREF = "dom.quotaManager.testing"
EXPECTED_CACHEDIR_SIZE_AFTER_CLEANUP = 128 * 1024 # 128KB
CACHE_ID = "data"
class CachesDeleteCleanupAtShutdownTestCase(MarionetteTestCase):
"""
Bug1784700: This test ensures that cache body files get cleaned up
properly after cache has been deleted. Note that body files gets
cleaned up asynchronously which means that these files might still
be around even after Caches.Delete promise gets resolved. Currently,
we would only clean up body files on origin initialization and that's
a firefox is necessary here.
"""
def setUp(self):
super(CachesDeleteCleanupAtShutdownTestCase, self).setUp()
self.marionette.restart(in_app=False, clean=True)
self.marionette.set_pref(QM_TESTING_PREF, True)
def tearDown(self):
self.marionette.restart(in_app=False, clean=True)
super(CachesDeleteCleanupAtShutdownTestCase, self).tearDown()
self.marionette.set_pref(QM_TESTING_PREF, False)
def getUsage(self):
return self.marionette.execute_script(
"""
return window.wrappedJSObject.getStorageEstimate();
""",
new_sandbox=False,
)
def doCacheWork(self, n):
# max timeout for this script to execute is 5 minutes
maxTimeout = 5 * 60 * 1000
return self.marionette.execute_script(
"""
const [cacheId, n] = arguments;
return window.wrappedJSObject.doCacheWork(cacheId, n);
""",
script_args=(
CACHE_ID,
n,
),
new_sandbox=False,
script_timeout=maxTimeout,
)
def openCache(self):
return self.marionette.execute_async_script(
"""
const [cacheId, resolve] = arguments;
window.wrappedJSObject.openCache(cacheId).then(resolve("success"));
""",
new_sandbox=False,
script_args=(CACHE_ID,),
)
def countBodies(self):
profile = self.marionette.instance.profile.profile
originDir = (
self.marionette.absolute_url("")[:-1].replace(":", "+").replace("/", "+")
)
morgueDir = f"{profile}/storage/default/{originDir}/cache/morgue"
print("morgueDir path = ", morgueDir)
bodyCount = -1
if os.path.exists(morgueDir):
bodyCount = 0
for elem in os.listdir(morgueDir):
absPathElem = os.path.join(morgueDir, elem)
if os.path.isdir(absPathElem):
bodyCount += sum(
1
for e in os.listdir(absPathElem)
if os.path.isfile(os.path.join(absPathElem, e))
)
return bodyCount
def ensureCleanDirectory(self):
orphanedBodiesCount = self.countBodies()
return orphanedBodiesCount <= 0
def isStorageInitialized(self, temporary=False):
with self.marionette.using_context("chrome"):
return self.marionette.execute_async_script(
"""
const [resolve] = arguments;
const req = %s;
req.callback = () => {
resolve(req.resultCode == Cr.NS_OK && req.result)
};
"""
% (
"Services.qms.temporaryStorageInitialized()"
if temporary
else "Services.qms.storageInitialized()"
),
new_sandbox=False,
)
def create_and_cleanup_cache(self, ensureCleanCallback, in_app):
# create 640 cache entries
self.doCacheWork(640)
print("usage after doCacheWork = ", self.getUsage())
self.marionette.restart(in_app=in_app)
print("restart successful")
self.marionette.navigate(
self.marionette.absolute_url("dom/cache/cacheUsage.html")
)
return ensureCleanCallback()
def afterCleanupClosure(self, usage):
print(
f"Storage initialized = {self.isStorageInitialized()}, temporary storage initialized = {self.isStorageInitialized(True)}"
)
print(f"Usage = {usage} and number of orphaned bodies = {self.countBodies()}")
return usage < EXPECTED_CACHEDIR_SIZE_AFTER_CLEANUP
def test_ensure_cache_cleanup_after_clean_restart(self):
self.marionette.navigate(
self.marionette.absolute_url("dom/cache/cacheUsage.html")
)
beforeUsage = self.getUsage()
def ensureCleanCallback():
Wait(self.marionette, interval=1, timeout=60).until(
lambda _: self.afterCleanupClosure(self.getUsage() - beforeUsage),
message="Cache directory is not cleaned up properly",
)
return (
abs(beforeUsage - self.getUsage())
<= EXPECTED_CACHEDIR_SIZE_AFTER_CLEANUP
and self.ensureCleanDirectory()
)
if not self.create_and_cleanup_cache(ensureCleanCallback, True):
print(f"beforeUsage = {beforeUsage}, and afterUsage = {self.getUsage()}")
assert False
def test_ensure_cache_cleanup_after_unclean_restart(self):
self.marionette.navigate(
self.marionette.absolute_url("dom/cache/cacheUsage.html")
)
print(f"profile path = {self.marionette.instance.profile.profile}")
beforeUsage = self.getUsage()
def ensureCleanCallback():
print(
f"ensureCleanCallback, profile path = {self.marionette.instance.profile.profile}"
)
self.openCache()
Wait(self.marionette, interval=1, timeout=60).until(
lambda _: self.afterCleanupClosure(self.getUsage() - beforeUsage),
message="Cache directory is not cleaned up properly",
)
return (
abs(beforeUsage - self.getUsage())
<= EXPECTED_CACHEDIR_SIZE_AFTER_CLEANUP
and self.ensureCleanDirectory()
)
if not self.create_and_cleanup_cache(ensureCleanCallback, False):
print(f"beforeUsage = {beforeUsage}, and afterUsage = {self.getUsage()}")
assert False
|