aboutsummaryrefslogtreecommitdiffstats
path: root/components/net/tests/cookie_http_state_utils.py
blob: 30ee99f7f86b5e7b96a3100aec85733af2ce11b8 (plain) (blame)
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
# 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 https://mozilla.org/MPL/2.0/.

import os
import subprocess
import tempfile

REPO = "https://github.com/abarth/http-state.git"
TEST_FILE = "cookie_http_state.rs"
DOMAIN = "http://home.example.org:8888"
RUST_FN = """
#[test]{should_panic}
fn test_{name}() {{
    let r = run("{set_location}",
                {set_cookies},
                "{location}");
    assert_eq!(&r, "{expect}");
}}
"""
SET_COOKIES_INDENT = 18
SHOULD_PANIC = "\n#[should_panic] // Look at cookie_http_state_utils.py if this test fails"

# Those tests should PASS. But until fixes land in servo, keep them failing
FAILING_TESTS = [
    "0003",           # Waiting for a way to clean expired cookies
    "0006",           # Waiting for a way to clean expired cookies
    "mozilla0001",    # Waiting for a way to clean expired cookies
    "mozilla0002",    # Waiting for a way to clean expired cookies
    "mozilla0003",    # Waiting for a way to clean expired cookies
    "mozilla0005",    # Waiting for a way to clean expired cookies
    "mozilla0007",    # Waiting for a way to clean expired cookies
    "mozilla0009",    # Waiting for a way to clean expired cookies
    "mozilla0010",    # Waiting for a way to clean expired cookies
    "mozilla0013",    # Waiting for a way to clean expired cookies
]


def list_tests(dir):
    suffix = "-test"

    def keep(name):
        return name.endswith(suffix) and not name.startswith("disabled")

    tests = [name[:-len(suffix)] for name in os.listdir(dir) if keep(name)]
    tests.sort()
    return tests


def escape(s):
    """ Escape the string `s` so that it can be parsed by rust as a valid
    UTF-8 string.
    We can't use only `encode("unicode_escape")` as it produces things that
    rust does not accept ("\\xbf", "\\u6265" for example). So we manually
    convert all character whose code point is greater than 128 to
    \\u{code_point}.
    All other characters are encoded with "unicode_escape" to get escape
    sequences ("\\r" for example) except for `"` that we specifically escape
    because our string will be quoted by double-quotes.
    Lines are also limited in size, so split the string every 70 characters
    (gives room for indentation).
    """
    res = ""
    last_split = 0
    for c in s:
        if len(res) - last_split > 70:
            res += "\\\n"
            last_split = len(res)
        o = ord(c)
        if o == 34:
            res += "\\\""
            continue
        if o >= 128:
            res += "\\u{" + hex(o)[2:] + "}"
        else:
            res += c.encode("unicode_escape")
    return res


def format_slice_cookies(cookies):
    esc_cookies = ['"%s"' % escape(c) for c in cookies]
    if sum(len(s) for s in esc_cookies) < 80:
        sep = ", "
    else:
        sep = ",\n" + " " * SET_COOKIES_INDENT
    return "&[" + sep.join(esc_cookies) + "]"


def generate_code_for_test(test_dir, name):
    if name in FAILING_TESTS:
        should_panic = SHOULD_PANIC
    else:
        should_panic = ""

    test_file = os.path.join(test_dir, name + "-test")
    expect_file = os.path.join(test_dir, name + "-expected")

    set_cookies = []
    set_location = DOMAIN + "/cookie-parser?" + name
    expect = ""
    location = DOMAIN + "/cookie-parser-result?" + name

    with open(test_file) as fo:
        for line in fo:
            line = line.decode("utf-8").rstrip()
            prefix = "Set-Cookie: "
            if line.startswith(prefix):
                set_cookies.append(line[len(prefix):])
            prefix = "Location: "
            if line.startswith(prefix):
                location = line[len(prefix):]
                if location.startswith("/"):
                    location = DOMAIN + location

    with open(expect_file) as fo:
        for line in fo:
            line = line.decode("utf-8").rstrip()
            prefix = "Cookie: "
            if line.startswith(prefix):
                expect = line[len(prefix):]

    return RUST_FN.format(name=name.replace('-', '_'),
                          set_location=escape(set_location),
                          set_cookies=format_slice_cookies(set_cookies),
                          should_panic=should_panic,
                          location=escape(location),
                          expect=escape(expect))


def update_test_file(cachedir):
    workdir = os.path.dirname(os.path.realpath(__file__))
    test_file = os.path.join(workdir, TEST_FILE)

    # Create the cache dir
    if not os.path.isdir(cachedir):
        os.makedirs(cachedir)

    # Clone or update the repo
    repo_dir = os.path.join(cachedir, "http-state")
    if os.path.isdir(repo_dir):
        args = ["git", "pull", "-f"]
        process = subprocess.Popen(args, cwd=repo_dir)
        if process.wait() != 0:
            print("failed to update the http-state git repo")
            return 1
    else:
        args = ["git", "clone", REPO, repo_dir]
        process = subprocess.Popen(args)
        if process.wait() != 0:
            print("failed to clone the http-state git repo")
            return 1

    # Truncate the unit test file to remove all existing tests
    with open(test_file, "r+") as fo:
        while True:
            line = fo.readline()
            if line.strip() == "// Test listing":
                fo.truncate()
                fo.flush()
                break
            if line == "":
                print("Failed to find listing delimiter on unit test file")
                return 1

    # Append all tests to unit test file
    tests_dir = os.path.join(repo_dir, "tests", "data", "parser")
    with open(test_file, "a") as fo:
        for test in list_tests(tests_dir):
            fo.write(generate_code_for_test(tests_dir, test).encode("utf-8"))

    return 0


if __name__ == "__main__":
    update_test_file(tempfile.gettempdir())