# 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())