Просмотр исходного кода

skipBom only in checker-mode for output, more tests

mikemirzayanov 1 год назад
Родитель
Сommit
139afc4c51

+ 1 - 2
testlib.h

@@ -3120,7 +3120,6 @@ void InStream::init(std::string fileName, TMode mode) {
     }
 
     reset();
-    skipBom();
 }
 
 void InStream::init(std::FILE *f, TMode mode) {
@@ -3136,7 +3135,6 @@ void InStream::init(std::FILE *f, TMode mode) {
         name = "stderr", stdfile = true;
 
     reset(f);
-    skipBom();
 }
 
 void InStream::skipBom() {
@@ -4627,6 +4625,7 @@ void registerTestlibCmd(int argc, char *argv[]) {
 
     inf.init(args[1], _input);
     ouf.init(args[2], _output);
+    ouf.skipBom();
     ans.init(args[3], _answer);
 }
 

+ 3 - 0
tests/test-002_run-fcmp-wcmp/files/output.01.bom

@@ -0,0 +1,3 @@
+abc 01 \%test!$
+
+me

+ 5 - 0
tests/test-002_run-fcmp-wcmp/run.sh

@@ -5,6 +5,11 @@ bash ../scripts/compile src/wcmp.cpp
 bash ../scripts/test-ref r1 "$VALGRIND" ./wcmp files/input.01 files/output.01 files/answer.01
 bash ../scripts/test-ref r2 "$VALGRIND" ./wcmp files/input.01 files/output.01 files/output.01
 bash ../scripts/test-ref r3 "$VALGRIND" ./wcmp files/input.01 files/output.01 files/answer2.01
+
+# BOM shouldn't change refs
+bash ../scripts/test-ref r1 "$VALGRIND" ./wcmp files/input.01 files/output.01.bom files/answer.01
+bash ../scripts/test-ref r2 "$VALGRIND" ./wcmp files/input.01 files/output.01.bom files/output.01
+bash ../scripts/test-ref r3 "$VALGRIND" ./wcmp files/input.01 files/output.01.bom files/answer2.01
 rm -f wcmp wcmp.exe
 
 bash ../scripts/compile src/fcmp.cpp

+ 1 - 0
tests/test-006_interactors/refs/r-interactor-a-plus-b-2-1/exit_code

@@ -0,0 +1 @@
+0

+ 0 - 0
tests/test-006_interactors/refs/r-interactor-a-plus-b-2-1/stderr


+ 3 - 0
tests/test-006_interactors/refs/r-interactor-a-plus-b-2-1/stdout

@@ -0,0 +1,3 @@
+3
+7
+-2

+ 9 - 1
tests/test-006_interactors/run.sh

@@ -10,4 +10,12 @@ bash ../scripts/compile src/interactor-a-plus-b.cpp
 bash ../scripts/test-ref r-interactor-a-plus-b-1-1 "$VALGRIND" ./interactor-a-plus-b files/"$os"/input.01 output.01 < files/"$os"/participant.01
 tr -d '\r' < output.01 > output.01.nix
 bash ../scripts/test-ref r-interactor-a-plus-b-1-2 cat output.01.nix
-rm -f interactor-a-plus-b interactor-a-plus-b.exe output.01 output.01.nix
+rm -f output.01 output.01.nix
+
+bash ../scripts/compile src/interactive-a-plus-b.cpp
+python src/interactive_runner.py ./interactor-a-plus-b files/"$os"/input.01 output.02 -- ./interactive-a-plus-b &
+sleep 1
+kill $! 2>/dev/null
+tr -d '\r' < output.02 > output.02.nix
+bash ../scripts/test-ref r-interactor-a-plus-b-2-1 cat output.02.nix
+rm -f output.02 output.02.nix interactive-a-plus-b interactive-a-plus-b.exe interactor-a-plus-b interactor-a-plus-b.exe

+ 9 - 0
tests/test-006_interactors/src/interactive-a-plus-b.cpp

@@ -0,0 +1,9 @@
+#include <bits/stdc++.h>
+
+using namespace std;
+
+int main() {
+    int a, b;
+    while (scanf("%d %d", &a, &b) == 2)
+        cout << a + b << endl;
+}

+ 122 - 0
tests/test-006_interactors/src/interactive_runner.py

@@ -0,0 +1,122 @@
+# This code can be run as python2 or python3 in most systems.
+#
+# This is a small program that runs two processes, connecting the stdin of each
+# one to the stdout of the other.
+# It doesn't perform a lot of checking, so many errors may
+# be caught internally by Python (e.g., if your command line has incorrect
+# syntax) or not caught at all (e.g., if the judge or solution hangs).
+#
+# Run this as:
+# python interactive_runner.py <cmd_line_judge> -- <cmd_line_solution>
+#
+# For example, if you have a testing_tool.py in python3 (that takes a single
+# integer as a command line parameter) to use as judge -- like one
+# downloaded from a problem statement -- and you would run your solution
+# in a standalone using one of the following:
+#   1. python3 my_solution.py
+#   2. ./my_solution
+#   3. java Solution
+#   4. my_solution.exe
+# Then you could run the judge and solution together, using this, as:
+#   1. python interactive_runner.py python3 testing_tool.py 0 -- python3 my_solution.py
+#   2. python interactive_runner.py python3 testing_tool.py 0 -- ./my_solution
+#   3. python interactive_runner.py python3 testing_tool.py 0 -- java solution
+#   4. python interactive_runner.py python3 testing_tool.py 0 -- my_solution.exe
+# Notice that the solution in cases 2, 3 and 4 would usually have a
+# compilation step before running, which you should run in your usual way
+# before using this tool.
+#
+# This is only intended as a convenient tool to help contestants test solutions
+# locally. In particular, it is not identical to the implementation on our
+# server, which is more complex.
+#
+# The standard streams are handled the following way:
+# - judge's stdin is connected to the solution's stdout;
+# - judge's stdout is connected to the solution's stdin;
+# - stderrs of both judge and solution are piped to standard error stream, with
+#   lines prepended by "judge: " or "sol: " respectively (note, no
+#   synchronization is done so it's possible for the messages from both programs
+#   to overlap with each other).
+
+from __future__ import print_function
+import sys, subprocess, threading
+
+class SubprocessThread(threading.Thread):
+  def __init__(self,
+               args,
+               stdin_pipe=subprocess.PIPE,
+               stdout_pipe=subprocess.PIPE,
+               stderr_prefix=None):
+    threading.Thread.__init__(self)
+    self.stderr_prefix = stderr_prefix
+    self.p = subprocess.Popen(
+        args, stdin=stdin_pipe, stdout=stdout_pipe, stderr=subprocess.PIPE)
+
+  def run(self):
+    try:
+      self.pipeToStdErr(self.p.stderr)
+      self.return_code = self.p.wait()
+      self.error_message = None
+    except (SystemError, OSError):
+      self.return_code = -1
+      self.error_message = "The process crashed or produced too much output."
+
+  # Reads bytes from the stream and writes them to sys.stderr prepending lines
+  # with self.stderr_prefix.
+  # We are not reading by lines to guard against the case when EOL is never
+  # found in the stream.
+  def pipeToStdErr(self, stream):
+    new_line = True
+    while True:
+      chunk = stream.readline(1)
+      if not chunk:
+        return
+      chunk = chunk.decode("UTF-8")
+      if new_line and self.stderr_prefix:
+        chunk = self.stderr_prefix + chunk
+        new_line = False
+      sys.stderr.write(chunk)
+      if chunk.endswith("\n"):
+        new_line = True
+      sys.stderr.flush()
+
+
+assert sys.argv.count("--") == 1, (
+    "There should be exactly one instance of '--' in the command line.")
+sep_index = sys.argv.index("--")
+judge_args = sys.argv[1:sep_index]
+sol_args = sys.argv[sep_index + 1:]
+
+t_sol = SubprocessThread(sol_args, stderr_prefix="  sol: ")
+t_judge = SubprocessThread(
+    judge_args,
+    stdin_pipe=t_sol.p.stdout,
+    stdout_pipe=t_sol.p.stdin,
+    stderr_prefix="judge: ")
+t_sol.start()
+t_judge.start()
+t_sol.join()
+t_judge.join()
+
+# Print an empty line to handle the case when stderr doesn't print EOL.
+print()
+print("Judge return code:", t_judge.return_code)
+if t_judge.error_message:
+  print("Judge error message:", t_judge.error_message)
+
+print("Solution return code:", t_sol.return_code)
+if t_sol.error_message:
+  print("Solution error message:", t_sol.error_message)
+
+if t_sol.return_code:
+  print("A solution finishing with exit code other than 0 (without exceeding "
+        "time or memory limits) would be interpreted as a Runtime Error "
+        "in the system.")
+elif t_judge.return_code:
+  print("A solution finishing with exit code 0 (without exceeding time or "
+        "memory limits) and a judge finishing with exit code other than 0 "
+        "would be interpreted as a Wrong Answer in the system.")
+else:
+  print("A solution and judge both finishing with exit code 0 (without "
+        "exceeding time or memory limits) would be interpreted as Correct "
+        "in the system.")