您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在 TopCoder 网页端提交后可以看到测试点详情。
// ==UserScript== // @name TopCoder tests viewer // @name:zh-CN TopCoder tests viewer // @namespace https://github.com/platelett/script // @version 0.1 // @description After submitting on the TopCoder web client, you can see the details of the tests. // @description:zh-CN 在 TopCoder 网页端提交后可以看到测试点详情。 // @author platelet // @match https://arena.topcoder.com/* // @icon https://www.google.com/s2/favicons?domain=topcoder.com // @grant none // @license MIT // ==/UserScript== var wsHook = {}; function loadWsHook(){ /* wsHook.js * https://github.com/skepticfx/wshook * Reference: http://www.w3.org/TR/2011/WD-websockets-20110419/#websocket */ (function () { // Mutable MessageEvent. // Subclasses MessageEvent and makes data, origin and other MessageEvent properites mutatble. function MutableMessageEvent (o) { this.bubbles = o.bubbles || false this.cancelBubble = o.cancelBubble || false this.cancelable = o.cancelable || false this.currentTarget = o.currentTarget || null this.data = o.data || null this.defaultPrevented = o.defaultPrevented || false this.eventPhase = o.eventPhase || 0 this.lastEventId = o.lastEventId || '' this.origin = o.origin || '' this.path = o.path || new Array(0) this.ports = o.parts || new Array(0) this.returnValue = o.returnValue || true this.source = o.source || null this.srcElement = o.srcElement || null this.target = o.target || null this.timeStamp = o.timeStamp || null this.type = o.type || 'message' this.__proto__ = o.__proto__ || MessageEvent.__proto__ } var before = wsHook.before = function (data, url, wsObject) { return data } var after = wsHook.after = function (e, url, wsObject) { return e } var modifyUrl = wsHook.modifyUrl = function(url) { return url } wsHook.resetHooks = function () { wsHook.before = before wsHook.after = after wsHook.modifyUrl = modifyUrl } var _WS = WebSocket WebSocket = function (url, protocols) { var WSObject url = wsHook.modifyUrl(url) || url this.url = url this.protocols = protocols if (!this.protocols) { WSObject = new _WS(url) } else { WSObject = new _WS(url, protocols) } var _send = WSObject.send WSObject.send = function (data) { arguments[0] = wsHook.before(data, WSObject.url, WSObject) || data _send.apply(this, arguments) } // Events needs to be proxied and bubbled down. WSObject._addEventListener = WSObject.addEventListener WSObject.addEventListener = function () { var eventThis = this // if eventName is 'message' if (arguments[0] === 'message') { arguments[1] = (function (userFunc) { return function instrumentAddEventListener () { arguments[0] = wsHook.after(new MutableMessageEvent(arguments[0]), WSObject.url, WSObject) if (arguments[0] === null) return userFunc.apply(eventThis, arguments) } })(arguments[1]) } return WSObject._addEventListener.apply(this, arguments) } Object.defineProperty(WSObject, 'onmessage', { 'set': function () { var eventThis = this var userFunc = arguments[0] var onMessageHandler = function () { arguments[0] = wsHook.after(new MutableMessageEvent(arguments[0]), WSObject.url, WSObject) if (arguments[0] === null) return userFunc.apply(eventThis, arguments) } WSObject._addEventListener.apply(this, ['message', onMessageHandler, false]) } }) return WSObject } })() } var container var timeLimit, memoryLimit, multiArgs var numberOfTests, runningIndex, maxTime, maxMemory, totTime var results, firstFail var blocks = new Array() function append(parent, html) { var element = document.createElement("div") element.innerHTML = html return parent.appendChild(element.children[0]) } function initialize() { maxTime = maxMemory = totTime = 0, runningIndex = 1 results = new Array(numberOfTests) firstFail = 0, blocks.length = 0 var temp = document.querySelectorAll("#top-content > div:nth-child(1) > div > div:nth-child(11) > div") timeLimit = parseFloat(temp[1].textContent) * (temp[0].textContent == "Time limit (s):" ? 1000 : 1) temp = document.querySelectorAll("#top-content > div:nth-child(1) > div > div:nth-child(12) > div") memoryLimit = parseFloat(temp[1].textContent) * (temp[0].textContent == "Memory limit (MB):" ? 1024 : 1) multiArgs = document.querySelector("#top-content > div:nth-child(1) > div > div:nth-child(6) > div.textWhite.ng-binding").textContent.indexOf(',') != -1 console.log(timeLimit, memoryLimit, document.querySelector("#top-content > div:nth-child(1) > div > div:nth-child(6) > div.textWhite.ng-binding").textContent) } function loadContainer() { if (container) document.body.removeChild(container) container = append(document.body, ` <div style="background: white; padding-top: 50px; padding-left: 50px; font-size: 48px; font-family: verdana, arial, sans-serif; color: black;"> <span style="color: #808080; line-height: 50px;" id="verdict">Running on test 1</span> (${numberOfTests} test cases) <br><br> <table style="line-height: 50px;" id="overview"><tbody> <tr><td>Total Time</td><td>Max Time</td><td>Max Memory</td></tr> <tr><td id="totTime">0 ms</td><td id="maxTime">0 ms</td><td id="memory">0 KB</td></tr> </tbody></table> </div> `) } function memoryString(value) { return value < 1024 ? `${value} KB` : `${(value / 1024).toFixed(2)} MB` } function printData(ctr, name, data) { var index = blocks.length blocks.push(data) var button = append(ctr, ` <div> <span style="font-size: 24px; vertical-align: middle; line-height: 50px;">${name}</span> <span style="font-weight: bold; color: navy; cursor: pointer; user-select: none !important; font-family: 'Lato', 'Helvetica Neue', arial, sans-serif; vertical-align: middle;">[Copy full data]</span> <br> <div class="codeblock">${data.length <= 80 ? data : data.slice(0, 77) + "..."}</div> </div> `).children[1] button.onclick = () => navigator.clipboard.writeText(blocks[index]).then(() => { button.innerHTML = "[Copied]" setTimeout(() => button.innerHTML = "[Copy full data]", 500) }, () => alert("Copy failed!")); } function dataString(data) { var args = JSON.stringify(data).split("") var inq = false for (let i in args) { if (args[i] == '"') inq = !inq if (inq) continue if (args[i] == '[') args[i] = '{' if (args[i] == ']') args[i] = '}' } return args.join("") } function getVerdict(result) { if (result.succeeded) return "Accepted" if ("checkAnswerResponse" in result) return "Wrong answer" if (result.message.startsWith("The code execution time exceeded")) return "Time limit exceeded" if (result.message.startsWith("sandbox did not finish on time")) return "Time limit exceeded" if (result.message.startsWith("abnormal termination")) return "Runtime error" if (result.message.startsWith("segmentation fault")) return "Runtime error" if (result.message.startsWith("caught signal")) return "Runtime error" return "Unknown error (possibly MLE)" } function printResult(result) { var verdict = getVerdict(result) var ctr = append(container, ` <span style="font-size: 18px"> <br> <span style="font-size: 30px; color: #3B5998">#Test: ${result.testCaseIndex}</span> <br> <span style="font-size: 24px; color: ${verdict == "Accepted" ? "#00AA00" : "#0000AA"};">${verdict}</span> <br> <strong>Time</strong>: ${result.execTime} ms, <strong>Memory</strong>: ${memoryString(result.maxMemoryUsed)} <br> </span> `) printData(ctr, "Input", dataString(multiArgs ? result.args.slice(1, -1) : result.args)) if ("returnValue" in result) printData(ctr, "Output", dataString(result.returnValue)) if (verdict != "Accepted") printData(ctr, "Answer", dataString(result.expectedValue)) if ("checkAnswerResponse" in result) printData(ctr, "Message", result.checkAnswerResponse) else if ("message" in result) printData(ctr, "Message", result.message) } function printResults() { var state = document.querySelector("#verdict") state.style["font-weight"] = "bold" if (!firstFail) { state.innerHTML = "Accepted", state.style.color = "#00AA00" for (const a of results) if(a) printResult(a) } else { state.innerHTML = `${getVerdict(results[firstFail - 1])} on test ${firstFail}` state.style.color = "#0000AA" for (const a of results) if (a && !a.succeeded) printResult(a) for (const a of results) if (a && a.succeeded) printResult(a) } container.scrollIntoView(); } function addResult(result) { console.log(result) results[result.testCaseIndex++] = result if (getVerdict(result) == "Time limit exceeded") result.execTime = timeLimit; maxTime = Math.max(maxTime, result.execTime) maxMemory = Math.max(maxMemory, result.maxMemoryUsed) totTime += result.execTime document.querySelector("#maxTime").innerHTML = `${maxTime} ms` document.querySelector("#totTime").innerHTML = `${totTime} ms` document.querySelector("#memory").innerHTML = memoryString(maxMemory) if (firstFail) return if (!result.succeeded) { firstFail = result.testCaseIndex setTimeout(printResults, 200) } if (++runningIndex > numberOfTests) printResults() else document.querySelector("#verdict").innerHTML = `Running on test ${runningIndex}` } function handleArenaEvent(event) { if (event.name == "PracticeSystemTestResponse") { numberOfTests = Object.values(event.args[0].testCaseCountByComponentId)[0] initialize(), loadContainer(), container.scrollIntoView() } if (event.name == "PracticeSystemTestResultResponse") addResult(event.args[0].resultData) } (function() { onload = () => append(document.head, ` <style type="text/css"> .codeblock { height: 35px; width: 1000px; background: #EEEEEE; padding-top: 5px; padding-left: 10px; font-size: 24px; font-family: monospace; color: black; } #overview { width: 1000px; min-height: 140px; text-align: center; border-collapse: collapse; } #overview, #overview td { border: 5px solid; } </style> `) loadWsHook() wsHook.after = function(messageEvent, url, wsObject) { if (messageEvent.data.startsWith("5:::")) { handleArenaEvent(JSON.parse(messageEvent.data.slice(4))) } return messageEvent } })();