From 20b8d7f4021490aacb78c52f8edd93d193629a55 Mon Sep 17 00:00:00 2001
From: Mateusz Masiarz <matimasiarz@gmail.com>
Date: Mon, 9 Sep 2024 13:28:21 +0200
Subject: [PATCH 1/2] Use oicompare as default checker

---
 src/sinol_make/__init__.py           |  3 +-
 src/sinol_make/helpers/oicompare.py  | 56 ++++++++++++++++++++++++++++
 src/sinol_make/task_type/__init__.py | 21 ++++++++++-
 tests/conftest.py                    |  4 +-
 4 files changed, 81 insertions(+), 3 deletions(-)
 create mode 100644 src/sinol_make/helpers/oicompare.py

diff --git a/src/sinol_make/__init__.py b/src/sinol_make/__init__.py
index 4603ba6a..ae077244 100644
--- a/src/sinol_make/__init__.py
+++ b/src/sinol_make/__init__.py
@@ -5,7 +5,7 @@
 import argcomplete
 
 from sinol_make import util, sio2jail
-from sinol_make.helpers import cache
+from sinol_make.helpers import cache, oicompare
 
 # Required for side effects
 from sinol_make.task_type.normal import NormalTaskType # noqa
@@ -76,6 +76,7 @@ def main_exn():
         parser.print_help()
         exit(1)
     check_sio2jail()
+    oicompare.check_and_download()
 
     for curr_args in arguments:
         args = parser.parse_args(curr_args)
diff --git a/src/sinol_make/helpers/oicompare.py b/src/sinol_make/helpers/oicompare.py
new file mode 100644
index 00000000..e72f8f88
--- /dev/null
+++ b/src/sinol_make/helpers/oicompare.py
@@ -0,0 +1,56 @@
+import os
+import requests
+import subprocess
+
+from sinol_make import util
+
+
+__OICOMAPRE_VERSION = 'v1.0.2'
+
+
+def get_path():
+    return os.path.expanduser('~/.local/bin/oicompare')
+
+
+def check_installed():
+    path = get_path()
+    if not os.path.exists(path):
+        return False
+    try:
+        output = subprocess.run([path, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    except PermissionError:
+        return False
+    if output.returncode != 0:
+        return False
+    if output.stdout.decode().strip() != f'oicompare version {__OICOMAPRE_VERSION[1:]}':
+        return False
+    return True
+
+
+def download_oicomapare():
+    url = f'https://github.com/sio2project/oicompare/releases/download/{__OICOMAPRE_VERSION}/oicompare'
+    if util.is_macos_arm():
+        url += '-arm64'
+    path = get_path()
+    os.makedirs(os.path.dirname(path), exist_ok=True)
+    manual_download_msg = ("You can try downloading it manually from "
+                           f"https://github.com/sio2project/oicompare/releases/tag/{__OICOMAPRE_VERSION}/ and placing "
+                           f"it in ~/.local/bin/oicomapre")
+    try:
+        request = requests.get(url)
+    except requests.exceptions.ConnectionError:
+        util.exit_with_error("Couldn't download oicompare (couldn't connect). " + manual_download_msg)
+    if request.status_code != 200:
+        util.exit_with_error(f"Couldn't download oicompare (returned status code: {request.status_code}). "
+                            + manual_download_msg)
+    with open(path, 'wb') as f:
+        f.write(request.content)
+    os.chmod(path, 0o755)
+
+
+def check_and_download():
+    if check_installed():
+        return
+    download_oicomapare()
+    if not check_installed():
+        util.exit_with_error("Couldn't download oicompare. Please try again later or download it manually.")
diff --git a/src/sinol_make/task_type/__init__.py b/src/sinol_make/task_type/__init__.py
index e37c20c9..ca02a86c 100644
--- a/src/sinol_make/task_type/__init__.py
+++ b/src/sinol_make/task_type/__init__.py
@@ -6,7 +6,7 @@
 from sinol_make import util
 from sinol_make.executors.sio2jail import Sio2jailExecutor
 from sinol_make.executors.time import TimeExecutor
-from sinol_make.helpers import package_util, paths, cache
+from sinol_make.helpers import package_util, paths, cache, oicompare
 from sinol_make.helpers.classinit import RegisteredSubclassesBase
 from sinol_make.interfaces.Errors import CheckerException
 from sinol_make.structs.status_structs import ExecutionResult
@@ -162,6 +162,23 @@ def _run_diff(self, output_file_path, answer_file_path) -> Tuple[bool, Fraction,
         else:
             return False, Fraction(0, 1), ""
 
+    def _run_oicompare(self, output_file_path, answer_file_path) -> Tuple[bool, Fraction, str]:
+        path = oicompare.get_path()
+        proc = subprocess.Popen([path, output_file_path, answer_file_path, 'english_abbreviated'],
+                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        proc.wait()
+        output, stderr = proc.communicate()
+        if proc.returncode == 0:
+            return True, Fraction(100, 1), ""
+        elif proc.returncode == 1:
+            return False, Fraction(0, 1), output.decode('utf-8').strip()
+        else:
+            raise CheckerException(f"!!! oicompare failed with code {proc.returncode}. This is a huge bug, please report"
+                                   f" it here https://github.com/sio2project/sinol-make/issues/new/choose and provide "
+                                   f"these files: {output_file_path}, {answer_file_path}.\n"
+                                   f"Output: {output.decode('utf-8').strip()}\n"
+                                   f"Stderr: {stderr.decode('utf-8').strip()}")
+
     def check_output(self, input_file_path, output_file_path, answer_file_path) -> Tuple[bool, Fraction, str]:
         """
         Runs the checker (or runs diff) and returns a tuple of three values:
@@ -171,6 +188,8 @@ def check_output(self, input_file_path, output_file_path, answer_file_path) -> T
         """
         if self.has_checker:
             return self._run_checker(input_file_path, output_file_path, answer_file_path)
+        elif oicompare.check_installed():
+            return self._run_oicompare(output_file_path, answer_file_path)
         else:
             return self._run_diff(output_file_path, answer_file_path)
 
diff --git a/tests/conftest.py b/tests/conftest.py
index 4998aaf3..9683c6fe 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -8,7 +8,7 @@
 import multiprocessing as mp
 
 from sinol_make import util
-from sinol_make.helpers import compile, paths, cache
+from sinol_make.helpers import compile, paths, cache, oicompare
 from sinol_make.interfaces.Errors import CompilationError
 
 
@@ -91,6 +91,8 @@ def pytest_configure(config):
             except FileNotFoundError:
                 pass
 
+    oicompare.check_and_download()
+
 
 def pytest_generate_tests(metafunc):
     if "time_tool" in metafunc.fixturenames:

From b31890e1afe760f8e4a60f55151b9d933688e3bb Mon Sep 17 00:00:00 2001
From: Mateusz Masiarz <matimasiarz@gmail.com>
Date: Mon, 9 Sep 2024 14:21:46 +0200
Subject: [PATCH 2/2] Bump version

---
 src/sinol_make/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sinol_make/__init__.py b/src/sinol_make/__init__.py
index ae077244..a7b99549 100644
--- a/src/sinol_make/__init__.py
+++ b/src/sinol_make/__init__.py
@@ -12,7 +12,7 @@
 from sinol_make.task_type.interactive import InteractiveTaskType # noqa
 
 
-__version__ = "1.8.2"
+__version__ = "1.8.3"
 
 
 def configure_parsers():