summaryrefslogtreecommitdiffstats
path: root/testing/awsy/awsy/test_memory_usage.py
blob: e3daaf08dc7bda4e1a4412d7d19644e58d9a15e8 (plain)
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# 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
import sys

import mozinfo
import yaml
from marionette_driver.errors import JavascriptException, ScriptTimeoutException
from mozproxy import get_playback

AWSY_PATH = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
if AWSY_PATH not in sys.path:
    sys.path.append(AWSY_PATH)

from awsy import process_perf_data, webservers
from awsy.awsy_test_case import AwsyTestCase


class TestMemoryUsage(AwsyTestCase):
    """Provides a test that collects memory usage at various checkpoints:
    - "Start" - Just after startup
    - "StartSettled" - After an additional wait time
    - "TabsOpen" - After opening all provided URLs
    - "TabsOpenSettled" - After an additional wait time
    - "TabsOpenForceGC" - After forcibly invoking garbage collection
    - "TabsClosed" - After closing all tabs
    - "TabsClosedSettled" - After an additional wait time
    - "TabsClosedForceGC" - After forcibly invoking garbage collection
    """

    def urls(self):
        return self._urls

    def perf_suites(self):
        return process_perf_data.PERF_SUITES

    def perf_checkpoints(self):
        return process_perf_data.CHECKPOINTS

    def perf_extra_opts(self):
        return self._extra_opts

    def setupTp5(self):
        urls = None
        default_tp5n_manifest = os.path.join(
            self._webroot_dir, "page_load_test", "tp5n", "tp5n.manifest"
        )
        tp5n_manifest = self.testvars.get("pageManifest", default_tp5n_manifest)
        with open(tp5n_manifest) as fp:
            urls = fp.readlines()
        # pylint --py3k: W1636
        urls = list(map(lambda x: x.replace("localhost", "localhost:{}"), urls))

        # We haven't set self._urls yet, so this value might be zero if
        # 'entities' wasn't specified.
        to_load = self.pages_to_load()
        if not to_load:
            to_load = len(urls)
        self._webservers = webservers.WebServers(
            "localhost", 8001, self._webroot_dir, to_load
        )
        self._webservers.start()
        for url, server in zip(urls, self._webservers.servers):
            self._urls.append(url.strip().format(server.port))

    def setupTp6(self):
        # tp5n stores its manifest in the zip file that gets extracted, tp6
        # doesn't so we just keep one in our project dir for now.
        default_tp6_pages_manifest = os.path.join(AWSY_PATH, "conf", "tp6-pages.yml")
        tp6_pages_manifest = self.testvars.get(
            "pageManifest", default_tp6_pages_manifest
        )
        urls = []
        with open(tp6_pages_manifest) as f:
            d = yaml.safe_load(f)
            for r in d:
                url = r["url"]
                if isinstance(url, list):
                    urls.extend(url)
                else:
                    urls.append(url)

        self._urls = urls

        # Indicate that we're using tp6 in the perf data.
        self._extra_opts = ["tp6"]

        if self.marionette.get_pref("fission.autostart"):
            self._extra_opts.append("fission")

        # Now we setup the mitm proxy with our tp6 pageset.
        tp6_pageset_manifest = os.path.join(AWSY_PATH, "tp6-pageset.manifest")
        config = {
            "playback_tool": "mitmproxy",
            "playback_version": "5.1.1",
            "playback_files": [tp6_pageset_manifest],
            "platform": mozinfo.os,
            "obj_path": self._webroot_dir,
            "binary": self._binary,
            "run_local": self._run_local,
            "app": "firefox",
            "host": "127.0.0.1",
            "ignore_mitmdump_exit_failure": True,
        }

        self._playback = get_playback(config)
        self._playback.start()

        # We need to reload after the mitmproxy cert is installed
        self.marionette.restart(in_app=False, clean=False)

        # Setup WebDriver capabilities that we need
        self.marionette.delete_session()
        caps = {
            "unhandledPromptBehavior": "dismiss",  # Ignore page navigation warnings
        }
        self.marionette.start_session(caps)
        self.marionette.set_context("chrome")

    def setUp(self):
        AwsyTestCase.setUp(self)
        self.logger.info("setting up")
        self._webroot_dir = self.testvars["webRootDir"]
        self._urls = []
        self._extra_opts = None

        if self.testvars.get("tp6", False):
            self.setupTp6()
        else:
            self.setupTp5()

        self.logger.info(
            "areweslimyet run by %d pages, %d iterations,"
            " %d perTabPause, %d settleWaitTime"
            % (
                self._pages_to_load,
                self._iterations,
                self._perTabPause,
                self._settleWaitTime,
            )
        )
        self.logger.info("done setting up!")

    def tearDown(self):
        self.logger.info("tearing down!")

        self.logger.info("tearing down webservers!")

        if self.testvars.get("tp6", False):
            self._playback.stop()
        else:
            self._webservers.stop()

        AwsyTestCase.tearDown(self)

        self.logger.info("done tearing down!")

    def clear_preloaded_browser(self):
        """
        Clears out the preloaded browser.
        """
        self.logger.info("closing preloaded browser")
        script = """
            if (window.NewTabPagePreloading) {
                return NewTabPagePreloading.removePreloadedBrowser(window);
            }
            return "NewTabPagePreloading.removePreloadedBrowser not available";
            """
        try:
            result = self.marionette.execute_script(script, script_timeout=180000)
        except JavascriptException as e:
            self.logger.error("removePreloadedBrowser() JavaScript error: %s" % e)
        except ScriptTimeoutException:
            self.logger.error("removePreloadedBrowser() timed out")
        except Exception:
            self.logger.error(
                "removePreloadedBrowser() Unexpected error: %s" % sys.exc_info()[0]
            )
        else:
            if result:
                self.logger.info(result)

    def test_open_tabs(self):
        """Marionette test entry that returns an array of checkpoint arrays.

        This will generate a set of checkpoints for each iteration requested.
        Upon successful completion the results will be stored in
        |self.testvars["results"]| and accessible to the test runner via the
        |testvars| object it passed in.
        """
        # setup the results array
        results = [[] for _ in range(self.iterations())]

        def create_checkpoint(name, iteration, minimize=False):
            checkpoint = self.do_memory_report(name, iteration, minimize)
            self.assertIsNotNone(checkpoint, "Checkpoint was recorded")
            results[iteration].append(checkpoint)

        # The first iteration gets Start and StartSettled entries before
        # opening tabs
        create_checkpoint("Start", 0)
        self.settle()
        create_checkpoint("StartSettled", 0)

        for itr in range(self.iterations()):
            self.open_pages()

            create_checkpoint("TabsOpen", itr)
            self.settle()
            create_checkpoint("TabsOpenSettled", itr)
            create_checkpoint("TabsOpenForceGC", itr, minimize=True)

            # Close all tabs
            self.reset_state()

            with self.marionette.using_context("content"):
                self.logger.info("navigating to about:blank")
                self.marionette.navigate("about:blank")
                self.logger.info("navigated to about:blank")
            self.signal_user_active()

            # Create checkpoint that may contain retained processes that will
            # be reused.
            create_checkpoint("TabsClosedExtraProcesses", itr)

            # Clear out the retained processes and measure again.
            self.clear_preloaded_browser()

            create_checkpoint("TabsClosed", itr)
            self.settle()
            create_checkpoint("TabsClosedSettled", itr)
            create_checkpoint("TabsClosedForceGC", itr, minimize=True)

        # TODO(ER): Temporary hack until bug 1121139 lands
        self.logger.info("setting results")
        self.testvars["results"] = results