#!/usr/bin/python3
# -*- coding: utf-8 -*-

# This test runs a local copy of https://html5test.com/ and
# checks the resulting score against the expected result.
# The score may get higher with newer versions of firefox.
# The local copy of the test was cloned from
# https://github.com/WebPlatformTest/HTML5test.git

import http.server
import os
import re
import sys
import threading
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

if __name__ == '__main__':
    options = webdriver.firefox.options.Options()
    options.binary_location = "/usr/bin/firefox"
    options.add_argument("-headless")
    driver = webdriver.Firefox(options=options)

    tests_dir = os.path.dirname(os.path.realpath(__file__))
    html5test_dir = os.path.join(tests_dir, "data", "HTML5test")
    os.chdir(html5test_dir)

    handler = http.server.SimpleHTTPRequestHandler
    server = http.server.HTTPServer(("", 0), handler)
    server.allow_reuse_address = True
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.start()

    driver.get("http://localhost:{}/".format(server.server_port))
    WebDriverWait(driver, 10).until(
        EC.text_to_be_present_in_element(
            (By.ID, "score"), "YOUR BROWSER SCORES"))
    score = driver.find_element_by_id("score").text
    match = re.search("YOUR BROWSER SCORES (\d+) OUT OF (\d+) POINTS", score)
    assert match is not None
    score = (int(match.group(1)), int(match.group(2)))
    expected_score = (508, 555)
    sys.stderr.flush()
    print("Score: {}/{}".format(score[0], score[1]))

    expected = [
        ("row-parsing.doctype", "Yes"),
        ("row-parsing.tokenizer", "Yes"),
        ("row-parsing.tree", "Yes"),
        ("row-parsing.svg", "Yes"),
        ("row-parsing.mathml", "Yes"),
        ("row-elements.dataset", "Yes"),
        ("row-elements.section", "Yes"),
        ("row-elements.section.section", "Yes"),
        ("row-elements.section.nav", "Yes"),
        ("row-elements.section.article", "Yes"),
        ("row-elements.section.aside", "Yes"),
        ("row-elements.section.header", "Yes"),
        ("row-elements.section.footer", "Yes"),
        ("row-elements.grouping", "Yes"),
        ("row-elements.grouping.main", "Yes"),
        ("row-elements.grouping.figure", "Yes"),
        ("row-elements.grouping.figcaption", "Yes"),
        ("row-elements.grouping.ol", "Yes"),
        ("row-elements.semantic", "Yes"),
        ("row-elements.semantic.download", "Yes"),
        ("row-elements.semantic.ping", "Yes"),
        ("row-elements.semantic.mark", "Yes"),
        ("row-elements.semantic.ruby", "Yes"),
        ("row-elements.semantic.time", "Yes"),
        ("row-elements.semantic.data", "Yes"),
        ("row-elements.semantic.wbr", "Yes"),
        ("row-elements.interactive", "Partial"),
        ("row-elements.interactive.details", "Yes"),
        ("row-elements.interactive.summary", "Yes"),
        ("row-elements.interactive.menutoolbar", "No"),
        ("row-elements.interactive.menucontext", "No"),
        ("row-elements.interactive.dialog", "Yes"),
        ("row-elements.hidden", "Yes"),
        ("row-elements.dynamic", "Yes"),
        ("row-elements.dynamic.outerHTML", "Yes"),
        ("row-elements.dynamic.insertAdjacentHTML", "Yes"),
        ("row-form.text", "Yes"),
        ("row-form.text.element", "Yes"),
        ("row-form.text.selection", "Yes"),
        ("row-form.search", "Yes"),
        ("row-form.search.element", "Yes"),
        ("row-form.tel", "Yes"),
        ("row-form.tel.element", "Yes"),
        ("row-form.url", "Yes"),
        ("row-form.url.element", "Yes"),
        ("row-form.url.validation", "Yes"),
        ("row-form.email", "Yes"),
        ("row-form.email.element", "Yes"),
        ("row-form.email.validation", "Yes"),
        ("row-form.date", "Yes"),
        ("row-form.date.element", "Yes"),
        ("row-form.date.ui", "Yes"),
        ("row-form.date.sanitization", "Yes"),
        ("row-form.date.min", "Yes"),
        ("row-form.date.max", "Yes"),
        ("row-form.date.step", "Yes"),
        ("row-form.date.stepDown", "Yes"),
        ("row-form.date.stepUp", "Yes"),
        ("row-form.date.valueAsDate", "Yes"),
        ("row-form.date.valueAsNumber", "Yes"),
        ("row-form.month", "No"),
        ("row-form.month.element", "No"),
        ("row-form.month.ui", "No"),
        ("row-form.month.sanitization", "No"),
        ("row-form.month.min", "No"),
        ("row-form.month.max", "No"),
        ("row-form.month.step", "No"),
        ("row-form.month.stepDown", "No"),
        ("row-form.month.stepUp", "No"),
        ("row-form.month.valueAsDate", "No"),
        ("row-form.month.valueAsNumber", "No"),
        ("row-form.week", "No"),
        ("row-form.week.element", "No"),
        ("row-form.week.ui", "No"),
        ("row-form.week.sanitization", "No"),
        ("row-form.week.min", "No"),
        ("row-form.week.max", "No"),
        ("row-form.week.step", "No"),
        ("row-form.week.stepDown", "No"),
        ("row-form.week.stepUp", "No"),
        ("row-form.week.valueAsDate", "No"),
        ("row-form.week.valueAsNumber", "No"),
        ("row-form.time", "Yes"),
        ("row-form.time.element", "Yes"),
        ("row-form.time.ui", "Yes"),
        ("row-form.time.sanitization", "Yes"),
        ("row-form.time.min", "Yes"),
        ("row-form.time.max", "Yes"),
        ("row-form.time.step", "Yes"),
        ("row-form.time.stepDown", "Yes"),
        ("row-form.time.stepUp", "Yes"),
        ("row-form.time.valueAsDate", "Yes"),
        ("row-form.time.valueAsNumber", "Yes"),
        ("row-form.datetime-local", "Yes"),
        ("row-form.datetime-local.element", "Yes"),
        ("row-form.datetime-local.ui", "Yes"),
        ("row-form.datetime-local.sanitization", "Yes"),
        ("row-form.datetime-local.min", "Yes"),
        ("row-form.datetime-local.max", "Yes"),
        ("row-form.datetime-local.step", "Yes"),
        ("row-form.datetime-local.stepDown", "Yes"),
        ("row-form.datetime-local.stepUp", "Yes"),
        ("row-form.datetime-local.valueAsNumber", "Yes"),
        ("row-form.number", "Yes"),
        ("row-form.number.element", "Yes"),
        ("row-form.number.ui", "Yes"),
        ("row-form.number.sanitization", "Yes"),
        ("row-form.number.validation", "Yes"),
        ("row-form.number.min", "Yes"),
        ("row-form.number.max", "Yes"),
        ("row-form.number.step", "Yes"),
        ("row-form.number.stepDown", "Yes"),
        ("row-form.number.stepUp", "Yes"),
        ("row-form.number.valueAsNumber", "Yes"),
        ("row-form.range", "Yes"),
        ("row-form.range.element", "Yes"),
        ("row-form.range.ui", "Yes"),
        ("row-form.range.sanitization", "Yes"),
        ("row-form.range.min", "Yes"),
        ("row-form.range.max", "Yes"),
        ("row-form.range.step", "Yes"),
        ("row-form.range.stepDown", "Yes"),
        ("row-form.range.stepUp", "Yes"),
        ("row-form.range.valueAsNumber", "Yes"),
        ("row-form.color", "Yes"),
        ("row-form.color.element", "Yes"),
        ("row-form.color.ui", "Yes"),
        ("row-form.color.sanitization", "Yes"),
        ("row-form.checkbox", "Yes"),
        ("row-form.checkbox.element", "Yes"),
        ("row-form.checkbox.indeterminate", "Yes"),
        ("row-form.image", "Yes"),
        ("row-form.image.element", "Yes"),
        ("row-form.image.width", "Yes"),
        ("row-form.image.height", "Yes"),
        ("row-form.file", "Partial"),
        ("row-form.file.element", "Yes"),
        ("row-form.file.files", "Yes"),
        ("row-form.file.directory", "No"),
        ("row-form.textarea", "Yes"),
        ("row-form.textarea.element", "Yes"),
        ("row-form.textarea.maxlength", "Yes"),
        ("row-form.textarea.wrap", "Yes"),
        ("row-form.select", "Yes"),
        ("row-form.select.element", "Yes"),
        ("row-form.select.required", "Yes"),
        ("row-form.fieldset", "Yes"),
        ("row-form.fieldset.element", "Yes"),
        ("row-form.fieldset.elements", "Yes"),
        ("row-form.fieldset.disabled", "Yes"),
        ("row-form.datalist", "Yes"),
        ("row-form.datalist.element", "Yes"),
        ("row-form.datalist.list", "Yes"),
        ("row-form.output", "Yes"),
        ("row-form.output.element", "Yes"),
        ("row-form.progress", "Yes"),
        ("row-form.progress.element", "Yes"),
        ("row-form.meter", "Yes"),
        ("row-form.meter.element", "Yes"),
        ("row-form.validation", "Yes"),
        ("row-form.validation.pattern", "Yes"),
        ("row-form.validation.required", "Yes"),
        ("row-form.association", "Yes"),
        ("row-form.association.control", "Yes"),
        ("row-form.association.form", "Yes"),
        ("row-form.association.formAction", "Yes"),
        ("row-form.association.formEnctype", "Yes"),
        ("row-form.association.formMethod", "Yes"),
        ("row-form.association.formNoValidate", "Yes"),
        ("row-form.association.formTarget", "Yes"),
        ("row-form.association.labels", "Yes"),
        ("row-form.other", "Yes"),
        ("row-form.other.autofocus", "Yes"),
        ("row-form.other.autocomplete", "Yes"),
        ("row-form.other.placeholder", "Yes"),
        ("row-form.other.multiple", "Yes"),
        ("row-form.other.dirname", "Yes"),
        ("row-form.selectors", "Yes"),
        ("row-form.selectors.valid", "Yes"),
        ("row-form.selectors.invalid", "Yes"),
        ("row-form.selectors.optional", "Yes"),
        ("row-form.selectors.required", "Yes"),
        ("row-form.selectors.in-range", "Yes"),
        ("row-form.selectors.out-of-range", "Yes"),
        ("row-form.selectors.read-write", "Yes"),
        ("row-form.selectors.read-only", "Yes"),
        ("row-form.events", "Yes"),
        ("row-form.events.oninput", "Yes"),
        ("row-form.events.onchange", "Yes"),
        ("row-form.events.oninvalid", "Yes"),
        ("row-form.formvalidation", "Yes"),
        ("row-form.formvalidation.checkValidity", "Yes"),
        ("row-form.formvalidation.noValidate", "Yes"),
        ("row-components.custom", "No"),
        ("row-components.shadowdom", "Yes"),
        ("row-components.template", "Yes"),
        ("row-components.imports", "No"),
        ("row-location.geolocation", "Yes"),
        ("row-location.orientation", "Yes"),
        ("row-location.motion", "Yes"),
        ("row-output.requestFullScreen", "Yes"),
        ("row-output.notifications", "Yes"),
        ("row-input.getGamepads", "Yes"),
        ("row-input.pointerevents", "Yes"),
        ("row-input.pointerLock", "Yes"),
        ("row-communication.eventSource", "Yes"),
        ("row-communication.beacon", "Yes"),
        ("row-communication.fetch", "Yes"),
        ("row-communication.xmlhttprequest2.upload", "Yes"),
        ("row-communication.xmlhttprequest2.response", "Yes"),
        ("row-communication.xmlhttprequest2.response.text", "Yes"),
        ("row-communication.xmlhttprequest2.response.document", "Yes"),
        ("row-communication.xmlhttprequest2.response.array", "Yes"),
        ("row-communication.xmlhttprequest2.response.blob", "Yes"),
        ("row-communication.websocket.basic", "Yes"),
        ("row-communication.websocket.binary", "Yes"),
        ("row-streams.readable", "Yes"),
        ("row-streams.writeable", "No"),
        ("row-rtc.webrtc", "Yes"),
        ("row-rtc.objectrtc", "Yes"),
        ("row-rtc.datachannel", "Yes"),
        ("row-media.getUserMedia", "Yes"),
        ("row-media.getDisplayMedia", "Yes"),
        ("row-media.enumerateDevices", "Yes"),
        ("row-rtc.recorder", "Yes"),
        ("row-interaction.dragdrop.attributes", "Partial"),
        ("row-interaction.dragdrop.attributes.draggable", "Yes"),
        ("row-interaction.dragdrop.attributes.dropzone", "No"),
        ("row-interaction.dragdrop.events", "Yes"),
        ("row-interaction.dragdrop.events.ondrag", "Yes"),
        ("row-interaction.dragdrop.events.ondragstart", "Yes"),
        ("row-interaction.dragdrop.events.ondragenter", "Yes"),
        ("row-interaction.dragdrop.events.ondragover", "Yes"),
        ("row-interaction.dragdrop.events.ondragleave", "Yes"),
        ("row-interaction.dragdrop.events.ondragend", "Yes"),
        ("row-interaction.dragdrop.events.ondrop", "Yes"),
        ("row-interaction.editing.elements", "Yes"),
        ("row-interaction.editing.elements.contentEditable", "Yes"),
        ("row-interaction.editing.elements.isContentEditable", "Yes"),
        ("row-interaction.editing.documents", "Yes"),
        ("row-interaction.editing.documents.designMode", "Yes"),
        ("row-interaction.editing.selectors", "Yes"),
        ("row-interaction.editing.selectors.read-write", "Yes"),
        ("row-interaction.editing.selectors.read-only", "Yes"),
        ("row-interaction.editing.apis", "Yes"),
        ("row-interaction.editing.apis.execCommand", "Yes"),
        ("row-interaction.editing.apis.queryCommandEnabled", "Yes"),
        ("row-interaction.editing.apis.queryCommandIndeterm", "Yes"),
        ("row-interaction.editing.apis.queryCommandState", "Yes"),
        ("row-interaction.editing.apis.queryCommandSupported", "Yes"),
        ("row-interaction.editing.apis.queryCommandValue", "Yes"),
        ("row-interaction.clipboard", "Yes"),
        ("row-interaction.spellcheck", "Yes"),
        ("row-performance.worker", "Yes"),
        ("row-performance.sharedWorker", "Yes"),
        ("row-performance.requestIdleCallback", "Yes"),
        ("row-security.crypto", "Yes"),
        ("row-security.csp10", "No"),
        ("row-security.csp11", "Yes"),
        ("row-security.cors", "Yes"),
        ("row-security.integrity", "Yes"),
        ("row-security.postMessage", "Yes"),
        ("row-security.authentication", "No"),
        ("row-security.credential", "Yes"),
        ("row-security.sandbox", "Yes"),
        ("row-security.srcdoc", "Yes"),
        ("row-payments.payments", "No"),
        ("row-video.element", "Yes"),
        ("row-video.subtitle", "Yes"),
        ("row-video.audiotracks", "No"),
        ("row-video.videotracks", "No"),
        ("row-video.poster", "Yes"),
        ("row-video.canplaytype", "Yes"),
        ("row-video.codecs.mp4.mpeg4", "No"),
        ("row-video.codecs.mp4.h264", "Yes"),
        ("row-video.codecs.mp4.h265", "No"),
        ("row-video.codecs.ogg.theora", "Yes"),
        ("row-video.codecs.webm.vp8", "Yes"),
        ("row-video.codecs.webm.vp9", "Yes"),
        ("row-audio.element", "Yes"),
        ("row-audio.loop", "Yes"),
        ("row-audio.preload", "Yes"),
        ("row-audio.webaudio", "Yes"),
        ("row-audio.speechrecognition", "No"),
        ("row-audio.speechsynthesis", "No"),
        ("row-audio.codecs.pcm", "Yes"),
        ("row-audio.codecs.mp3", "Yes"),
        ("row-audio.codecs.mp4.aac", "Yes"),
        ("row-audio.codecs.mp4.ac3", "No"),
        ("row-audio.codecs.mp4.ec3", "No"),
        ("row-audio.codecs.ogg.vorbis", "Yes"),
        ("row-audio.codecs.ogg.opus", "Yes"),
        ("row-audio.codecs.webm.vorbis", "Yes"),
        ("row-audio.codecs.webm.opus", "Yes"),
        ("row-streaming.mediasource", "Yes"),
        ("row-streaming.drm", "Yes"),
        ("row-streaming.type.dash", "No"),
        ("row-streaming.type.hls", "No"),
        ("row-streaming.video.codecs", "Partial"),
        ("row-streaming.video.codecs.mp4.h264", "Yes"),
        ("row-streaming.video.codecs.mp4.h265", "No"),
        ("row-streaming.video.codecs.ts.h264", "No"),
        ("row-streaming.video.codecs.ts.h265", "No"),
        ("row-streaming.video.codecs.webm.vp8", "Yes"),
        ("row-streaming.video.codecs.webm.vp9", "Yes"),
        ("row-streaming.audio.codecs", "Partial"),
        ("row-streaming.audio.codecs.mp4.aac", "Yes"),
        ("row-streaming.audio.codecs.mp4.ac3", "No"),
        ("row-streaming.audio.codecs.mp4.ec3", "No"),
        ("row-streaming.audio.codecs.ts.aac", "No"),
        ("row-streaming.audio.codecs.ts.ac3", "No"),
        ("row-streaming.audio.codecs.ts.ec3", "No"),
        ("row-streaming.audio.codecs.webm.vorbis", "Yes"),
        ("row-streaming.audio.codecs.webm.opus", "Yes"),
        ("row-responsive.picture", "Yes"),
        ("row-responsive.srcset", "Yes"),
        ("row-responsive.sizes", "Yes"),
        ("row-canvas.context", "Yes"),
        ("row-canvas.text", "Yes"),
        ("row-canvas.path", "Yes"),
        ("row-canvas.ellipse", "Yes"),
        ("row-canvas.dashed", "Yes"),
        ("row-canvas.focusring", "Yes"),
        ("row-canvas.hittest", "No"),
        ("row-canvas.blending", "Yes"),
        ("row-canvas.png", "Yes"),
        ("row-canvas.jpeg", "Yes"),
        ("row-canvas.jpegxr", "No"),
        ("row-canvas.webp", "Yes"),
        ("row-3d.webgl", "Yes"),
        ("row-3d.webgl2", "Yes"),
        ("row-3d.webvr", "No"),
        ("row-animation.webanimation", "Yes"),
        ("row-animation.requestAnimationFrame", "Yes"),
        ("row-offline.applicationCache", "No"),
        ("row-offline.serviceWorkers", "Yes"),
        ("row-offline.pushMessages", "Yes"),
        ("row-offline.registerProtocolHandler", "Yes"),
        ("row-offline.registerContentHandler", "No"),
        ("row-storage.sessionStorage", "Yes"),
        ("row-storage.localStorage", "Yes"),
        ("row-storage.indexedDB.basic", "Yes"),
        ("row-storage.indexedDB.blob", "Yes"),
        ("row-storage.indexedDB.arraybuffer", "Yes"),
        ("row-storage.sqlDatabase", "No"),
        ("row-files.fileReader", "Yes"),
        ("row-files.fileReader.blob", "Yes"),
        ("row-files.fileReader.dataURL", "Yes"),
        ("row-files.fileReader.arraybuffer", "Yes"),
        ("row-files.fileReader.objectURL", "Yes"),
        ("row-files.getFileSystem", "No"),
        ("row-files.fileSystem", "No"),
        ("row-scripting.async", "Yes"),
        ("row-scripting.defer", "Yes"),
        ("row-scripting.executionevents", "Yes"),
        ("row-scripting.onerror", "Yes"),
        ("row-scripting.es5.json", "Yes"),
        ("row-scripting.es6.modules", "Yes"),
        ("row-scripting.es6.class", "Yes"),
        ("row-scripting.es6.arrow", "Yes"),
        ("row-scripting.es6.promises", "Yes"),
        ("row-scripting.es6.template", "Yes"),
        ("row-scripting.es6.datatypes", "Yes"),
        ("row-scripting.es6.i18n", "Yes"),
        ("row-scripting.es7.async", "Yes"),
        ("row-scripting.base64", "Yes"),
        ("row-scripting.mutationObserver", "Yes"),
        ("row-scripting.url", "Yes"),
        ("row-scripting.encoding", "Yes"),
        ("row-other.history", "Yes"),
        ("row-other.pagevisiblity", "Yes"),
        ("row-other.getSelection", "Yes"),
        ("row-other.scrollIntoView", "Yes")
    ]
    unexpected = []
    for (k, ev) in expected:
        tr = driver.find_element_by_id(k)
        klass = tr.get_attribute("class")
        if "hasChild" in klass and "hidden" in klass:
            driver.execute_script("arguments[0].scrollIntoView()", tr)
            tr.click()
        v = tr.find_element_by_tag_name("td").text
        print("  {}: {}".format(k, v))
        if not v.startswith(ev):
            unexpected.append((k, v, ev))

    failed = False
    if score != expected_score:
        print("Unexpected score: {}/{} (expected {}/{})"
              .format(score[0], score[1],
                      expected_score[0], expected_score[1]),
              file=sys.stderr)
        failed = True
    for k, v, ev in unexpected:
        print("Unexpected value for {}: {} (expected: {})".format(k, v, ev),
              file=sys.stderr)
        failed = True

    driver.quit()

    server.shutdown()
    server.server_close()
    server_thread.join()

    assert not failed
