diff --git a/samps/contests/pointscoring/README.txt b/samps/contests/pointscoring/README.txt new file mode 100644 index 000000000..1f25f4a93 --- /dev/null +++ b/samps/contests/pointscoring/README.txt @@ -0,0 +1,40 @@ +This folder contains the configuration for a PC2 sample "point scoring" contest. + +The contest has a "problemset.yaml" file containing descriptions of three problems: 'a', 'b', and 'c'. +The intent is to (eventually) configure a variety of different problems, each with different "scoring properties". +Currently however, problem folders 'b' and 'c' contain duplicates of problem 'a' (except with the appropriate problem names changed). + +The intent is to expand the contest by modifying the scoring attributes of problems 'b' and 'c' so that they test various other + "point scoring" attribute combinations. (Eventually of course there may be additional combinations, requiring addition of new problems 'd', 'e', etc.) + +All problems are intended to be based on the PC2 "Sumit" problem ("read input integers and print the sum of the positive integers"). +This allows for easy testing, since there already exist correct and incorrect solutions to Sumit (in several languages) in the PC2 "samps/src" folder. + +Problem 'a' has the following attributes: + - a problem_statement folder with a "problem.tex" file describing the PC2 "Sumit" problem. + - a problem.yaml file stating that the problem named "a" is "type:scoring". + - a submissions folder containing accepted, "partially accepted" and "wrong answer" solutions for the Sumit problem. + (The "partially accepted" submission prints the sum of ALL integers in the input; i.e., it fails to ignore negative integers. + This could be used for example to give "partial credit" for such a solution.) + - both "sample" and "secret" data folders, but only with test data (.in/.ans pairs) at the root level (i.e., no scoring subgroups) + - no "testdata.yaml" files at any level -- hence, tests the premise that if there is no testdata.yaml file, then a "testdata.yaml" + file is implicitly added to the root ("data") group (see https://www.kattis.com/problem-package-format/spec/legacy.html#test-data-groups) + with default values, which are: + - on_reject: break + - grading: default + - grader_flags: "" (i.e. the empty string) + - input_validator_flags: "" + - output_validator_flags: "" + - accept_score: 1.0 + - reject_score: 0.0 + - range: -inf +inf + - Since "grader_flags" defaults to the empty string, the Grader will implicitly use the following default values + (see https://www.kattis.com/problem-package-format/spec/legacy.html#default-grader-specification): + - verdict mode: worst_error + - scoring mode: sum + +Problems 'b' and 'c' are currently just duplicates of problem 'a'; they need to be modified to test other point-scoring combinations + (for example, the use of scoring subgroups; the use of different grader flags, etc.) + + + \ No newline at end of file diff --git a/samps/contests/pointscoring/config/a/data/sample/01.ans b/samps/contests/pointscoring/config/a/data/sample/01.ans new file mode 100644 index 000000000..15f395758 --- /dev/null +++ b/samps/contests/pointscoring/config/a/data/sample/01.ans @@ -0,0 +1 @@ +The sum of the integers is 75 diff --git a/samps/contests/pointscoring/config/a/data/sample/01.in b/samps/contests/pointscoring/config/a/data/sample/01.in new file mode 100644 index 000000000..ad7b842a9 --- /dev/null +++ b/samps/contests/pointscoring/config/a/data/sample/01.in @@ -0,0 +1,3 @@ +25 +50 +0 diff --git a/samps/contests/pointscoring/config/a/data/secret/02.ans b/samps/contests/pointscoring/config/a/data/secret/02.ans new file mode 100644 index 000000000..15f395758 --- /dev/null +++ b/samps/contests/pointscoring/config/a/data/secret/02.ans @@ -0,0 +1 @@ +The sum of the integers is 75 diff --git a/samps/contests/pointscoring/config/a/data/secret/02.in b/samps/contests/pointscoring/config/a/data/secret/02.in new file mode 100644 index 000000000..f5669d925 --- /dev/null +++ b/samps/contests/pointscoring/config/a/data/secret/02.in @@ -0,0 +1,4 @@ +25 +50 +-25 +0 diff --git a/samps/contests/pointscoring/config/a/data/secret/03.ans b/samps/contests/pointscoring/config/a/data/secret/03.ans new file mode 100644 index 000000000..1f1acd032 --- /dev/null +++ b/samps/contests/pointscoring/config/a/data/secret/03.ans @@ -0,0 +1 @@ +The sum of the integers is 50 diff --git a/samps/contests/pointscoring/config/a/data/secret/03.in b/samps/contests/pointscoring/config/a/data/secret/03.in new file mode 100644 index 000000000..29643f4ed --- /dev/null +++ b/samps/contests/pointscoring/config/a/data/secret/03.in @@ -0,0 +1,4 @@ +25 +25 +-25 +0 diff --git a/samps/contests/pointscoring/config/a/problem.yaml b/samps/contests/pointscoring/config/a/problem.yaml new file mode 100644 index 000000000..21ab2ae25 --- /dev/null +++ b/samps/contests/pointscoring/config/a/problem.yaml @@ -0,0 +1,12 @@ +# Point Scoring problem configuration, problem 'a', version 1.0 +--- + +name: a +type: scoring + +limits: + timeout: 1 + +input: + readFromSTDIN: true + diff --git a/samps/contests/pointscoring/config/a/problem_statement/problem.tex b/samps/contests/pointscoring/config/a/problem_statement/problem.tex new file mode 100644 index 000000000..f2b891eb6 --- /dev/null +++ b/samps/contests/pointscoring/config/a/problem_statement/problem.tex @@ -0,0 +1,7 @@ +\problemtitle{Sumit a} + +\begin{document +\section{Problem Statement} +Write a program which reads a series of integers from stdin and prints a message "The sum of the integers is XXX", where XXX is the sum of the positive +integers in the input. +\end{document} \ No newline at end of file diff --git a/samps/contests/pointscoring/config/a/submissions/accepted/ISumit.java b/samps/contests/pointscoring/config/a/submissions/accepted/ISumit.java new file mode 100644 index 000000000..38447a7a6 --- /dev/null +++ b/samps/contests/pointscoring/config/a/submissions/accepted/ISumit.java @@ -0,0 +1,29 @@ + +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +import java.io.*; + +// +// File: ISumit.java +// Purpose: to print the sum of the positive integers from stdin + +public class ISumit { + public static void main(String[] args) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1); + + String line; + int sum = 0; + int rv = 0; + while ((line = br.readLine()) != null) { + rv = new Integer(line.trim()).intValue(); + if (rv > 0) + sum = sum + rv; + } + System.out.print("The sum of the integers is "); + System.out.println(sum); + } catch (Exception e) { + System.out.println("Possible trouble reading stdin"); + System.out.println("Message: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/samps/contests/pointscoring/config/a/submissions/partially_accepted/ISumitPartial.java b/samps/contests/pointscoring/config/a/submissions/partially_accepted/ISumitPartial.java new file mode 100644 index 000000000..1574be0a7 --- /dev/null +++ b/samps/contests/pointscoring/config/a/submissions/partially_accepted/ISumitPartial.java @@ -0,0 +1,28 @@ + +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +import java.io.*; + +// +// File: ISumitPartial.java +// Purpose: to print the sum of the integers from stdin but failing to ignore negative numbers + +public class ISumitPartial { + public static void main(String[] args) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1); + + String line; + int sum = 0; + int rv = 0; + while ((line = br.readLine()) != null) { + rv = new Integer(line.trim()).intValue(); + sum = sum + rv; + } + System.out.print("The sum of the integers is "); + System.out.println(sum); + } catch (Exception e) { + System.out.println("Possible trouble reading stdin"); + System.out.println("Message: " + e.getMessage()); + } + } +} diff --git a/samps/contests/pointscoring/config/a/submissions/wrong_answer/ISumitWA.java b/samps/contests/pointscoring/config/a/submissions/wrong_answer/ISumitWA.java new file mode 100644 index 000000000..3c6030fe4 --- /dev/null +++ b/samps/contests/pointscoring/config/a/submissions/wrong_answer/ISumitWA.java @@ -0,0 +1,30 @@ + +// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +import java.io.*; + +// +// File: ISumitWA.java +// Purpose: to sum the integers from stdin but print an incorrect sum +// (that is, to fail to correctly print the sum of the positive integers from stdin) + +public class ISumitWA { + public static void main(String[] args) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1); + + String line; + int sum = 0; + int rv = 0; + while ((line = br.readLine()) != null) { + rv = new Integer(line.trim()).intValue(); + if (rv > 0) + sum = sum + rv; + } + System.out.print("Dumn integers is "); + System.out.println(sum + 1024); + } catch (Exception e) { + System.out.println("Possible trouble reading stdin"); + System.out.println("Message: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/samps/contests/pointscoring/config/b/data/sample/01.ans b/samps/contests/pointscoring/config/b/data/sample/01.ans new file mode 100644 index 000000000..15f395758 --- /dev/null +++ b/samps/contests/pointscoring/config/b/data/sample/01.ans @@ -0,0 +1 @@ +The sum of the integers is 75 diff --git a/samps/contests/pointscoring/config/b/data/sample/01.in b/samps/contests/pointscoring/config/b/data/sample/01.in new file mode 100644 index 000000000..ad7b842a9 --- /dev/null +++ b/samps/contests/pointscoring/config/b/data/sample/01.in @@ -0,0 +1,3 @@ +25 +50 +0 diff --git a/samps/contests/pointscoring/config/b/data/secret/02.ans b/samps/contests/pointscoring/config/b/data/secret/02.ans new file mode 100644 index 000000000..15f395758 --- /dev/null +++ b/samps/contests/pointscoring/config/b/data/secret/02.ans @@ -0,0 +1 @@ +The sum of the integers is 75 diff --git a/samps/contests/pointscoring/config/b/data/secret/02.in b/samps/contests/pointscoring/config/b/data/secret/02.in new file mode 100644 index 000000000..f5669d925 --- /dev/null +++ b/samps/contests/pointscoring/config/b/data/secret/02.in @@ -0,0 +1,4 @@ +25 +50 +-25 +0 diff --git a/samps/contests/pointscoring/config/b/data/secret/03.ans b/samps/contests/pointscoring/config/b/data/secret/03.ans new file mode 100644 index 000000000..1f1acd032 --- /dev/null +++ b/samps/contests/pointscoring/config/b/data/secret/03.ans @@ -0,0 +1 @@ +The sum of the integers is 50 diff --git a/samps/contests/pointscoring/config/b/data/secret/03.in b/samps/contests/pointscoring/config/b/data/secret/03.in new file mode 100644 index 000000000..29643f4ed --- /dev/null +++ b/samps/contests/pointscoring/config/b/data/secret/03.in @@ -0,0 +1,4 @@ +25 +25 +-25 +0 diff --git a/samps/contests/pointscoring/config/b/problem.yaml b/samps/contests/pointscoring/config/b/problem.yaml new file mode 100644 index 000000000..ab4596f68 --- /dev/null +++ b/samps/contests/pointscoring/config/b/problem.yaml @@ -0,0 +1,12 @@ +# Point Scoring problem configuration, problem 'b', version 1.0 (a duplicate of problem 'a') +--- + +name: b +type: scoring + +limits: + timeout: 1 + +input: + readFromSTDIN: true + diff --git a/samps/contests/pointscoring/config/b/problem_statement/problem.tex b/samps/contests/pointscoring/config/b/problem_statement/problem.tex new file mode 100644 index 000000000..bb7e4cfc7 --- /dev/null +++ b/samps/contests/pointscoring/config/b/problem_statement/problem.tex @@ -0,0 +1,7 @@ +\problemtitle{Sumit b} + +\begin{document +\section{Problem Statement} +Write a program which reads a series of integers from stdin and prints a message "The sum of the integers is XXX", where XXX is the sum of the positive +integers in the input. +\end{document} \ No newline at end of file diff --git a/samps/contests/pointscoring/config/b/submissions/accepted/ISumit.java b/samps/contests/pointscoring/config/b/submissions/accepted/ISumit.java new file mode 100644 index 000000000..38447a7a6 --- /dev/null +++ b/samps/contests/pointscoring/config/b/submissions/accepted/ISumit.java @@ -0,0 +1,29 @@ + +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +import java.io.*; + +// +// File: ISumit.java +// Purpose: to print the sum of the positive integers from stdin + +public class ISumit { + public static void main(String[] args) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1); + + String line; + int sum = 0; + int rv = 0; + while ((line = br.readLine()) != null) { + rv = new Integer(line.trim()).intValue(); + if (rv > 0) + sum = sum + rv; + } + System.out.print("The sum of the integers is "); + System.out.println(sum); + } catch (Exception e) { + System.out.println("Possible trouble reading stdin"); + System.out.println("Message: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/samps/contests/pointscoring/config/b/submissions/partially_accepted/ISumitPartial.java b/samps/contests/pointscoring/config/b/submissions/partially_accepted/ISumitPartial.java new file mode 100644 index 000000000..1574be0a7 --- /dev/null +++ b/samps/contests/pointscoring/config/b/submissions/partially_accepted/ISumitPartial.java @@ -0,0 +1,28 @@ + +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +import java.io.*; + +// +// File: ISumitPartial.java +// Purpose: to print the sum of the integers from stdin but failing to ignore negative numbers + +public class ISumitPartial { + public static void main(String[] args) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1); + + String line; + int sum = 0; + int rv = 0; + while ((line = br.readLine()) != null) { + rv = new Integer(line.trim()).intValue(); + sum = sum + rv; + } + System.out.print("The sum of the integers is "); + System.out.println(sum); + } catch (Exception e) { + System.out.println("Possible trouble reading stdin"); + System.out.println("Message: " + e.getMessage()); + } + } +} diff --git a/samps/contests/pointscoring/config/b/submissions/wrong_answer/ISumitWA.java b/samps/contests/pointscoring/config/b/submissions/wrong_answer/ISumitWA.java new file mode 100644 index 000000000..3c6030fe4 --- /dev/null +++ b/samps/contests/pointscoring/config/b/submissions/wrong_answer/ISumitWA.java @@ -0,0 +1,30 @@ + +// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +import java.io.*; + +// +// File: ISumitWA.java +// Purpose: to sum the integers from stdin but print an incorrect sum +// (that is, to fail to correctly print the sum of the positive integers from stdin) + +public class ISumitWA { + public static void main(String[] args) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1); + + String line; + int sum = 0; + int rv = 0; + while ((line = br.readLine()) != null) { + rv = new Integer(line.trim()).intValue(); + if (rv > 0) + sum = sum + rv; + } + System.out.print("Dumn integers is "); + System.out.println(sum + 1024); + } catch (Exception e) { + System.out.println("Possible trouble reading stdin"); + System.out.println("Message: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/samps/contests/pointscoring/config/c/data/sample/01.ans b/samps/contests/pointscoring/config/c/data/sample/01.ans new file mode 100644 index 000000000..15f395758 --- /dev/null +++ b/samps/contests/pointscoring/config/c/data/sample/01.ans @@ -0,0 +1 @@ +The sum of the integers is 75 diff --git a/samps/contests/pointscoring/config/c/data/sample/01.in b/samps/contests/pointscoring/config/c/data/sample/01.in new file mode 100644 index 000000000..ad7b842a9 --- /dev/null +++ b/samps/contests/pointscoring/config/c/data/sample/01.in @@ -0,0 +1,3 @@ +25 +50 +0 diff --git a/samps/contests/pointscoring/config/c/data/secret/02.ans b/samps/contests/pointscoring/config/c/data/secret/02.ans new file mode 100644 index 000000000..15f395758 --- /dev/null +++ b/samps/contests/pointscoring/config/c/data/secret/02.ans @@ -0,0 +1 @@ +The sum of the integers is 75 diff --git a/samps/contests/pointscoring/config/c/data/secret/02.in b/samps/contests/pointscoring/config/c/data/secret/02.in new file mode 100644 index 000000000..f5669d925 --- /dev/null +++ b/samps/contests/pointscoring/config/c/data/secret/02.in @@ -0,0 +1,4 @@ +25 +50 +-25 +0 diff --git a/samps/contests/pointscoring/config/c/data/secret/03.ans b/samps/contests/pointscoring/config/c/data/secret/03.ans new file mode 100644 index 000000000..1f1acd032 --- /dev/null +++ b/samps/contests/pointscoring/config/c/data/secret/03.ans @@ -0,0 +1 @@ +The sum of the integers is 50 diff --git a/samps/contests/pointscoring/config/c/data/secret/03.in b/samps/contests/pointscoring/config/c/data/secret/03.in new file mode 100644 index 000000000..29643f4ed --- /dev/null +++ b/samps/contests/pointscoring/config/c/data/secret/03.in @@ -0,0 +1,4 @@ +25 +25 +-25 +0 diff --git a/samps/contests/pointscoring/config/c/problem.yaml b/samps/contests/pointscoring/config/c/problem.yaml new file mode 100644 index 000000000..bcf06400a --- /dev/null +++ b/samps/contests/pointscoring/config/c/problem.yaml @@ -0,0 +1,12 @@ +# Point Scoring problem configuration, problem 'c', version 1.0 (currently a duplicat of problem 'a') +--- + +name: c +type: scoring + +limits: + timeout: 1 + +input: + readFromSTDIN: true + diff --git a/samps/contests/pointscoring/config/c/problem_statement/problem.tex b/samps/contests/pointscoring/config/c/problem_statement/problem.tex new file mode 100644 index 000000000..3b8088aab --- /dev/null +++ b/samps/contests/pointscoring/config/c/problem_statement/problem.tex @@ -0,0 +1,7 @@ +\problemtitle{Sumit c} + +\begin{document +\section{Problem Statement} +Write a program which reads a series of integers from stdin and prints a message "The sum of the integers is XXX", where XXX is the sum of the positive +integers in the input. +\end{document} \ No newline at end of file diff --git a/samps/contests/pointscoring/config/c/submissions/accepted/ISumit.java b/samps/contests/pointscoring/config/c/submissions/accepted/ISumit.java new file mode 100644 index 000000000..38447a7a6 --- /dev/null +++ b/samps/contests/pointscoring/config/c/submissions/accepted/ISumit.java @@ -0,0 +1,29 @@ + +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +import java.io.*; + +// +// File: ISumit.java +// Purpose: to print the sum of the positive integers from stdin + +public class ISumit { + public static void main(String[] args) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1); + + String line; + int sum = 0; + int rv = 0; + while ((line = br.readLine()) != null) { + rv = new Integer(line.trim()).intValue(); + if (rv > 0) + sum = sum + rv; + } + System.out.print("The sum of the integers is "); + System.out.println(sum); + } catch (Exception e) { + System.out.println("Possible trouble reading stdin"); + System.out.println("Message: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/samps/contests/pointscoring/config/c/submissions/partially_accepted/ISumitPartial.java b/samps/contests/pointscoring/config/c/submissions/partially_accepted/ISumitPartial.java new file mode 100644 index 000000000..1574be0a7 --- /dev/null +++ b/samps/contests/pointscoring/config/c/submissions/partially_accepted/ISumitPartial.java @@ -0,0 +1,28 @@ + +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +import java.io.*; + +// +// File: ISumitPartial.java +// Purpose: to print the sum of the integers from stdin but failing to ignore negative numbers + +public class ISumitPartial { + public static void main(String[] args) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1); + + String line; + int sum = 0; + int rv = 0; + while ((line = br.readLine()) != null) { + rv = new Integer(line.trim()).intValue(); + sum = sum + rv; + } + System.out.print("The sum of the integers is "); + System.out.println(sum); + } catch (Exception e) { + System.out.println("Possible trouble reading stdin"); + System.out.println("Message: " + e.getMessage()); + } + } +} diff --git a/samps/contests/pointscoring/config/c/submissions/wrong_answer/ISumitWA.java b/samps/contests/pointscoring/config/c/submissions/wrong_answer/ISumitWA.java new file mode 100644 index 000000000..3c6030fe4 --- /dev/null +++ b/samps/contests/pointscoring/config/c/submissions/wrong_answer/ISumitWA.java @@ -0,0 +1,30 @@ + +// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +import java.io.*; + +// +// File: ISumitWA.java +// Purpose: to sum the integers from stdin but print an incorrect sum +// (that is, to fail to correctly print the sum of the positive integers from stdin) + +public class ISumitWA { + public static void main(String[] args) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1); + + String line; + int sum = 0; + int rv = 0; + while ((line = br.readLine()) != null) { + rv = new Integer(line.trim()).intValue(); + if (rv > 0) + sum = sum + rv; + } + System.out.print("Dumn integers is "); + System.out.println(sum + 1024); + } catch (Exception e) { + System.out.println("Possible trouble reading stdin"); + System.out.println("Message: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/samps/contests/pointscoring/config/contest.yaml b/samps/contests/pointscoring/config/contest.yaml new file mode 100644 index 000000000..c7c1ba5d6 --- /dev/null +++ b/samps/contests/pointscoring/config/contest.yaml @@ -0,0 +1,114 @@ +# +# +--- + +name: Point Scoring Contest +short-name: pointscoring + +scoreboard_type: score + +team-scoreboard-display-format-string : 'Team{:clientnumber} : {:teamname}' + +duration: 5:00:00 + +# freeze time before end of contest +scoreboard-freeze-length: 01:00:00 + +# pc2 specific settings +# elapsed: 0:00:00 +# remaining: 5:00:00 +running: true + +# default stop of first fail +stop-on-first-failed-test-case: false + +# Halt contest clock at end of contest +auto-stop-clock-at-end: true + +# Do not halt contest clock at end of contest, default setting +# auto-stop-clock-at-end: false + + +# default pc2 judging types +computer-judged: true +manual-review: false +send-prelim-judgement: false + +# pc2 Shadow CCS settings +# enable shadow mode +shadow-mode: true +# base URL for primary CCS REST service +ccs-url: https://localhost:50443/contest +# primary CCS REST login +ccs-login: admin +# primary CCS REST password +ccs-password: admin + +languages: + - name: 'Java' + clics-id: 'java' + + - name: 'python' + clics-id: 'pythonCLICSId' + +accounts: + - account: JUDGE + site: 1 + count: 12 + + - account: SCOREBOARD + site: 1 + count: 2 + + - account: FEEDER + site: 1 + count: 4 + + - account: ADMINISTRATOR + site: 1 + count: 3 + +# set feeder1 as proxy +team-proxy-accounts: + - account: FEEDER + number: 1 + + +auto-judging: + - account: JUDGE + site: 1 + number: 6,7,8,9 + letters: All + enabled: yes + +# General forms +# permissions: +# - account: TYPE +# number: 10,11,12,13,14,15 +# number: All +# enable: list of permissions +# disable: list of permissions +# +# + +# disable Contest Security Alert window + +# account permissions +permissions: + + - account: ADMINISTRATOR + number: 1,2,3 + disable: VIEW_SECURITY_ALERTS + + - account: JUDGE + number: All + enable: GIVE_RUN + + +# enable EDIT_RUN for FEEDER 3 + + - account: FEEDER + number: 3 + enable: SHADOW_PROXY_TEAM, EDIT_RUN + +# EOF Contest Configuration diff --git a/samps/contests/pointscoring/config/problemset.yaml b/samps/contests/pointscoring/config/problemset.yaml new file mode 100644 index 000000000..e218bb309 --- /dev/null +++ b/samps/contests/pointscoring/config/problemset.yaml @@ -0,0 +1,19 @@ +problems: + - letter: A + short-name: a + clics-id : 'A-problem' + color: Dark green + rgb: '#399956' + + - letter: B + short-name: b + clics-id : 'B-problem' + color: Purple + rgb: '#825eaa' + + - letter: C + short-name: c + clics-id : 'C-problem' + color: Red + rgb: '#df1328' + diff --git a/src/edu/csus/ecs/pc2/clics/API202306/CLICSContestInfo.java b/src/edu/csus/ecs/pc2/clics/API202306/CLICSContestInfo.java index 0d174bb1e..ba9b00ee9 100644 --- a/src/edu/csus/ecs/pc2/clics/API202306/CLICSContestInfo.java +++ b/src/edu/csus/ecs/pc2/clics/API202306/CLICSContestInfo.java @@ -107,7 +107,7 @@ public CLICSContestInfo(IInternalContest model, ContestInformation ci) { } } penalty_time = Integer.valueOf(ci.getScoringProperties().getProperty(DefaultScoringAlgorithm.POINTS_PER_NO, "20")); - scoreboard_type = "pass-fail"; + scoreboard_type = ci.getScoreboardType().toString().toLowerCase(); } public String toJSON() { diff --git a/src/edu/csus/ecs/pc2/clics/API202306/CLICSJudgement.java b/src/edu/csus/ecs/pc2/clics/API202306/CLICSJudgement.java index 98515855d..4271365ef 100644 --- a/src/edu/csus/ecs/pc2/clics/API202306/CLICSJudgement.java +++ b/src/edu/csus/ecs/pc2/clics/API202306/CLICSJudgement.java @@ -6,7 +6,6 @@ import java.util.Set; import java.util.logging.Level; -import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -26,7 +25,6 @@ * */ @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonFilter("rtFilter") public class CLICSJudgement { @JsonProperty @@ -38,9 +36,8 @@ public class CLICSJudgement { @JsonProperty private String judgement_type_id; -// Only for "score" type contests, N/A for ICPC pass/fail -// @JsonProperty -// private String score; + @JsonProperty + private Double score; @JsonProperty private String start_time; diff --git a/src/edu/csus/ecs/pc2/clics/API202306/CLICSProblem.java b/src/edu/csus/ecs/pc2/clics/API202306/CLICSProblem.java index cb1ca8fb0..203ceca57 100644 --- a/src/edu/csus/ecs/pc2/clics/API202306/CLICSProblem.java +++ b/src/edu/csus/ecs/pc2/clics/API202306/CLICSProblem.java @@ -8,7 +8,9 @@ import edu.csus.ecs.pc2.core.StringUtilities; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.Problem; +import edu.csus.ecs.pc2.core.model.ProblemDataFiles; import edu.csus.ecs.pc2.core.util.IJSONTool; +import edu.csus.ecs.pc2.imports.ccs.TestDataGroup; import edu.csus.ecs.pc2.services.core.JSONUtilities; /** @@ -48,9 +50,8 @@ public class CLICSProblem { @JsonProperty private int test_data_count; -// only for "score" type contests, N/A for pc2/wf pass-fail type contests -// @JsonProperty -// private int max_score; + @JsonProperty + private Double max_score; // The next two will be 'null' for now until we implement the new json CPF @JsonProperty("package") @@ -59,6 +60,8 @@ public class CLICSProblem { @JsonProperty private CLICSFileReference [] statement; + private boolean isPointScoring = false; + /** * Fill in properties for a Problem description. * @@ -82,15 +85,25 @@ public CLICSProblem(IInternalContest model, Problem problem, int ordinal) { } test_data_count = problem.getNumberTestCases(); time_limit = problem.getTimeOutInSeconds(); + if(model.getContestInformation().isScoreboardTypeScore()) { + isPointScoring = true; + ProblemDataFiles problemDataFiles = model.getProblemDataFile(problem); + if(problemDataFiles != null) { + TestDataGroup [] testDataGroups = problemDataFiles.getJudgesDataGroups(); + // Really, there's only 1 top level TestDataGroup + if(testDataGroups != null && testDataGroups.length > 0) { + max_score = testDataGroups[0].getRangeMax(); + } + } + } } public String toJSON() { - try { ObjectMapper mapper = JSONUtilities.getObjectMapper(); return mapper.writeValueAsString(this); } catch (Exception e) { - return "Error creating JSON for CLICS problem info " + e.getMessage(); + return "Error creating JSON for CLICSProblem " + e.getMessage(); } } } diff --git a/src/edu/csus/ecs/pc2/clics/API202306/CLICSProblemScore.java b/src/edu/csus/ecs/pc2/clics/API202306/CLICSProblemScore.java index 1cab9dedb..c6e4c08c1 100644 --- a/src/edu/csus/ecs/pc2/clics/API202306/CLICSProblemScore.java +++ b/src/edu/csus/ecs/pc2/clics/API202306/CLICSProblemScore.java @@ -33,9 +33,8 @@ public class CLICSProblemScore { @JsonProperty private boolean solved; -// Not needed for pass-fail contest -// @JsonProperty -// private double score; + @JsonProperty + private Double score; @JsonProperty private int time; diff --git a/src/edu/csus/ecs/pc2/clics/API202306/CLICSScore.java b/src/edu/csus/ecs/pc2/clics/API202306/CLICSScore.java index a9b3b8c40..40a36e24d 100644 --- a/src/edu/csus/ecs/pc2/clics/API202306/CLICSScore.java +++ b/src/edu/csus/ecs/pc2/clics/API202306/CLICSScore.java @@ -23,9 +23,8 @@ public class CLICSScore { @JsonProperty private int total_time; -// Not used for pass/fail contest and we don't want it appearing in the json -// @JsonProperty -// private int score; + @JsonProperty + private Double score; @JsonProperty private int time; @@ -50,9 +49,11 @@ public CLICSScore(TeamStanding teamStanding) { if(num_solved > 0) { // Problem solution time is in minutes. time = Integer.parseInt(teamStanding.getLastSolved()); + score = Double.parseDouble(teamStanding.getScore()); } } + public int getNum_solved() { return num_solved; } @@ -65,4 +66,18 @@ public int getTime() { return time; } + /** + * @return the score + */ + public double getScore() { + return score.doubleValue(); + } + + /** + * @param score the score to set + */ + public void setScore(double score) { + this.score = score; + } + } diff --git a/src/edu/csus/ecs/pc2/core/Utilities.java b/src/edu/csus/ecs/pc2/core/Utilities.java index 81c1aae88..5b9d94c7e 100644 --- a/src/edu/csus/ecs/pc2/core/Utilities.java +++ b/src/edu/csus/ecs/pc2/core/Utilities.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core; import java.io.BufferedReader; @@ -57,7 +57,7 @@ public final class Utilities { private static boolean debugMode = false; - + private static boolean showStandingsPanes = true; /** @@ -81,12 +81,12 @@ public final class Utilities { /** * CLICS path where judge's (secret) data file are stored - * + * * @see #getSecretDataPath(String, Problem) * @see #getSecretDataPath(String, String) */ public static final String SECRET_DATA_PATH = "data" + File.separator + ExportYAML.SECRET_DIRECTORY_NAME; - + /** * CLICS directory where judge's sample data file are stored */ @@ -120,7 +120,7 @@ public final class Utilities { /** * File Types. - * + * */ public enum DataFileType { /** @@ -164,7 +164,7 @@ public static String getSecretDataPath(String baseCDPPath, String problemShortNa public static String getSecretDataPath(String baseCDPPath, Problem problem) { return getSecretDataPath(baseCDPPath, problem.getShortName()); } - + /** * Return CLICS path for sample data and answer file names. */ @@ -257,7 +257,7 @@ public static boolean isIntegerNumber(String s) { /** * Compares 2 char arrays for equality. - * + * * @param oldBuffer * @param newBuffer * @return true if oldBuffer is the same size and has the same contents of newBuffer @@ -283,7 +283,7 @@ public static boolean isEquals(char[] oldBuffer, char[] newBuffer) { /** * Returns lines from file. - * + * * @param filename * String file to load * @return lines from file @@ -322,11 +322,11 @@ public static String[] loadFile(String filename) throws IOException { return out; } - + /** * Load string array with file contents. - * + * * @param filename * @param maximumLinesReturned minimum value is 1. * @return lines from file @@ -341,9 +341,9 @@ public static String[] loadFile(String filename, long maximumLinesReturned) thro if (new File(filename).exists()) { BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(filename), "UTF8")); - + long lineCount = 0; - + String line = in.readLine(); while (lineCount < maximumLinesReturned && line != null) { lines.add(line); @@ -354,12 +354,12 @@ public static String[] loadFile(String filename, long maximumLinesReturned) thro in = null; } - return (String[]) lines.toArray(new String[lines.size()]); + return lines.toArray(new String[lines.size()]); } /** * Get Current Working Directory. - * + * * @return current working directory. */ public static String getCurrentDirectory() { @@ -399,7 +399,7 @@ public static String getL10nDateTime(int dateStyle, int timeStyle, Locale curren /** * Returns a date-time String as defined by RFC 2822 section 3.3 Useful for mail messages. - * + * * @return the date in rfc2822 format */ public static String getRFC2822DateTime() { @@ -436,7 +436,7 @@ public static String getRFC2822DateTime() { /** * Returns Yes if true, No if false. - * + * * @param b * @return Yes or No */ @@ -458,11 +458,11 @@ public static String trueFalseString(boolean value, String trueString, String fa /** * Load INI file. - * + * * This will read a text file and strip out blank/empty lines and lines that start with a hash mark. *

* This will also trim the input lines. - * + * * @param filename * file to be read * @return String [] null if can't read/find file, else lines infile @@ -490,7 +490,7 @@ public static String[] loadINIFile(String filename) { return null; } - return (String[]) v.toArray(new String[v.size()]); + return v.toArray(new String[v.size()]); } public static String forHTML(String aText) { @@ -606,10 +606,10 @@ public static String getReportFilename(IReport selectedReport) { } - + /** - * Create/Write a report to file in {@value Constants#REPORT_DIRECTORY_NAME} - * + * Create/Write a report to file in {@value Constants#REPORT_DIRECTORY_NAME} + * * @param report * IReport to write * @param contest @@ -620,11 +620,11 @@ public static String getReportFilename(IReport selectedReport) { public static String createReport(IReport report, IInternalContest contest, IInternalController controller, boolean printHeaderAndFooter) throws FileNotFoundException { return createReport(Constants.REPORT_DIRECTORY_NAME, report, contest, controller, printHeaderAndFooter); } - + /** - * Create/Write a report to file in outputDirectoryName, creates outputDirectoryName if dir doesnot exist. - * - * + * Create/Write a report to file in outputDirectoryName, creates outputDirectoryName if dir doesnot exist. + * + * * @param outputDirectoryName output directory name * @param report * IReport to write @@ -636,7 +636,7 @@ public static String createReport(IReport report, IInternalContest contest, IInt public static String createReport(String outputDirectoryName, IReport report, IInternalContest contest, IInternalController controller, boolean printHeaderAndFooter) throws FileNotFoundException { String filename = getReportFilename(report); - + if (outputDirectoryName != null && outputDirectoryName.trim().length() > 0) { ExecuteUtilities.ensureDirectory(outputDirectoryName); filename = outputDirectoryName + File.separator + filename; @@ -712,7 +712,7 @@ private static void printReportHeader(PrintWriter printWriter, IReport report, I /** * Create and view report. - * + * * @param report * @param title * title for tab in pane for this report @@ -722,7 +722,7 @@ private static void printReportHeader(PrintWriter printWriter, IReport report, I public static void viewReport(IReport report, String title, IInternalContest contest, IInternalController controller, boolean printHeaderAndFooter) { try { - + String filename = createReport(report, contest, controller, printHeaderAndFooter); MultipleFileViewer multipleFileViewer = new MultipleFileViewer(controller.getLog()); @@ -741,7 +741,7 @@ public static void viewReport(IReport report, String title, IInternalContest con /** * Dump String array. - * + * * @param out * stream to output to * @param prefix @@ -781,7 +781,7 @@ public static void debugPrint(Exception e) { /** * View report - * + * * @see #viewReport(IReport, String, IInternalContest, IInternalController, boolean) * @param report * @param title @@ -794,9 +794,9 @@ public static void viewReport(IReport report, String title, IInternalContest con /** * Create disk file for input SerializedFile. - * + * * Returns true if file is written to disk and is not null. - * + * * @param file * @param outputFileName * @return true if file written to disk. @@ -837,7 +837,7 @@ public static StringBuffer join(String delimiter, String[] strings) { /** * return full path for input relative path/filename. - * + * * @param relativePath */ public static String getFullPath(String relativePath) { @@ -851,7 +851,7 @@ public static String getFullPath(String relativePath) { /** * Convert String to second. - * + * * @param s * string in form hh:mm:ss, ss or mm:ss * @return -1 if invalid time string, else returns number of seconds @@ -909,7 +909,7 @@ public static long convertStringToSeconds(String s) { /** * Parse and return positive long. - * + * * @param s1 * @return -1 if non-long string, else long value */ @@ -926,7 +926,7 @@ public static long stringToLong(String s1) { /** * return date/time string. - * + * * @return curernt date using format {@value Utilities#DATE_TIME_FORMAT_STRING}. */ public static String getDateTime() { @@ -935,7 +935,7 @@ public static String getDateTime() { /** * Convert String to second. Expects input in form: ss or mm:ss or hh:mm:ss - * + * * @param s * string to be converted to seconds * @return -1 if invalid time string, 0 or >0 if valid @@ -993,7 +993,7 @@ public long stringToLongSecs(String s) { /** * Locate judges data file on disk. - * + * * @param problem * @param serializedFile * @param alternateCDPPath @@ -1003,11 +1003,11 @@ public long stringToLongSecs(String s) { public static String locateJudgesDataFile(Problem problem, SerializedFile serializedFile, String alternateCDPPath, DataFileType judgeDataFile) { String testFileName = locateJudgesDataFile(problem, serializedFile.getName(), alternateCDPPath); - + if (testFileName != null && fileExists(testFileName)) { return testFileName; } - + testFileName = serializedFile.getAbsolutePath(); if (fileExists(testFileName)) { @@ -1016,13 +1016,13 @@ public static String locateJudgesDataFile(Problem problem, SerializedFile serial return null; } - + public static String locateJudgesDataFile(Problem problem, String baseFileName, String alternateCDPPath) { String testFileName = null; if (alternateCDPPath != null && alternateCDPPath.trim().length() > 0) { - + // Try to find data file under secret directory testFileName = getSecretDataPath(alternateCDPPath, problem) + File.separator + baseFileName; @@ -1030,9 +1030,9 @@ public static String locateJudgesDataFile(Problem problem, String baseFileName, return testFileName; } } - + if (alternateCDPPath != null && alternateCDPPath.trim().length() > 0) { - + // Try to find data file under sample directory testFileName = getSampleDataPath(alternateCDPPath, problem) + File.separator + baseFileName; @@ -1062,7 +1062,7 @@ public static boolean fileExists(String filename) { /** * Write file to output printWriter. - * + * * @param printWriter * @param outputfilename */ @@ -1119,7 +1119,7 @@ public static String[] fullJudgesAnswerFilenames(IInternalContest contest, Probl * For external files (aka CDP files) on the JUDGE the path is from {@link ContestInformation#getJudgeCDPBasePath()} where ContestInformation is in {@link IInternalContest#getContestInformation()} * .
* For external files (aka CDP files) on the ADMIN (or JUDGE without the CDP path set) the path is stored in the {@link Problem#getExternalDataFileLocation()} - * + * * @param contest * @param problem * @param serializedFiles @@ -1132,7 +1132,7 @@ public static String[] getProblemfullFilenames(IInternalContest contest, Problem ArrayList output = new ArrayList(); String originalJudgeDataPath = getJudgeCDPLocation(contest); - + if (problem.isUsingExternalDataFiles()) { ClientId id = contest.getClientId(); if (id == null) { @@ -1144,7 +1144,7 @@ public static String[] getProblemfullFilenames(IInternalContest contest, Problem if (!"".equals(judgeDataFilesPath)) { judgeDataFilesPath = Utilities.getSecretDataPath(judgeDataFilesPath, problem) + File.separator; File judgeDir = new File(judgeDataFilesPath); - + if (!judgeDir.isDirectory()) { judgeDataFilesPath = judgeDataFilesPath.replaceFirst(".data.secret", ""); } @@ -1186,12 +1186,12 @@ public static String[] getProblemfullFilenames(IInternalContest contest, Problem } } } - return (String[]) output.toArray(new String[output.size()]); + return output.toArray(new String[output.size()]); } /** * Start Windows Explorer. - * + * * @param dir * directory to display * @throws IOException @@ -1215,14 +1215,14 @@ public static void startExplorer(String directoryName) { /** * Dump contents of ProblemDataFiles using ProblemReport - * + * *

      * PrintWriter printWriter = new PrintWriter(System.out);
      * dump(problemDataFiles, "dump this");
      * printWriter.close();
      * printWriter = null;
      * 
- * + * * @param printWriter * @param dataFiles * @param message @@ -1244,7 +1244,7 @@ public static void dump(PrintWriter printWriter, ProblemDataFiles dataFiles, Str /** * Dump problem data files to System.out. - * + * * @param dataFiles * @param message */ @@ -1256,7 +1256,7 @@ public static void dump(ProblemDataFiles dataFiles, String message) { /** * Return list of directory entries matching with extension. - * + * * @param directoryName * @param extension * @return list of basename filenames in directory directoryName @@ -1279,12 +1279,12 @@ public static String[] getFileNames(String directoryName, String extension) { } } - return (String[]) list.toArray(new String[list.size()]); + return list.toArray(new String[list.size()]); } /** * If there is a freezeTime set, return the contestLength - freezeTime, else the contestLength. - * + * * @param model * - contest to get info from * @return The freeze time in seconds (counting up from contest Start) @@ -1311,14 +1311,14 @@ public static SerializedFile[] createSerializedFiles(String dataFileBaseDirector outfiles.add(new SerializedFile(filename, externalFilesFlag)); } - return (SerializedFile[]) outfiles.toArray(new SerializedFile[outfiles.size()]); + return outfiles.toArray(new SerializedFile[outfiles.size()]); } /** * Find base data path for problems. - * + * * If {@value #SECRET_DATA_DIR} in filePath will strop off anything including and after {@value #SECRET_DATA_DIR}. - * + * * @param filePath * @return directory name up to {@value #SECRET_DATA_DIR} */ @@ -1330,8 +1330,8 @@ public static String findDataBasePath(String filePath) { } return filePath; } - - + + /** * If file has extension replaces with replacement. * @param fullName @@ -1345,22 +1345,22 @@ public static String replaceExtension(String fullName, String extension, String /** * Validate problem files. - * + * * Only checks for files on disk if {@link Problem#isUsingExternalDataFiles()} is true. - * + * * Will throw a MultipleIssuesException if any directories or files are missing, use {@link MultipleIssuesException#getIssueList()) for list of missing files or errors. - * + * * @param contest * @param cdpPath * - base path for CDP config directory * @param problem * problem to validate - * @param allProblemDCPFiles + * @param allProblemCDPFiles * include problem.tex and problem.yaml files. * @return true if all files present * @throws MultipleIssuesException */ - public static boolean validateCDP(IInternalContest contest, String cdpPath, Problem problem, boolean allProblemDCPFiles) throws MultipleIssuesException { + public static boolean validateCDP(IInternalContest contest, String cdpPath, Problem problem, boolean allProblemCDPFiles) throws MultipleIssuesException { List messages = new ArrayList<>(); if (problem == null) { @@ -1399,44 +1399,22 @@ public static boolean validateCDP(IInternalContest contest, String cdpPath, Prob // If no secret directory - done, show Missing data directory message messages.add(problemTitle + "\tMissing data directory, expected at: " + dataPath + " or (" + dataPath + File.separator + "data" + File.separator + "secret)"); } else { - - int missingData = 0; - int missingAnswer = 0; - - for (int i = 0; i < problem.getNumberTestCases(); i++) { - - String dataFile = problem.getDataFileName(i + 1); - String ansFile = problem.getAnswerFileName(i + 1); - - String judgeFileName = dataPath + File.separator + dataFile; - String answerFilename = dataPath + File.separator + ansFile; - - if (dataFile != null && !isFileThere(judgeFileName)) { - // Try to find file under samples - String testFile = locateJudgesDataFile(problem, dataFile, cdpPath); - if (isFileThere(testFile)) { - judgeFileName = testFile; - answerFilename = replaceExtension(testFile, "in", "ans"); - } - } - - if (dataFile != null && !isFileThere(judgeFileName)) { - messages.add(problemTitle + "\tMissing judge file '" + dataFile + "' in " + dataPath); - missingData++; - } - - if (ansFile != null && !isFileThere(answerFilename)) { - messages.add(problemTitle + "\tMissing answer file '" + ansFile + "' in " + dataPath); - missingAnswer++; - } - } - if ((missingData + missingAnswer) > 0) { - messages.add(problemTitle + "\ttotal files missing = " + (missingData + missingAnswer)); + ProblemDataFiles dataFiles = contest.getProblemDataFile(problem); + SerializedFile[] inFiles = dataFiles.getJudgesDataFiles(); + SerializedFile[] ansFiles = dataFiles.getJudgesAnswerFiles(); + + checkSerializedFileList(inFiles, problemTitle, "input", messages); + checkSerializedFileList(ansFiles, problemTitle, "answer", messages); + + int nTestCases = inFiles.length; + int nAnsCases = ansFiles.length; + if(inFiles.length != ansFiles.length) { + messages.add(problemTitle + "\tDifferent numbers of judge's input (" + nTestCases + ") and answer (" + + nAnsCases + ") test cases in " + problemDir); } } - - if (allProblemDCPFiles) { + if (allProblemCDPFiles) { // check for problem.tex String laTextProblemFilename = problemDir + IContestLoader.DEFAULT_PROBLEM_LATEX_FILENAME; @@ -1462,11 +1440,35 @@ public static boolean validateCDP(IInternalContest contest, String cdpPath, Prob } } + /** + * Validates that all the files in the supplied list exist + * + * @param files the list of files + * @param problem name of problem for messages + * @param fileType the type of file, eg. input, answer - used for messages + * @param messages list of error messages to add to in the event of errors + * + * @return true of the list is OK, false if there were errors + */ + private static boolean checkSerializedFileList(SerializedFile[] files, String problemTitle, String fileType, List messages) { + String file; + boolean result = true; + + for(SerializedFile f : files) { + file = f.getAbsolutePath(); + if(!isFileThere(file)) { + messages.add(problemTitle + "\tMissing judge " + fileType + " file '" + file + "'"); + result = false; + } + } + return(false); + } + /** * Validate all problem data files. - * + * * Only checks for files on disk if {@link Problem#isUsingExternalDataFiles()} is true. - * + * * @see #validateCDP(IInternalContest, String, Problem, boolean) */ public static boolean validateCDP(IInternalContest contest, String cdpPath) throws MultipleIssuesException { @@ -1499,9 +1501,9 @@ public static boolean validateCDP(IInternalContest contest, String cdpPath) thro /** * For the input number, returns an upper-case letters. - * + * * 1 = A, 2 = B, ..., 27 = AA, ... 702 = ZZ - * + * * @param id * problem number, base one (not zero), range 1 (A) through 702 (ZZ) * @return uppercase letters @@ -1525,9 +1527,9 @@ public static String getProblemLetter(int id) { /** * The problem number, base 1. - * + * * A = 1, B = 2, etc. - * + * * @param contest * @param problem * @return the problem number, base 1. Returns 0 if not found. @@ -1545,9 +1547,9 @@ public static int getProblemNumber(IInternalContest contest, Problem problem) { /** * Return date/time string for now. - * + * * Uses format {@value #FORMAT_YYYY_MM_DD_HH_MM_SS}. - * + * * @return */ public static String getDateTimeString() { @@ -1556,9 +1558,9 @@ public static String getDateTimeString() { /** * Convert DOS file seperator with unix. - * + * * replace all \ with /. - * + * * @param filename * @return */ @@ -1568,7 +1570,7 @@ public static String unixifyPath(String filename) { /** * Write lines to file. - * + * * @param filename * @param lines * @throws FileNotFoundException @@ -1584,18 +1586,18 @@ public static void writeLinesToFile(String filename, String[] lines) throws File } /** - * Checks the specified {@link SerializedFile} for error messages and/or exceptions. - * This method is provided because the SerializedFile class does not throw exceptions when errors occur - * (such as its constructor encountering a "File Not Found" condition). Rather, the SerializedFile class simply - * sets an "error message" and records the "exception" within the SerializedFile object. - * This method throws any exception found in the specified SerializedFile, or returns true if there is an error message (but no exception). + * Checks the specified {@link SerializedFile} for error messages and/or exceptions. + * This method is provided because the SerializedFile class does not throw exceptions when errors occur + * (such as its constructor encountering a "File Not Found" condition). Rather, the SerializedFile class simply + * sets an "error message" and records the "exception" within the SerializedFile object. + * This method throws any exception found in the specified SerializedFile, or returns true if there is an error message (but no exception). * It returns false if the specified SerializedFile contains no error message or is null. - * + * * @param serFile * the SerializedFile to be checked - * + * * @return true if the SerializedFile contains an error message or exceptions; false if the SerializedFile contains no error message or is null - * + * * @throws Exception * if any exception is found in the SerializedFile object */ @@ -1623,9 +1625,9 @@ public static boolean serializedFileError(SerializedFile serFile) throws Excepti /** * Return OS type. - * + * * Attempts to identify {@link OSType}. - * + * * @return {@link OSType#UNCLASSIFIED} if cannot be determined else returns {@link OSType} */ public static OSType getOSType() { @@ -1669,9 +1671,9 @@ public static OSType getOSType() { /** * Return true if considered an executable. - * + * * If not extension found or extension in #VALID_PROGRAM_EXTENSIONS - * + * * @param baseFileName * @return */ @@ -1693,7 +1695,7 @@ public static boolean isExecutableExtension(String baseFileName) { /** * Convert from int array to list. - * + * * @param proxySites * @return */ @@ -1709,7 +1711,7 @@ public static List arrayToList(int[] proxySites) { /** * return base name, strip path and extension. - * + * * @param filename * @return */ @@ -1728,7 +1730,7 @@ public static String getFileBaseName(String filename) { return baseFileName; } - + /** * Print stack trace with only elements with csus in them. * @param printStream @@ -1736,19 +1738,19 @@ public static String getFileBaseName(String filename) { */ public static void printStackTrace(PrintStream printStream, Exception e) { printStackTrace(printStream, e, "csus"); - + } /** * Prints a stack trace, prints stack elements which only matches pattern. - * + * * Example to only print stack trace elements with csus: - * + * *
      *    Utilities.printStackTrace(System.err,e,"csus");
-     * 
+     *
      * prints:
-     * 
+     *
      * java.sql.SQLException: No value specified for parameter 7
      *    Matching: csus
      *      at edu.csus.ecs.pc2.db.adapters.MySqlDatabaseAdapter(MySqlDatabaseAdapter.java:274)
@@ -1756,7 +1758,7 @@ public static void printStackTrace(PrintStream printStream, Exception e) {
      *      at edu.csus.ecs.pc2.db.adapters.MySqlDatabaseAdapter(MySqlDatabaseAdapter.java:2442)
      *      at edu.csus.ecs.pc2.db.adapters.DatabaseAdapterTest(DatabaseAdapterTest.java:237)
      * 
- * + * * @param printStream * @param e * @param pattern @@ -1796,7 +1798,7 @@ public static void dumpProblemGroups(String message, Problem problem) { /** * If object is empty/null throw InvalidParameterException - * + * * @param obj * @param message */ @@ -1808,7 +1810,7 @@ public static void isEmpty(Object obj, String message) { /** * If string is empty/null throw InvalidParameterException - * + * * @param s * @param message */ @@ -1820,7 +1822,7 @@ public static void isEmptyString(String s, String message) { /** * Convert String to int. - * + * * @param string * @param defaultNumber * used if invalid or null string @@ -1843,63 +1845,63 @@ public static int nullSafeToInt(String string, int defaultNumber) { /** * Converts a CLICS Contest API "RELTIME" (contest time) string to milliseconds. - * + * * The format for CLICS RELTIME values is: (-)?(h)*h:mm:ss(.uuu)? * Note that this differs from standard ISO 8601 times in that it allows more than two hour * digits and in that the optional fractional seconds (.uuu), if specified, MUST have exactly * three digits (the ISO 8601 spec doesn't require/restrict the number of digits in the fraction). - * + * * @param time a time string in CLICS Contest API format * @return the number of milliseconds corresponding to the specified time string, or the most * negative long value possible (Long.MIN_VALUE) if the string could not be parsed correctly */ public static long convertCLICSContestTimeToMS(String time) { - + final long MSECS_PER_HOUR = 1000 * 60 * 60 ; final long MSECS_PER_MIN = 1000 * 60 ; final long MSECS_PER_SEC = 1000 ; - + boolean isNegative = false ; - + long hoursMS; long minsMS; long secondsMS; long msecs; - + try { //strip off any optional minus sign if (time.startsWith("-")) { time = new String(time.substring(1,time.length())); isNegative = true; } - + String [] fields = time.split(":"); if (fields.length!=3) { //missing one or more required fields return Long.MIN_VALUE; } - + //verify there are the required number of chars in each field (>=1 for hours, =2 for mins, either 2 or 6 secs) if (fields[0].length()<1 || fields[1].length()!=2 || (fields[2].length()!=2 && fields[2].length()!=6) ) { return Long.MIN_VALUE; } - + //verify the minutes digits are legitmate time values int mins = Integer.parseInt(fields[1]) ; if (mins<0 || mins>59) { return Long.MIN_VALUE; } - + //split out any option fractional part in the seconds field String [] secondsFields = fields[2].split("\\."); - + //verify the seconds digits are legitmate time values int secs = Integer.parseInt(secondsFields[0]) ; if (secs<0 || secs > 59) { return Long.MIN_VALUE; } - - + + //process any optional fraction on the input msecs = 0; if (secondsFields.length>1) { @@ -1917,9 +1919,9 @@ public static long convertCLICSContestTimeToMS(String time) { hoursMS = Long.parseLong(fields[0]) * MSECS_PER_HOUR; minsMS = Long.parseLong(fields[1]) * MSECS_PER_MIN; secondsMS = Long.parseLong(secondsFields[0]) * MSECS_PER_SEC; - + long retVal = hoursMS + minsMS + secondsMS + msecs ; - + if (isNegative) { retVal = -retVal ; } @@ -1931,10 +1933,10 @@ public static long convertCLICSContestTimeToMS(String time) { return Long.MIN_VALUE; } } - + /** * Fetch directories names (full path). - * + * * @param directory * @return list of directories (prepended by directory) */ @@ -1953,7 +1955,7 @@ public static List getDirectoryNames(String directory) { for (String name : entries) { String entry = directory + File.separator + name; - + if (new File(entry).isDirectory()) { list.add(entry); } @@ -1961,10 +1963,10 @@ public static List getDirectoryNames(String directory) { return list; } - + /** * Fetch all CDP data directories for all problems under cdpConfigDirectory directory. - * + * * @param cdpConfigDirectory * @return a list of data directories (full path) */ @@ -1990,25 +1992,25 @@ public static List getCDPDataDirectories(String cdpConfigDirectory) { return list; - } - + } + /** * Accepts a "duration" (an amount of time) in milliseconds and returns that duration in formatted form. * The general form of the returned format is HHHH:MM:SS.sss, where the number of hour digits will always be - * at least two, but may be more depending on the input value + * at least two, but may be more depending on the input value * (in other words, this is not a "time of day" which is restricted to just two hour-digits). - * Note however that the minutes and seconds fields will always be two digits, and the msec field will + * Note however that the minutes and seconds fields will always be two digits, and the msec field will * always be three digits. * If the input value is negative then it is converted to a positive value and the returned value is * the positive value preceded by a minus sign. - * + * * @param milliseconds the duration to be formatted, in msec. * @return the formatted value of the input duration. */ public static String formatDuration (long milliseconds) { - + String result = ""; - + //ensure calculations are based on positive milliseconds long posMsec = milliseconds; if (milliseconds<0) { @@ -2018,8 +2020,8 @@ public static String formatDuration (long milliseconds) { long msecPerSecond = Constants.MS_PER_SECOND; long msecPerMinute = Constants.MS_PER_MINUTE; - long msecPerHour = msecPerMinute*60 ; - + long msecPerHour = msecPerMinute*60 ; + long hours = posMsec / msecPerHour ; //whole hours (fractional portion is truncated if (hours<10) { //prepend a zero to insure at least two hour digits @@ -2027,21 +2029,21 @@ public static String formatDuration (long milliseconds) { } String hourString = Long.toString(hours); result += hourString + ":"; - + long minutes = (posMsec - (hours*msecPerHour)) / (60*1000); //whole minutes (truncated) if (minutes<10) { result += "0"; } String minuteString = Long.toString(minutes); result += minuteString + ":"; - + long seconds = (posMsec - (hours*msecPerHour) - (minutes*msecPerMinute)) / 1000; //whole seconds (truncated) if (seconds<10) { result += "0"; } String secondsString = Long.toString(seconds); result += secondsString + "."; - + long millis = posMsec - (hours*msecPerHour) - (minutes*msecPerMinute) - (seconds*msecPerSecond); //fractional seconds (truncated) if (millis<100) { result += "0"; @@ -2051,24 +2053,24 @@ public static String formatDuration (long milliseconds) { } String fractionString = Long.toString(millis); result += fractionString; - + return result; } public static void setShowStandingsPanes(boolean showStandingsPanes) { Utilities.showStandingsPanes = showStandingsPanes; } - + public static boolean isShowStandingsPanes() { return showStandingsPanes; } - + /** * Concatenate Arrays. - * - * This is null-safe, arrays can be null and will return + * + * This is null-safe, arrays can be null and will return * an array. - * + * * @param one first array * @param two secodn array * @return a new array which contains contents of one and two arrays @@ -2090,14 +2092,14 @@ public static String[] concatenateArrays(String[] one, String[] two) { return newArray; } - - + + /** * Concatenate Arrays. - * - * This is null-safe, arrays can be null and will return + * + * This is null-safe, arrays can be null and will return * an array. - * + * * @param one first array * @param two secodn array * @return a new array which contains contents of one and two arrays diff --git a/src/edu/csus/ecs/pc2/core/model/ContestInformation.java b/src/edu/csus/ecs/pc2/core/model/ContestInformation.java index b1a424c21..19a2343c0 100644 --- a/src/edu/csus/ecs/pc2/core/model/ContestInformation.java +++ b/src/edu/csus/ecs/pc2/core/model/ContestInformation.java @@ -141,6 +141,23 @@ public enum TeamDisplayMask { ALIAS, } + public enum ScoreboardType { + PASSFAIL("pass-fail"), + SCORE("score"); + + private final String type; + + private ScoreboardType(String type) { + this.type = type; + } + public String getType() { + return type; + } + + } + + private ScoreboardType scoreboardType = ScoreboardType.PASSFAIL; + private Properties scoringProperties = new Properties(); /** @@ -449,6 +466,37 @@ public boolean isSameAs(ContestInformation contestInformation) { return false; } + if(stopOnFirstFailedtestCase != contestInformation.isStopOnFirstFailedtestCase()) { + return false; + } + + if(memoryLimitInMeg != contestInformation.getMemoryLimitInMeg()) { + return false; + } + + if(!StringUtilities.stringSame(sandboxCommandLine, contestInformation.getSandboxCommandLine())){ + return false; + } + + if(!StringUtilities.stringSame(overrideLoadAccountsFilename, contestInformation.getOverrideLoadAccountsFilename())){ + return false; + } + + if(loadSampleJudgesData != contestInformation.isLoadSampleJudgesData()) { + return false; + } + + if(sandboxGraceTimeSecs != contestInformation.getSandboxGraceTimeSecs()) { + return false; + } + + if(sandboxInteractiveGraceMultiplier != contestInformation.getSandboxInteractiveGraceMultiplier()) { + return false; + } + + if (scoreboardType != contestInformation.getScoreboardType()) { + return false; + } return true; } catch (Exception e) { e.printStackTrace(System.err); // TODO log this exception @@ -931,4 +979,45 @@ public void setSandboxInteractiveGraceMultiplier(int nSecs) { sandboxInteractiveGraceMultiplier = nSecs; } + /** + * Set contest scoreboard type based on the string passed in. + * + * @param type one of "pass-fail" or "score", currently. + */ + public void setScoreboardType(String type) { + for(ScoreboardType sbType : ScoreboardType.values()) { + if(type.compareToIgnoreCase(sbType.getType()) == 0) { + scoreboardType = sbType; + break; + } + } + } + + /** + * Accessor for contest scoreboard type. Note there are convenient shorthands + * available below. + * + * @return scoreboardType enum + */ + public ScoreboardType getScoreboardType() { + return scoreboardType; + } + + /** + * Shorthand to determine if the contest is pass-fail scoring. + * + * @return true if the contest is pass-fail + */ + public boolean isScoreboardTypePassFail() { + return (scoreboardType == ScoreboardType.PASSFAIL); + } + + /** + * Shorthand to determine if the contest is score (point scoring). + * + * @return true of the contest is point scoring + */ + public boolean isScoreboardTypeScore() { + return (scoreboardType == ScoreboardType.SCORE); + } } diff --git a/src/edu/csus/ecs/pc2/core/model/JudgementRecord.java b/src/edu/csus/ecs/pc2/core/model/JudgementRecord.java index 6bb3fa8ad..19ee00758 100644 --- a/src/edu/csus/ecs/pc2/core/model/JudgementRecord.java +++ b/src/edu/csus/ecs/pc2/core/model/JudgementRecord.java @@ -127,6 +127,8 @@ public class JudgementRecord implements Serializable, IGetDate { // Copied from submission when judgement record is complete private Date judgeStartDate = null; + // For point scoring contests + private double score = 0.0; /** * Create a Judgement Record. @@ -451,4 +453,21 @@ public void setJudgeStartDate(Date startDate) { public Date getJudgeStartDate() { return judgeStartDate; } + + /** + * + * @return the score, for point scoring contests + */ + public double getScore() { + return score; + } + + /** + * Set the score for point scoring contests + * + * @param score + */ + public void setScore(double score) { + this.score = score; + } } diff --git a/src/edu/csus/ecs/pc2/core/model/Problem.java b/src/edu/csus/ecs/pc2/core/model/Problem.java index 9ef3321c8..fb484cd40 100644 --- a/src/edu/csus/ecs/pc2/core/model/Problem.java +++ b/src/edu/csus/ecs/pc2/core/model/Problem.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.model; import java.io.File; @@ -14,6 +14,7 @@ import edu.csus.ecs.pc2.core.log.StaticLog; import edu.csus.ecs.pc2.core.model.inputValidation.InputValidationResult; import edu.csus.ecs.pc2.core.model.inputValidation.VivaInputValidatorSettings; +import edu.csus.ecs.pc2.imports.ccs.TestCaseInfo; import edu.csus.ecs.pc2.ui.EditProblemSandboxPane; import edu.csus.ecs.pc2.validator.clicsValidator.ClicsValidatorSettings; import edu.csus.ecs.pc2.validator.customValidator.CustomValidatorSettings; @@ -1385,6 +1386,42 @@ public void addTestCaseFilenames (String datafile, String answerfile){ } } + /** + * Add data and answer filenames to list of test cases from the TestCaseInfo ArrayList + * This is better than adding them one-at-a-time and reallocating the array for each addition. + * + * @see #removeAllTestCaseFilenames() + * @param testCases - all the test cases in a ArrayList + */ + public void addTestCaseFilenames (ArrayList testCases){ + + int nCases = testCases.size(); + testCaseDataFilenames = new String[nCases]; + testCaseAnswerFilenames = new String[nCases]; + TestCaseInfo testCase; + String groupPath; + + for(int iCase = 0; iCase < nCases; iCase++) { + testCase= testCases.get(iCase); + testCaseDataFilenames[iCase] = extractFileNameFromPath(testCase.getInputFileName()); + testCaseAnswerFilenames[iCase] = extractFileNameFromPath(testCase.getAnswerFileName()); + } + } + + /** + * Returns last component of a path (the filename). + * It relies on the fact that the last component has a File.separator before it. If not, + * then the whole string is returned. + * @param fileName + * @return the filename component of the path + */ + private String extractFileNameFromPath(String fileName) { + int idx= fileName.lastIndexOf(File.separator); + if(idx == -1) { + return(fileName); + } + return(fileName.substring(idx+1)); + } /** * Remove all test case filenames. diff --git a/src/edu/csus/ecs/pc2/core/model/ProblemDataFiles.java b/src/edu/csus/ecs/pc2/core/model/ProblemDataFiles.java index 8e3dca610..63e859573 100644 --- a/src/edu/csus/ecs/pc2/core/model/ProblemDataFiles.java +++ b/src/edu/csus/ecs/pc2/core/model/ProblemDataFiles.java @@ -1,26 +1,30 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.model; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.ArrayList; import edu.csus.ecs.pc2.core.Utilities; +import edu.csus.ecs.pc2.imports.ccs.TestDataGroup; +// JB TODO - this will have to be reworked if we intend to update data files AFTER the CDP +// has been loaded. Specifically, the judgesDataGroups will need to be addressed. /** * Data files and programs (validator) for a Problem. - * - * Multiple data sets are supported. + * + * Multiple data sets are supported. *

- * - * + * + * * @see #getJudgesAnswerFile() * @see #getJudgesDataFile() - * + * * @see #getJudgesAnswerFiles() * @see #getJudgesDataFiles() - * + * * @see edu.csus.ecs.pc2.core.model.Problem * @see edu.csus.ecs.pc2.core.model.ProblemDataFilesList * @@ -39,9 +43,9 @@ public class ProblemDataFiles implements IElementObject { private ElementId elementId = new ElementId("ProblemDF"); private ElementId problemId = null; - + private SerializedFile outputValidatorFile; - + private SerializedFile inputValidatorFile; public ProblemDataFiles(Problem problem) { @@ -63,10 +67,15 @@ public ProblemDataFiles(Problem problem) { */ private SerializedFile[] judgesAnswerFiles = new SerializedFile[0]; + /** + * Data groups for each test file + */ + private TestDataGroup[] judgesDataGroups = new TestDataGroup[0]; + /** * This should be invoked with the new Problem and will attempt to copy * the existing ProblemDataFiles into a new ProblemDataFiles. - * + * * @param problem * @return a copy of this ProblemDataFiles * @throws CloneNotSupportedException @@ -75,7 +84,7 @@ public ProblemDataFiles copy(Problem problem) throws CloneNotSupportedException ProblemDataFiles clone = new ProblemDataFiles(problem); // inherited field clone.setSiteNumber(getSiteNumber()); - + // local fields // clone.elementId = elementId; // clone.problemId = getProblemId(); @@ -85,7 +94,9 @@ public ProblemDataFiles copy(Problem problem) throws CloneNotSupportedException clone.setJudgesAnswerFiles(cloneSFArray(getJudgesAnswerFiles())); clone.setJudgesDataFiles(cloneSFArray(getJudgesDataFiles())); - + // JB TODO - Does this have to be cloned? I dont think tso. + clone.judgesDataGroups = getJudgesDataGroups(); + return clone; } @@ -112,7 +123,7 @@ private SerializedFile cloneSerializedFile(SerializedFile file) throws CloneNotS /** * Return array of answer files. - * + * * @return the judge answer files or a single zero length array. */ public SerializedFile[] getJudgesAnswerFiles() { @@ -121,13 +132,22 @@ public SerializedFile[] getJudgesAnswerFiles() { /** * Return array of data files. - * + * * @return Returns the judge data files or a single zero length array. */ public SerializedFile[] getJudgesDataFiles() { return judgesDataFiles; } + /** + * Return array of data groups. + * + * @return Returns the judge data groups. + */ + public TestDataGroup [] getJudgesDataGroups() { + return judgesDataGroups; + } + /** * @param judgesAnswerFiles * The judgesAnswerFiles to set. @@ -136,6 +156,14 @@ public void setJudgesAnswerFiles(SerializedFile[] judgesAnswerFiles) { this.judgesAnswerFiles = judgesAnswerFiles; } + /** + * @param dataGroups + * The dataGroups for each test case. + */ + public void setJudgesDataGroups(ArrayList dataGroups) { + judgesDataGroups = dataGroups.toArray(new TestDataGroup [dataGroups.size()]); + } + /** * set a single answer file. * @@ -171,7 +199,7 @@ public void setJudgesDataFile(SerializedFile judgesDataFile) { files[0] = judgesDataFile; setJudgesDataFiles(files); } - + /** * Get the judge data file. * @return a file or null if not present. @@ -184,10 +212,10 @@ public SerializedFile getJudgesDataFile() { return files[0]; } } - + /** * Get the judge answer file. - * + * * @return a file or null if not present. */ public SerializedFile getJudgesAnswerFile() { @@ -202,18 +230,22 @@ public SerializedFile getJudgesAnswerFile() { /** * @return Returns the elementId. */ + @Override public ElementId getElementId() { return elementId; } + @Override public int versionNumber() { return elementId.getVersionNumber(); } + @Override public int getSiteNumber() { return elementId.getSiteNumber(); } + @Override public void setSiteNumber(int siteNumber) { elementId.setSiteNumber(siteNumber); } @@ -225,10 +257,10 @@ public ElementId getProblemId() { protected void setProblemId(ElementId problemId) { this.problemId = problemId; } - + /** * Returns the output validator program file. - * + * * @return a SerializedFile containing the output validator code, or null if no output validator has been specified */ public SerializedFile getOutputValidatorFile() { @@ -238,10 +270,10 @@ public SerializedFile getOutputValidatorFile() { public void setOutputValidatorFile(SerializedFile validatorFile) { this.outputValidatorFile = validatorFile; } - + /** * Returns the custom input validator program file associated with this ProblemDataFiles object. - * + * * @return a SerializedFile containing the input validator code, or null if no custom input validator has been specified. */ public SerializedFile getCustomInputValidatorFile() { @@ -250,14 +282,14 @@ public SerializedFile getCustomInputValidatorFile() { /** * Sets the custom input validator program file associated with this ProblemDataFiles object. - * + * * @param validatorFile a SerializedFile containing the custom input validator program to be associated with this ProblemDataFiles. */ public void setCustomInputValidatorFile(SerializedFile validatorFile) { this.inputValidatorFile = validatorFile; } - - + + private boolean compareSerializedFiles(SerializedFile oldFile, SerializedFile newFile) { if (oldFile == null) { @@ -295,7 +327,7 @@ private boolean compareSerializedFiles(SerializedFile oldFile, SerializedFile ne } return true; } - + private boolean compareSerializedFileArrays(SerializedFile[] oldList, SerializedFile[] newList) { if (oldList == null) { return(newList == null); @@ -315,7 +347,7 @@ private boolean compareSerializedFileArrays(SerializedFile[] oldList, Serialized } return true; } - + public boolean isSameAs(ProblemDataFiles newProblemDataFiles) { try { if (newProblemDataFiles == null) { @@ -333,9 +365,9 @@ public boolean isSameAs(ProblemDataFiles newProblemDataFiles) { if (!compareSerializedFiles(this.getCustomInputValidatorFile(), newProblemDataFiles.getCustomInputValidatorFile())) { return false; } - + // TODO 917 should compare the other problemDataFile fields too. - + return true; } catch (Exception e) { // TODO Log to static exception Log @@ -343,7 +375,7 @@ public boolean isSameAs(ProblemDataFiles newProblemDataFiles) { return false; } } - + @Override public String toString() { @@ -354,20 +386,20 @@ public String toString() { int numDataFiles = judgesDataFiles.length; int numAnsFiles = judgesAnswerFiles.length; - + buf.append(numDataFiles); buf.append(" data files, "); - + buf.append(numAnsFiles); buf.append(" answer files. "); - + buf.append("Data: "); SerializedFile [] list = judgesDataFiles; for (SerializedFile file : list) { buf.append(file.getName()); buf.append(" "); } - + buf.append("Answer: "); list = judgesAnswerFiles; for (SerializedFile file : list) { @@ -395,27 +427,27 @@ public String toString() { return buf.toString(); } - - + + /** * Returns the full path for judge data filenames. - * + * * Expected locations vary depending on the client type (Admin or Judge) and * whether the problem has external or internal files. - * + * * See {@link Utilities#getProblemfullFilenames(IInternalContest, Problem, SerializedFile[], String)} for details. - * + * *

      * Sample code: for Judge
      * String [] filenames = Utilities.getFullJudgesDataFilenames(contest, executable.getExecuteDirectoryName());
-     * 
+     *
      * Sample code: for Admin
      * String [] filenames = Utilities.getFullJudgesDataFilenames(contest, null);
-     * 
+     *
      * 
- * + * * @param contest - * @param executableDir + * @param executableDir */ public String[] getFullJudgesDataFilenames(IInternalContest contest, String executableDir) { return Utilities.fullJudgesDataFilenames(contest, this, executableDir); @@ -423,22 +455,22 @@ public String[] getFullJudgesDataFilenames(IInternalContest contest, String exec /** * Returns the full path for judge answer filenames. - * + * *
      * Sample code:
      * String [] filenames = Utilities.getFullJudgesAnswerFilenames(contest, executable.getExecuteDirectoryName());
-     * 
- * + * + * * @param contest * @param executableDir */ public String[] getFullJudgesAnswerFilenames(IInternalContest contest, String executableDir) { return Utilities.fullJudgesAnswerFilenames(contest, this, executableDir); } - + /** * Check for existence of all judge data and answer files, may create data files if needed. - * + * * Will not create files if problem has external files, {@link Problem#isUsingExternalDataFiles()} set true. *

* Will create judges answer and and data files (in exedcutableDir) if problem has internal files. @@ -447,8 +479,8 @@ public String[] getFullJudgesAnswerFilenames(IInternalContest contest, String ex *

      * Sample code:
      * checkAndCreateFiles(contest, executable.getExecuteDirectoryName());
-     * 
- * + * + * * @param contest * @param executableDir directory where internal files are expected. * @throws FileNotFoundException if external files not found or cannot create internal file @@ -507,7 +539,7 @@ public void checkAndCreateFiles(IInternalContest contest, String executableDir) /** * Remove data set from problem data sets. - * + * * @param index zero based data set row */ public void removeDataSet(int index) { diff --git a/src/edu/csus/ecs/pc2/core/scoring/DefaultPointScoringStandingsRecordComparator.java b/src/edu/csus/ecs/pc2/core/scoring/DefaultPointScoringStandingsRecordComparator.java new file mode 100644 index 000000000..0e910866c --- /dev/null +++ b/src/edu/csus/ecs/pc2/core/scoring/DefaultPointScoringStandingsRecordComparator.java @@ -0,0 +1,120 @@ +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.core.scoring; + +import java.io.Serializable; +import java.util.Comparator; + +import edu.csus.ecs.pc2.core.list.AccountList; +import edu.csus.ecs.pc2.core.list.AccountNameComparator; +import edu.csus.ecs.pc2.core.model.Account; + +/** + * Sorts StandingsRecord according to the Legacy Problem Package Format point scoring specification (such as it is) + * + * @author John Buck + */ + +// $HeadURL$ +public class DefaultPointScoringStandingsRecordComparator implements Serializable, Comparator { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private AccountNameComparator accountNameComparator = new AccountNameComparator(); + + private AccountList cachedAccountList; + + /** + * Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less + * than, equal to, or greater than the second. + *

+ * + * The implementor must ensure that sgn(compare(x, y)) == + * -sgn(compare(y, x)) for all x and y. + * (This implies that compare(x, y) must throw an exception if and only if compare(y, x) throws an + * exception.) + *

+ * + * The implementor must also ensure that the relation is transitive: + * ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0. + *

+ * + * Finally, the implementer must ensure that compare(x, y)==0 implies that + * sgn(compare(x, z))==sgn(compare(y, z)) for all z. + *

+ * + * It is generally the case, but not strictly required that (compare(x, y)==0) == (x.equals(y)). Generally + * speaking, any comparator that violates this condition should clearly indicate this fact. The recommended language is "Note: + * this comparator imposes orderings that are inconsistent with equals." + * + * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the + * second. + * @throws ClassCastException + * if the arguments' types prevent them from being compared by this Comparator. + */ + @Override + public int compare(StandingsRecord o1, StandingsRecord o2) { + int status = 0; + long aLastSolvedTime, bLastSolvedTime; + double aScore, bScore; + int aClientHash, bClientHash; + String nameA, nameB; + int nameComparison; + + StandingsRecord teamA = o1; + StandingsRecord teamB = o2; + aScore = teamA.getScore(); + aLastSolvedTime = teamA.getLastSolved(); + Account accountA = cachedAccountList.getAccount(teamA.getClientId()); + nameA = accountA.getDisplayName(); + aClientHash = teamA.getClientId().hashCode(); + bScore = teamB.getScore(); + bLastSolvedTime = teamB.getLastSolved(); + Account accountB = cachedAccountList.getAccount(teamB.getClientId()); + nameB = accountB.getDisplayName(); + bClientHash = teamB.getClientId().hashCode(); + nameComparison = accountNameComparator.compare(nameA.toLowerCase(), nameB.toLowerCase()); + + // + // Primary Sort = score (high to low) + // Secondary Sort = earliest submittal of last submission (low to high) + // Third Sort = teamName (low to high) + // Fourth Sort = clientId (low to high) + + if ((bScore == aScore) && (bLastSolvedTime == aLastSolvedTime) && (nameComparison == 0) + && (bClientHash == aClientHash)) { + status = 0; // elements equal, this shouldn't happen, Tammy... + } else { + // The sorting algorithm sorts things from "low to high" by default (ascending). + // The comparators should return a value as to whether the first thing (A) should be + // considered(!) less-than, equal-to or greater-than the 2nd thing (B). That is, + // does A appear AFTER B in the sorted result. + // For Point Scoring, the item with the biggest score should be the first thing + // in the result. When comparing 2 things (ascore and bscore), we have to determine + // if we want ascore to come after bscore. (IE if ascore is smaller than bscore, + // then A comes after B, and should be considered that A is bigger than B + // in terms of how the sorting is done, so we return 1, indicating that A should + // come after B in the sorted list. + if ((bScore > aScore) + || ((bScore == aScore) && (bLastSolvedTime < aLastSolvedTime)) + || ((bScore == aScore) && (bLastSolvedTime == aLastSolvedTime) && (nameComparison > 0)) + || ((bScore == aScore) && (bLastSolvedTime == aLastSolvedTime) + && (nameComparison == 0) && (bClientHash < aClientHash))) { + status = 1; // a to be considered greater than b + } else { + status = -1; // a to be considered less than b + } + } + return status; + } + + /** + * @param accountList + * The cachedAccountList to set. + */ + public void setCachedAccountList(AccountList accountList) { + this.cachedAccountList = accountList; + } +} diff --git a/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java b/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java index 9596e3fe0..97cbadccb 100644 --- a/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java +++ b/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; @@ -39,6 +40,7 @@ import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.Judgement; +import edu.csus.ecs.pc2.core.model.JudgementRecord; import edu.csus.ecs.pc2.core.model.Problem; import edu.csus.ecs.pc2.core.model.Run; import edu.csus.ecs.pc2.core.model.Run.RunStates; @@ -177,9 +179,10 @@ AccountList getAccountList(IInternalContest theContest) { /** * Get the Score and Statistics information for one problem. * - * @return pc2.ex.ProblemScoreData - * @param treeMap - * java.util.TreeMap + * @param treeMap java.util.TreeMap of filtered Runs + * @param theContest + * @return ProblemSummaryInfo + * @throws IllegalContestState */ private ProblemSummaryInfo calcProblemScoreData(TreeMap treeMap, IInternalContest theContest) throws IllegalContestState { ProblemSummaryInfo problemSummaryInfo = new ProblemSummaryInfo(); @@ -245,6 +248,73 @@ private ProblemSummaryInfo calcProblemScoreData(TreeMap treeMap, IInter return problemSummaryInfo; } + /** + * Get the Point Scoring and Statistics information for one problem. + * + * @param treeMap java.util.TreeMap of filtered Runs + * @param theContest + * @return ProblemSummaryInfo + * @throws IllegalContestState + */ + private ProblemSummaryInfo calcProblemPointScoreData(TreeMap treeMap, IInternalContest theContest) throws IllegalContestState { + ProblemSummaryInfo problemSummaryInfo = new ProblemSummaryInfo(); + double score = 0.0; + int attempts = 0; + ElementId problemId = null; + long solutionTime = -1; + boolean solved = false; + boolean unJudgedRun = false; + JudgementRecord judgmentRecord; + + if (treeMap.isEmpty()) { + problemSummaryInfo = null; // ProblemScoreData must have ProblemId to be valid + } else { + Collection coll = treeMap.values(); + Object[] o; + Run run; + o = coll.toArray(); + for (int i = 0; i < o.length; i++) { + run = (Run) o[i]; + // this should not have made it into the incoming treeMap + if (run.isDeleted()) { + continue; + } + attempts++; + problemId = run.getProblemId(); + // added isValidJudgement to check and obey preliminary results + if (run.isSolved() && isValidJudgement(run)) { + // TODO: we might want some differing logic here if all + // yes's are counted + // and/or no's after yes's are counted + solved = true; + solutionTime = run.getElapsedMins(); + judgmentRecord = run.getJudgementRecord(); + if(judgmentRecord != null) { + score = judgmentRecord.getScore(); + } + break; + } else { + // we should really only do this if it's been judged + if (!isValidJudgement(run)) { + unJudgedRun = true; + } + } + } + } + // TODO put another if around this if there was a setting to include all + // no's before yes + if (!solved) { + score = 0.0; + } + problemSummaryInfo.setSolved(solved); + problemSummaryInfo.setSolutionTime(solutionTime); + problemSummaryInfo.setProblemId(problemId); + problemSummaryInfo.setNumberSubmitted(attempts); + problemSummaryInfo.setScore(score); + problemSummaryInfo.setUnJudgedRuns(unJudgedRun); + return problemSummaryInfo; + } + /** * @param key * property to lookup @@ -479,11 +549,27 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi } // else no runs - applyScoringAdjustments(standingsRecordHash, accountList); + Comparator src; + + // Note: each of DefaultStandingsRecordComparator and DefaultPointScoringStandingsRecordComparator + // implements the java.util.Comparator interface. However, we need additional information in the + // comparator for StandingsRecord, namely, the accountList (for looking up names). This is why we + // instantiate each object separately, set the accountlist then assign to src. I suppose we could create + // another interface that extends java.util.Comparator without our method to set the cached account list. + if(theContest.getContestInformation().isScoreboardTypePassFail()) { + // Not applicable for point scoring contests, is it? JB + applyScoringAdjustments(standingsRecordHash, accountList); + DefaultStandingsRecordComparator srcPassFailRecordComparator = new DefaultStandingsRecordComparator(); + srcPassFailRecordComparator.setCachedAccountList(accountList); + src = srcPassFailRecordComparator; + + } else { + DefaultPointScoringStandingsRecordComparator srcPointScoringRecordComparator = new DefaultPointScoringStandingsRecordComparator(); + srcPointScoringRecordComparator.setCachedAccountList(accountList); + src = srcPointScoringRecordComparator; + } // use TreeMap to sort - DefaultStandingsRecordComparator src = new DefaultStandingsRecordComparator(); - src.setCachedAccountList(accountList); TreeMap treeMap = new TreeMap(src); Collection enumeration = standingsRecordHash.values(); for (StandingsRecord record : enumeration) { @@ -639,6 +725,8 @@ private void createStandingXML (TreeMap treeMa IInternalContest theContest, IMemento summaryMememento, boolean excludedGroups) { ContestInformation contestInformation = theContest.getContestInformation(); + boolean isPointScoring = contestInformation.isScoreboardTypeScore(); + // easy access Hashtable groupHash = new Hashtable(); Hashtable groupIndexHash = new Hashtable(); @@ -695,6 +783,7 @@ private void createStandingXML (TreeMap treeMa // assign the ranks long numSolved = -1, score = 0, lastSolved = 0; + double pointScore = 0.0; int rank = 0, indexRank = 0; int index = 0; // these are indexed by groupIndex @@ -703,6 +792,7 @@ private void createStandingXML (TreeMap treeMa groupNumSolved[i] = -1; } long[] groupScore = new long[groupCount]; + double[] groupPointScore = new double[groupCount]; long[] groupLastSolved = new long[groupCount]; int[] groupRank = new int[groupCount]; int[] groupIndexRank = new int[groupCount]; @@ -711,6 +801,7 @@ private void createStandingXML (TreeMap treeMa groupLastSolved[i] = 0; groupRank[i] = 0; groupIndexRank[i] = 0; + groupPointScore[i] = 0.0; } long[] divisionNumSolved = new long[divisionCount]; for (int i = 0; i < divisionCount; i++) { @@ -719,12 +810,14 @@ private void createStandingXML (TreeMap treeMa int[] divisionRank = new int[divisionCount]; int[] divisionIndexRank = new int[divisionCount]; long[] divisionScore = new long[divisionCount]; + double[] divisionPointScore = new double[divisionCount]; long[] divisionLastSolved = new long[divisionCount]; for (int i = 0; i < divisionCount; i++) { divisionRank[i] = 0; divisionIndexRank[i] = 0; divisionScore[i] = 0; divisionLastSolved[i] = 0; + divisionPointScore[i] = 0.0; } RunStatistics runStats = new RunStatistics(theContest); @@ -736,9 +829,10 @@ private void createStandingXML (TreeMap treeMa StandingsRecord standingsRecord = (StandingsRecord)o; indexRank++; - if (!isTeamTied(standingsRecord, numSolved, score, lastSolved)) { + if (!isTeamTied(standingsRecord, isPointScoring, pointScore, numSolved, score, lastSolved)) { numSolved = standingsRecord.getNumberSolved(); score = standingsRecord.getPenaltyPoints(); + pointScore = standingsRecord.getScore(); lastSolved = standingsRecord.getLastSolved(); rank = indexRank; standingsRecord.setRankNumber(rank); @@ -757,6 +851,7 @@ private void createStandingXML (TreeMap treeMa standingsRecordMemento.putLong("firstSolved", standingsRecord.getFirstSolved()); standingsRecordMemento.putLong("lastSolved", standingsRecord.getLastSolved()); standingsRecordMemento.putLong("points", standingsRecord.getPenaltyPoints()); + standingsRecordMemento.putDouble("score", standingsRecord.getScore()); standingsRecordMemento.putInteger("solved", standingsRecord.getNumberSolved()); standingsRecordMemento.putInteger("rank", teamRank); standingsRecordMemento.putInteger("overallRank", teamRank); @@ -797,9 +892,10 @@ private void createStandingXML (TreeMap treeMa int groupIndex = groupIndexHash.get(group).intValue(); // do the same thing as above, now for the group groupIndexRank[groupIndex]++; - if (!isTeamTied(standingsRecord,groupNumSolved[groupIndex], groupScore[groupIndex],groupLastSolved[groupIndex])) { + if (!isTeamTied(standingsRecord, isPointScoring, groupPointScore[groupIndex],groupNumSolved[groupIndex], groupScore[groupIndex],groupLastSolved[groupIndex])) { groupNumSolved[groupIndex] = standingsRecord.getNumberSolved(); groupScore[groupIndex] = standingsRecord.getPenaltyPoints(); + groupPointScore[groupIndex] = standingsRecord.getScore(); groupLastSolved[groupIndex] = standingsRecord.getLastSolved(); groupRank[groupIndex] = groupIndexRank[groupIndex]; standingsRecord.setGroupRankNumber(groupRank[groupIndex]); @@ -816,9 +912,10 @@ private void createStandingXML (TreeMap treeMa if (divisionIndexHash.containsKey(group)) { int divisionIndex = divisionIndexHash.get(group).intValue()-1; divisionIndexRank[divisionIndex]++; - if (!isTeamTied(standingsRecord, divisionNumSolved[divisionIndex], divisionScore[divisionIndex],divisionLastSolved[divisionIndex])) { + if (!isTeamTied(standingsRecord, isPointScoring, divisionPointScore[divisionIndex], divisionNumSolved[divisionIndex], divisionScore[divisionIndex],divisionLastSolved[divisionIndex])) { divisionNumSolved[divisionIndex] = standingsRecord.getNumberSolved(); divisionScore[divisionIndex] = standingsRecord.getPenaltyPoints(); + divisionPointScore[divisionIndex] = standingsRecord.getScore(); divisionLastSolved[divisionIndex] = standingsRecord.getLastSolved(); divisionRank[divisionIndex] = divisionIndexRank[divisionIndex]; standingsRecord.setDivisionRankNumber(divisionRank[divisionIndex]); @@ -846,6 +943,7 @@ private void createStandingXML (TreeMap treeMa psiMemento.putString("shortName", problems[problemsIndexHash.get(psi.getProblemId())-1].getShortName()); psiMemento.putInteger("attempts", psi.getNumberSubmitted()); psiMemento.putInteger("points", psi.getPenaltyPoints()); + psiMemento.putDouble("score", psi.getScore()); psiMemento.putLong("solutionTime", psi.getSolutionTime()); psiMemento.putBoolean("isSolved", psi.isSolved()); psiMemento.putBoolean("isPending", psi.isUnJudgedRuns()); @@ -984,12 +1082,18 @@ boolean isValidJudgement(Run run) { * @param lastSolved * @return True if the long parameters match the corresponding numbers in the StandingsRecord */ - boolean isTeamTied(StandingsRecord standingsRecord, long numSolved, long score, long lastSolved) { - if (numSolved != standingsRecord.getNumberSolved()) { - return false; - } - if (score != standingsRecord.getPenaltyPoints()) { - return false; + boolean isTeamTied(StandingsRecord standingsRecord, boolean isPointScoring, double pointScore, long numSolved, long score, long lastSolved) { + if(isPointScoring) { + if(pointScore != standingsRecord.getScore()) { + return false; + } + } else { + if (numSolved != standingsRecord.getNumberSolved()) { + return false; + } + if (score != standingsRecord.getPenaltyPoints()) { + return false; + } } if (lastSolved != standingsRecord.getLastSolved()) { return false; @@ -1058,18 +1162,32 @@ private void generateStandingsValues(final TreeMap runTreeMap, Hashtab // cannot be null for 1st run String lastUser = ""; String lastProblem = ""; + double dScore; + boolean isPointScoreContest = theContest.getContestInformation().isScoreboardTypeScore(); while (runIterator.hasNext()) { Object o = runIterator.next(); Run run = (Run) o; if (!lastUser.equals(run.getSubmitter().toString()) || !lastProblem.equals(run.getProblemId().toString())) { if (!problemTreeMap.isEmpty()) { - ProblemSummaryInfo problemSummaryInfo = calcProblemScoreData(problemTreeMap, theContest); + ProblemSummaryInfo problemSummaryInfo; + if(isPointScoreContest) { + problemSummaryInfo = calcProblemPointScoreData(problemTreeMap, theContest); + } else { + problemSummaryInfo = calcProblemScoreData(problemTreeMap, theContest); + } StandingsRecord standingsRecord = standingsHash.get(lastUser); SummaryRow summaryRow = standingsRecord.getSummaryRow(); summaryRow.put(problemsHash.get(problemSummaryInfo.getProblemId()), problemSummaryInfo); standingsRecord.setSummaryRow(summaryRow); - standingsRecord.setPenaltyPoints(standingsRecord.getPenaltyPoints() + problemSummaryInfo.getPenaltyPoints()); + if(isPointScoreContest) { + // we just sum them up + dScore = problemSummaryInfo.getScore(); + standingsRecord.setScore(standingsRecord.getScore() + dScore); + } else { + standingsRecord.setPenaltyPoints(standingsRecord.getPenaltyPoints() + problemSummaryInfo.getPenaltyPoints()); + } + if (problemSummaryInfo.isSolved()) { standingsRecord.setNumberSolved(standingsRecord.getNumberSolved() + 1); oldTime = standingsRecord.getLastSolved(); @@ -1093,12 +1211,25 @@ private void generateStandingsValues(final TreeMap runTreeMap, Hashtab // handle last run if (!problemTreeMap.isEmpty()) { - ProblemSummaryInfo problemSummaryInfo = calcProblemScoreData(problemTreeMap, theContest); + ProblemSummaryInfo problemSummaryInfo; + if(isPointScoreContest) { + problemSummaryInfo = calcProblemPointScoreData(problemTreeMap, theContest); + } else { + problemSummaryInfo = calcProblemScoreData(problemTreeMap, theContest); + } StandingsRecord standingsRecord = standingsHash.get(lastUser); SummaryRow summaryRow = standingsRecord.getSummaryRow(); summaryRow.put(problemsHash.get(problemSummaryInfo.getProblemId()), problemSummaryInfo); standingsRecord.setSummaryRow(summaryRow); - standingsRecord.setPenaltyPoints(standingsRecord.getPenaltyPoints() + problemSummaryInfo.getPenaltyPoints()); + if(isPointScoreContest) { + // we just care about the biggest score for the problem. + dScore = problemSummaryInfo.getScore(); + if(dScore > standingsRecord.getScore()) { + standingsRecord.setScore(dScore); + } + } else { + standingsRecord.setPenaltyPoints(standingsRecord.getPenaltyPoints() + problemSummaryInfo.getPenaltyPoints()); + } if (problemSummaryInfo.isSolved()) { standingsRecord.setNumberSolved(standingsRecord.getNumberSolved() + 1); oldTime = standingsRecord.getLastSolved(); @@ -1157,6 +1288,7 @@ private void initializeStandingsRecordHash(IInternalContest theContest, AccountL ProblemSummaryInfo problemSummaryInfo = new ProblemSummaryInfo(); problemSummaryInfo.setProblemId(problems[j].getElementId()); problemSummaryInfo.setPenaltyPoints(0); + problemSummaryInfo.setScore(0.0); summaryRow.put(j + 1, problemSummaryInfo); } standingsRecord.setSummaryRow(summaryRow); @@ -1188,6 +1320,7 @@ private IMemento createSummaryMomento(IInternalContest contest, XMLMemento memen memento.putString("systemVersion", versionInfo.getVersionNumber() + " build " + versionInfo.getBuildNumber()); memento.putString("systemURL", versionInfo.getSystemURL()); memento.putString("currentDate", new Date().toString()); + memento.putString("scoreType", contestInformation.getScoreboardType().toString().toLowerCase()); memento.putString("generatorId", "$Id$"); // bug 1540 String value = "Live (unfrozen) scoreboard"; diff --git a/src/edu/csus/ecs/pc2/core/scoring/ProblemSummaryInfo.java b/src/edu/csus/ecs/pc2/core/scoring/ProblemSummaryInfo.java index e7a5d4d6e..b3e5a28c5 100644 --- a/src/edu/csus/ecs/pc2/core/scoring/ProblemSummaryInfo.java +++ b/src/edu/csus/ecs/pc2/core/scoring/ProblemSummaryInfo.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.scoring; import java.io.Serializable; @@ -7,7 +7,7 @@ /** * Problem (Scoring) Summary Info. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -16,7 +16,7 @@ public class ProblemSummaryInfo implements Serializable { /** - * + * */ private static final long serialVersionUID = -4157597862536104668L; @@ -28,6 +28,8 @@ public class ProblemSummaryInfo implements Serializable { private int penaltyPoints = 0; + private double score = 0.0; + private boolean unJudgedRuns = false; private int pendingRunCount = 0; @@ -35,7 +37,7 @@ public class ProblemSummaryInfo implements Serializable { private int judgedRunCount = 0; private ElementId problemId = null; - + private String shortName = ""; /** @@ -52,11 +54,11 @@ public ElementId getProblemId() { public void setProblemId(edu.csus.ecs.pc2.core.model.ElementId problemId) { this.problemId = problemId; } - + public String getShortName() { return shortName; } - + public void setShortName(String shortName) { this.shortName = shortName; } @@ -145,4 +147,18 @@ public int getJudgedRunCount() { return judgedRunCount; } + /** + * @return the score + */ + public double getScore() { + return score; + } + + /** + * @param score the score to set + */ + public void setScore(double score) { + this.score = score; + } + } diff --git a/src/edu/csus/ecs/pc2/core/scoring/StandingsRecord.java b/src/edu/csus/ecs/pc2/core/scoring/StandingsRecord.java index e1b8be745..2ff105e15 100644 --- a/src/edu/csus/ecs/pc2/core/scoring/StandingsRecord.java +++ b/src/edu/csus/ecs/pc2/core/scoring/StandingsRecord.java @@ -1,10 +1,11 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.scoring; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import edu.csus.ecs.pc2.api.BaseClient; import edu.csus.ecs.pc2.core.model.ClientId; /** @@ -31,13 +32,19 @@ public class StandingsRecord { * Rank within the Group. */ private int groupRankNumber = 0; - + /** * Penalty Points. * */ private long penaltyPoints; + /** + * Point score + * + */ + private double score; + /** * Number of problems solved. */ @@ -192,14 +199,14 @@ public int getGroupRankNumber() { public void setGroupRankNumber(int groupRankNumber) { this.groupRankNumber = groupRankNumber; } - + /** * Returns a String representation of this object in JSON format. */ @Override public String toString() { ObjectMapper mapper = new ObjectMapper(); - + String jsonString = "{}"; try { jsonString = mapper.writeValueAsString(this); @@ -220,5 +227,18 @@ public void setDivisionRankNumber(int divisionRankNumber) { this.divisionRankNumber = divisionRankNumber; } + /** + * @return the score + */ + public double getScore() { + return score; + } + + /** + * @param score the score to set + */ + public void setScore(double score) { + this.score = score; + } } diff --git a/src/edu/csus/ecs/pc2/core/standings/ProblemSummaryInfo.java b/src/edu/csus/ecs/pc2/core/standings/ProblemSummaryInfo.java index 1c9480927..24ea2fcf1 100644 --- a/src/edu/csus/ecs/pc2/core/standings/ProblemSummaryInfo.java +++ b/src/edu/csus/ecs/pc2/core/standings/ProblemSummaryInfo.java @@ -38,6 +38,9 @@ public class ProblemSummaryInfo { @XmlAttribute private String points; + @XmlAttribute + private String score; + @XmlAttribute private String problemId; @@ -111,4 +114,18 @@ public void setSolutionTime(String solutionTime) { this.solutionTime = solutionTime; } + /** + * @return the score + */ + public String getScore() { + return score; + } + + /** + * @param score the score to set + */ + public void setScore(String score) { + this.score = score; + } + } diff --git a/src/edu/csus/ecs/pc2/core/standings/TeamStanding.java b/src/edu/csus/ecs/pc2/core/standings/TeamStanding.java index afa7e19ad..92fd2a34b 100644 --- a/src/edu/csus/ecs/pc2/core/standings/TeamStanding.java +++ b/src/edu/csus/ecs/pc2/core/standings/TeamStanding.java @@ -14,12 +14,12 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; /** - * This class defines an entry (one for each team) which appears in a List of TeamStandings in a {@link ContestStandings} object. + * This class defines an entry (one for each team) which appears in a List of TeamStandings in a {@link ContestStandings} object. * (A {@link ContestStandings} consists of a single {@link StandingsHeader} followed by a List of {@link TeamStanding}s.) * This class is used as a target during conversion (deserialization) of an XML representation of a TeamStanding into a POJO. - * - * Note that the @JsonIgnoreProperties(ignoreUnknown=true) annotation is supplied in the event the XML returned by the - * DefaultScoringAlgorithm class (which is frequently converted to a ContestStandings object using, for example, + * + * Note that the @JsonIgnoreProperties(ignoreUnknown=true) annotation is supplied in the event the XML returned by the + * DefaultScoringAlgorithm class (which is frequently converted to a ContestStandings object using, for example, * the Jackson XMLMapper class), contains attributes which this class doesn't define. * * @author Douglas A. Lane, John Clevenger, @@ -50,6 +50,9 @@ public class TeamStanding { @XmlAttribute private String points; + @XmlAttribute + private String score; + @XmlAttribute private String problemsAttempted; @@ -266,8 +269,18 @@ public String getTotalAttempts() { public void setTotalAttempts(String totalAttempts) { this.totalAttempts = totalAttempts; } - - + /** + * @return the score + */ + public String getScore() { + return score; + } + /** + * @param score the score to set + */ + public void setScore(String score) { + this.score = score; + } } diff --git a/src/edu/csus/ecs/pc2/core/util/IMemento.java b/src/edu/csus/ecs/pc2/core/util/IMemento.java index c2e91b333..b67e6e2d9 100644 --- a/src/edu/csus/ecs/pc2/core/util/IMemento.java +++ b/src/edu/csus/ecs/pc2/core/util/IMemento.java @@ -1,11 +1,11 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. /********************************************************************** * Copyright (c) 2003, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html - * + * * Contributors: * IBM Corporation - Initial API and implementation **********************************************************************/ @@ -34,7 +34,7 @@ *

* This interface is not intended to be implemented by clients. *

- * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -43,7 +43,7 @@ public interface IMemento { /** * Special reserved key used to store the memento id (value "org.eclipse.ui.id"). - * + * * @see #getId */ String TAG_ID = "IMemento.internal.id"; //$NON-NLS-1$ @@ -53,7 +53,7 @@ public interface IMemento { *

* The getChild and getChildren methods are used to retrieve children of a given type. *

- * + * * @param type * the type * @return a new child memento @@ -68,7 +68,7 @@ public interface IMemento { *

* The getChild and getChildren methods are used to retrieve children of a given type. *

- * + * * @param type * the type * @param id @@ -80,7 +80,7 @@ public interface IMemento { /** * Returns the first child with the given type id. - * + * * @param type * the type id * @return the first child with the given type @@ -89,7 +89,7 @@ public interface IMemento { /** * Returns all children with the given type id. - * + * * @param type * the type id * @return the list of children with the given type @@ -98,16 +98,25 @@ public interface IMemento { /** * Returns the floating point value of the given key. - * + * * @param key * the key * @return the value, or null if the key was not found or was found but was not a floating point number */ Float getFloat(String key); + /** + * Returns the double precision point value of the given key. + * + * @param key + * the key + * @return the value, or null if the key was not found or was found but was not a double precision point number + */ + Double getDouble(String key); + /** * Returns the id for this memento. - * + * * @return the memento id, or null if none * @see #createChild(java.lang.String,java.lang.String) */ @@ -115,7 +124,7 @@ public interface IMemento { /** * Returns the name for this memento. - * + * * @return the memento name, or null if none * @see #createChild(java.lang.String,java.lang.String) */ @@ -123,7 +132,7 @@ public interface IMemento { /** * Returns the integer value of the given key. - * + * * @param key * the key * @return the value, or null if the key was not found or was found but was not an integer @@ -132,7 +141,7 @@ public interface IMemento { /** * Returns the long value of the given key. - * + * * @param key * the key * @return the value, or null if the key was not found or was found but was not an integer @@ -141,7 +150,7 @@ public interface IMemento { /** * Returns the string value of the given key. - * + * * @param key * the key * @return the value, or null if the key was not found or was found but was not an integer @@ -150,7 +159,7 @@ public interface IMemento { /** * Returns the boolean value of the given key. - * + * * @param key * the key * @return the value, or null if the key was not found or was found but was not a boolean @@ -159,14 +168,14 @@ public interface IMemento { /** * Return the list of names. - * + * * @return a possibly empty list of names */ List getNames(); /** * Sets the value of the given key to the given floating point number. - * + * * @param key * the key * @param value @@ -174,9 +183,19 @@ public interface IMemento { */ void putFloat(String key, float value); + /** + * Sets the value of the given key to the given double precision point number. + * + * @param key + * the key + * @param value + * the value + */ + void putDouble(String key, double value); + /** * Sets the value of the given key to the given integer. - * + * * @param key * the key * @param value @@ -186,7 +205,7 @@ public interface IMemento { /** * Sets the value of the given key to the given long. - * + * * @param key * the key * @param value @@ -196,7 +215,7 @@ public interface IMemento { /** * Sets the value of the given key to the given boolean value. - * + * * @param key * the key * @param value @@ -206,7 +225,7 @@ public interface IMemento { /** * Copy the attributes and children from memento to the receiver. - * + * * @param memento * the IMemento to be copied. */ @@ -214,7 +233,7 @@ public interface IMemento { /** * Sets the value of the given key to the given string. - * + * * @param key * the key * @param value @@ -224,17 +243,17 @@ public interface IMemento { /** * Create an element with a value. - * + * * {@link #createChild(String)} and {@link #createChild(String, String)} do not create an XML element with a value. *

* For example createChild("name", "Jenny") will create the following XML: - * + * *

 <name IMemento.internal.id="Jenny>
- * + * * This method places a value into the element, for example to create and element use the following createChildNode("name", "Jenny") - * + * *
 <name>Jenny</name>
- * + * * @see #getValue() * @param name * name for element @@ -246,11 +265,11 @@ public interface IMemento { /** * Get value/String for element. - * + * * Returns the value for the element. Ex. - * + * *
 <name>Troy</name> 
- * + * * getValue() would return: Troy *

* @see #createChildNode(String, String) diff --git a/src/edu/csus/ecs/pc2/core/util/XMLMemento.java b/src/edu/csus/ecs/pc2/core/util/XMLMemento.java index bd95208fc..9aeacbd14 100644 --- a/src/edu/csus/ecs/pc2/core/util/XMLMemento.java +++ b/src/edu/csus/ecs/pc2/core/util/XMLMemento.java @@ -1,11 +1,11 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. /******************************************************************************* * Copyright (c) 2003, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html - * + * * Contributors: * IBM Corporation - Initial API and implementation *******************************************************************************/ @@ -49,9 +49,9 @@ * 3) The class for an object may change. If so the new class should be able to read the old persistence info. *

* We could ask the objects to serialize themselves into an ObjectOutputStream, DataOutputStream, or Hashtable. However all of these approaches fail to meet the second requirement. - * + * * Memento supports binary persistance with a version ID. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -73,6 +73,7 @@ private XMLMemento(Document doc, Element el) { /** * @see IMemento#createChild(String) */ + @Override public IMemento createChild(String type) { Element child = factory.createElement(type); element.appendChild(child); @@ -82,6 +83,7 @@ public IMemento createChild(String type) { /** * @see IMemento#createChildNode(java.lang.String, java.lang.String) */ + @Override public IMemento createChildNode(String type, String value) { Element child = factory.createElement(type); @@ -93,6 +95,7 @@ public IMemento createChildNode(String type, String value) { /** * @see IMemento#createChild(String, String) */ + @Override public IMemento createChild(String type, String id) { Element child = factory.createElement(type); child.setAttribute(TAG_ID, id); @@ -131,7 +134,7 @@ protected static XMLMemento createReadRoot(InputStream in) { /** * Answer a root memento for writing a document. - * + * * @param type * a type * @return a memento @@ -151,6 +154,7 @@ public static XMLMemento createWriteRoot(String type) { /* * @see IMemento */ + @Override public IMemento getChild(String type) { // Get the nodes. NodeList nodes = element.getChildNodes(); @@ -177,6 +181,7 @@ public IMemento getChild(String type) { /* * @see IMemento */ + @Override public IMemento[] getChildren(String type) { // Get the nodes. NodeList nodes = element.getChildNodes(); @@ -201,14 +206,14 @@ public IMemento[] getChildren(String type) { size = list.size(); IMemento[] results = new IMemento[size]; for (int x = 0; x < size; x++) { - results[x] = new XMLMemento(factory, (Element) list.get(x)); + results[x] = new XMLMemento(factory, list.get(x)); } return results; } /** * Return the contents of this memento as a byte array. - * + * * @return byte[] * @throws IOException * if anything goes wrong @@ -221,7 +226,7 @@ public byte[] getContents() throws IOException { /** * Returns an input stream for writing to the disk with a local locale. - * + * * @return java.io.InputStream * @throws IOException * if anything goes wrong @@ -235,6 +240,7 @@ public InputStream getInputStream() throws IOException { /* * @see IMemento */ + @Override public Float getFloat(String key) { Attr attr = element.getAttributeNode(key); if (attr == null) { @@ -251,6 +257,24 @@ public Float getFloat(String key) { /* * @see IMemento */ + @Override + public Double getDouble(String key) { + Attr attr = element.getAttributeNode(key); + if (attr == null) { + return null; + } + String strValue = attr.getValue(); + try { + return new Double(strValue); + } catch (NumberFormatException e) { + return null; + } + } + + /* + * @see IMemento + */ + @Override public String getId() { return element.getAttribute(TAG_ID); } @@ -258,6 +282,7 @@ public String getId() { /* * @see IMemento */ + @Override public String getName() { return element.getNodeName(); } @@ -265,6 +290,7 @@ public String getName() { /* * @see IMemento */ + @Override public Integer getInteger(String key) { Attr attr = element.getAttributeNode(key); if (attr == null) { @@ -281,6 +307,7 @@ public Integer getInteger(String key) { /* * @see IMemento */ + @Override public Long getLong(String key) { Attr attr = element.getAttributeNode(key); if (attr == null) { @@ -297,6 +324,7 @@ public Long getLong(String key) { /* * @see IMemento */ + @Override public String getString(String key) { Attr attr = element.getAttributeNode(key); if (attr == null) { @@ -305,6 +333,7 @@ public String getString(String key) { return attr.getValue(); } + @Override public List getNames() { NamedNodeMap map = element.getAttributes(); int size = map.getLength(); @@ -319,7 +348,7 @@ public List getNames() { /** * Loads a memento from the given filename. - * + * * @param filename * java.lang.String * @exception java.io.IOException @@ -369,6 +398,15 @@ private void putElement(Element element2) { /* * @see IMemento */ + @Override + public void putDouble(String key, double d) { + element.setAttribute(key, String.valueOf(d)); + } + + /* + * @see IMemento + */ + @Override public void putFloat(String key, float f) { element.setAttribute(key, String.valueOf(f)); } @@ -376,6 +414,7 @@ public void putFloat(String key, float f) { /* * @see IMemento */ + @Override public void putInteger(String key, int n) { element.setAttribute(key, String.valueOf(n)); } @@ -383,6 +422,7 @@ public void putInteger(String key, int n) { /* * @see IMemento */ + @Override public void putLong(String key, long n) { element.setAttribute(key, Long.toString(n)); } @@ -390,6 +430,7 @@ public void putLong(String key, long n) { /* * @see IMemento */ + @Override public void putMemento(IMemento memento) { XMLMemento xmlMemento = (XMLMemento) memento; putElement(xmlMemento.element); @@ -398,6 +439,7 @@ public void putMemento(IMemento memento) { /* * @see IMemento */ + @Override public void putString(String key, String value) { if (value == null) { return; @@ -407,7 +449,7 @@ public void putString(String key, String value) { /** * Save this Memento to a Writer. - * + * * @param os * an output stream * @throws IOException @@ -416,10 +458,10 @@ public void putString(String key, String value) { public void save(OutputStream os) throws IOException { save(os, false); } - + /** * Save this Memento to a Writer. - * + * * @param os * an output stream * @param omitXMLDeclaration @@ -446,10 +488,10 @@ public void save(OutputStream os, boolean omitXMLDeclaration) throws IOException throw (IOException) (new IOException().initCause(e)); } } - + /** * Saves the memento to a String. - * + * * @exception java.io.IOException */ public String saveToString(boolean omitXMLDeclaration) throws IOException { @@ -460,7 +502,7 @@ public String saveToString(boolean omitXMLDeclaration) throws IOException { /** * Saves the memento to a String. - * + * * @exception java.io.IOException */ public String saveToString() throws IOException { @@ -469,7 +511,7 @@ public String saveToString() throws IOException { /** * Saves the memento to the given file. - * + * * @param filename * java.lang.String * @exception java.io.IOException @@ -500,6 +542,7 @@ public void saveToFile(String filename) throws IOException { /* * @see IMemento#getBoolean(String) */ + @Override public Boolean getBoolean(String key) { Attr attr = element.getAttributeNode(key); if (attr == null) { @@ -515,6 +558,7 @@ public Boolean getBoolean(String key) { /* * @see IMemento#putBoolean(String, boolean) */ + @Override public void putBoolean(String key, boolean value) { element.setAttribute(key, Boolean.toString(value)); } @@ -522,10 +566,11 @@ public void putBoolean(String key, boolean value) { /** * @see edu.csus.ecs.pc2.core.util.IMemento#getValue() */ + @Override public String getValue() { NodeList list = element.getChildNodes(); - Node node = (Node) list.item(0); + Node node = list.item(0); return node.getNodeValue(); } } diff --git a/src/edu/csus/ecs/pc2/graders/LegacyGrader.java b/src/edu/csus/ecs/pc2/graders/LegacyGrader.java new file mode 100644 index 000000000..9ddb9d5ee --- /dev/null +++ b/src/edu/csus/ecs/pc2/graders/LegacyGrader.java @@ -0,0 +1,350 @@ +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.graders; + +import java.util.ArrayList; +import java.util.Scanner; + +/** + * This class performs the functions described in the Problem Package Format "Legacy" specification + * for the "Default Grader" + * + * @author John Buck + * + */ +public class LegacyGrader { + + public static final String IGNORE_SAMPLE_FLAG = "ignore_sample"; + + public static final String ACCEPT_IF_ANY_ACCEPTED_FLAG = "accept_if_any_accepted"; + + public static final int GRADER_ERROR_BAD_FORMAT = 1; + public static final int GRADER_ERROR_BAD_SCORE = 2; + public static final int GRADER_ERROR_BAD_JUDGMENT = 3; + public static final int GRADER_ERROR_NO_TEST_CASES = 4; + public static final int GRADER_ERROR_BAD_WORST_CODE = 5; + public static final int GRADER_ERROR_BAD_FIRST_CODE = 6; + public static final int GRADER_ERROR_BAD_VERDICT_MODE = 7; + public static final int GRADER_ERROR_IGNORE_SAMPLE = 8; + + enum VerdictMode { + worst_error, + first_error, + always_accept; + } + + enum ScoringMode { + sum, + avg, + min, + max; + } + + enum JudgmentCodes { + AC, // MUST be first (index 0) + RTE, + TLE, + WA; + } + + private VerdictMode verdictMode = VerdictMode.worst_error; + private ScoringMode scoringMode = ScoringMode.sum; + private boolean acceptIfAnyAccepted = false; + private boolean ignoreSample = false; + private int graderError = 0; + + /** + * See if the supplied string argument is a valid verdict mode. + * + * @param arg string to check for a verdict mode + * @return true if the argument supplied was accepted as the verdict mode, false otherwise. + */ + private boolean checkVerdictMode(String arg) { + boolean result = false; + + for(VerdictMode vm : VerdictMode.values()) { + if(arg.equals(vm.toString().toLowerCase())) { + verdictMode = vm; + result = true; + break; + } + } + return result; + } + + /** + * See if the supplied string argument is a valid scoring mode. + * + * @param arg string to check for a scoring mode + * @return true if the argument supplied was accepted as the scoring mode, false otherwise. + */ + private boolean checkScoringMode(String arg) { + boolean result = false; + + for(ScoringMode sm : ScoringMode.values()) { + if(arg.equals(sm.toString().toLowerCase())) { + scoringMode = sm; + result = true; + break; + } + } + return result; + } + + /** + * See if the supplied string argument is a flag. + * + * @param arg string to check for a flag + * @return true if the argument supplied was accepted as a valid flag, false otherwise. + */ + private boolean checkFlags(String arg) + { + boolean result = false; + + if(arg.equals(ACCEPT_IF_ANY_ACCEPTED_FLAG)) { + acceptIfAnyAccepted = true; + result = true; + } else if(arg.equals(IGNORE_SAMPLE_FLAG)) { + ignoreSample = true; + result = true; + } + return(result); + } + + /** + * Process an array of string arguments for the grader. + * Note: This can be called directly if this package is included in a larger application + * + * @param args Array of arguments (see the Legacy Problem Package Format specification for Graders) + * @return true if there were no errors, false otherwise + */ + public boolean parseArguments(String [] args) { + boolean result = true; + + for(String arg : args ) { + if(!checkVerdictMode(arg) && !checkScoringMode(arg) && !checkFlags(arg)) { + result = false; + break; + } + } + return result; + } + + /** + * Read the stdin input for lines of test case judgments, create a list of them, + * and evaluate according the specification. + * + * @return 0 on success (this is the exit code for the program), non-zero indicates a judging error + */ + public int processResults() { + Scanner scanner = new Scanner(System.in); + ArrayList testCaseResults = new ArrayList(); + + while(scanner.hasNextLine()) { + String line = scanner.nextLine().trim(); + if(!line.isEmpty()) { + testCaseResults.add(line); + } + } + graderError = 0; + String result = gradeTestCases(testCaseResults); + + if(result != null) { + System.out.println(result); + } else { + System.out.println("JE 0"); + } + return(graderError); + } + + public String gradeTestCases(ArrayList testCaseResults) { + int nLine = 0; + int idx; + boolean found; + double scoreSum = 0; + double scoreCount = 0; + double scoreMin = Double.POSITIVE_INFINITY; + double scoreMax = Double.NEGATIVE_INFINITY; + boolean [] sawJudgment = new boolean[JudgmentCodes.values().length]; + boolean anyFailures = false; + boolean ignoreSampleGroup = ignoreSample; + String firstError = null; + String graderResult = null; + + graderError = 0; + for(String line : testCaseResults) { + nLine++; + /* + * A little explanation here about ignoring samples. + * The Legacy Grader specification says: + * "Must only be used on the root level. The first sub-result (sample) + * will be ignored, the second sub-result (secret) will be used, + * both verdict and score. + * If the ignore_sample command line flag was supplied, it implies the group + * being graded is the root level, as such, there will be exactly 2 sub-groups + * at the level, sample and secret (in that order - lexicographically). Therefore, + * the first group (sample) will simply be ignored (skipped). Grading will procede + * with the next line, which will be secret. + */ + if(ignoreSampleGroup) { + // Ignore sample group which is the first one. There better be exactly one more + // group in the list, or it's a judging error. + if(testCaseResults.size() != 2) { + graderError = GRADER_ERROR_IGNORE_SAMPLE; + break; + } + ignoreSampleGroup = false; + continue; + } + String [] values = line.trim().split("\\s+"); + if(values.length != 2) { + System.err.println("LegacyGrader: invalid input test case " + nLine); + graderError = GRADER_ERROR_BAD_FORMAT; + break; + } + // be generous and accept upper or lower case for judgment + String code = values[0].toUpperCase(); + double score = 0; + try { + score = Double.parseDouble(values[1]); + } catch(NumberFormatException e) { + System.err.println("LegacyGrader: invalid score for test case " + nLine); + graderError = GRADER_ERROR_BAD_SCORE; + break; + } + found = false; + // We now have the possible code and the score + for(JudgmentCodes jcode : JudgmentCodes.values()) { + if(code.equals(jcode.toString())) { + idx = jcode.ordinal(); + // Tally score - we do this even for reject cases(!) + scoreSum += score; + scoreCount += 1; + + // keep track of min and max score + if(score < scoreMin) { + scoreMin = score; + } + if(score > scoreMax) { + scoreMax = score; + } + + // check for first failure + if(idx > 0) { + if(firstError == null) { + firstError = jcode.toString(); + } + anyFailures = true; + } + sawJudgment[idx] = true; + found = true; + break; + } + } + if(!found) { + System.err.println("LegacyGrader: Unknown judgment code " + code + " for test case " + nLine); + graderError = GRADER_ERROR_BAD_JUDGMENT; + break; + } + if(graderError != 0) { + break; + } + } + // Only calculate judgment if there were no errors + if(graderError == 0) { + // Check for no failures or always accept mode or optional flag "any" accepted case + if(!anyFailures || verdictMode == VerdictMode.always_accept || (acceptIfAnyAccepted && scoreCount > 0)) { + if(scoreCount == 0) { + // this there were no judgments in the input + System.err.println("LegacyGrader: No judgments in the input."); + graderError = GRADER_ERROR_NO_TEST_CASES; + } else { + double score = 0; + // In this case, we return a score + switch(scoringMode) { + case sum: // add'm up + score = scoreSum; + break; + case avg: // calculate mean + score = scoreSum / scoreCount; + break; + case min: // the smallest score + score = scoreMin; + break; + case max: // the biggest score + score = scoreMax; + break; + } + graderResult = JudgmentCodes.AC.toString() + " " + score; + } + } else { + // determine non-accepted judgment + // All cases should either print the correct output to stdout, or print + // an error to stderr and set graderError to a non-zero value. + switch(verdictMode) { + case worst_error: + found = false; + for(JudgmentCodes jcode : JudgmentCodes.values()) { + idx = jcode.ordinal(); + if(idx > 0 && sawJudgment[idx]) { + System.out.println(jcode.toString() + " 0"); + found = true; + break; + } + } + if(!found) { + // Uhm. This is extremely bad, Tammy, since we KNOW anyFailures must be true + System.err.println("LegacyGrader: FATAL error - can not find judgment code for worst_error mode."); + graderError = GRADER_ERROR_BAD_WORST_CODE; + } + break; + + case first_error: + if(firstError == null) { + // Uhm. This is extremely bad took Tammy, since we know anyFailures must be true + System.err.println("LegacyGrader: FATAL error - can not find judgment code for first_error mode."); + graderError = GRADER_ERROR_BAD_FIRST_CODE; + } else { + System.out.println(firstError + " 0"); + } + break; + + default: + // Very bad, Tammy. This can't happen because alwaysAccept was handled above + System.err.println("LegacyGrader: FATAL error - invalid verdict mode " + verdictMode.toString()); + graderError = GRADER_ERROR_BAD_VERDICT_MODE; + break; + } + } + } + // this will be null in the case of an error, in which case graderError will have the error code + // in the case of success, this will be the "judgment_acronym score", eg. "AC 50" + return graderResult; + } + + /** + * Returns the last error the grader saw, or 0 if no errors. + * This may be useful if the getTestCases() method returns null. + * + * @return the last grader error + */ + int getGraderError() { + return graderError; + } + + /** + * @param args - optional combination of a VerdictMode, ScoringMode and Flag(s) + */ + public static void main(String[] args) { + LegacyGrader grader = new LegacyGrader(); + int exitCode = 0; + + if(!grader.parseArguments(args)) { + System.err.println("LegacyGrader: Unrecognized option supplied."); + exitCode = 1; + } else { + exitCode = grader.processResults(); + } + System.exit(exitCode); + } + +} diff --git a/src/edu/csus/ecs/pc2/imports/ccs/ContestImportUtilities.java b/src/edu/csus/ecs/pc2/imports/ccs/ContestImportUtilities.java new file mode 100644 index 000000000..495b3413b --- /dev/null +++ b/src/edu/csus/ecs/pc2/imports/ccs/ContestImportUtilities.java @@ -0,0 +1,308 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.imports.ccs; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; + +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.Mark; +import org.yaml.snakeyaml.error.MarkedYAMLException; + +import edu.csus.ecs.pc2.core.StringUtilities; +import edu.csus.ecs.pc2.core.exception.YamlLoadException; + +/** + * Utilities to help with importing contest data + * + * @author John Buck, PC^2 Team, pc2@ecs.csus.edu + */ +public class ContestImportUtilities { + /** + * Get boolean value for input key in map. + * + * Returns defaultVaue if no entry matches key. + * + * @param content + * @param key + * @param defaultValue + * @return defaultValue or value from item in map. + */ + public static boolean fetchBooleanValue(Map content, String key, boolean defaultValue) { + Object object = content.get(key); + Boolean value = false; + if (object == null) { + return defaultValue; + } else if (object instanceof Boolean) { + value = (Boolean) content.get(key); + } else if (object instanceof String) { + value = StringUtilities.getBooleanValue((String) content.get(key), defaultValue); + } + return value; + } + + public static Object fetchObjectValue(Map content, String key) { + if (content == null) { + return null; + } + Object value = content.get(key); + return value; + } + + @SuppressWarnings("unused") + private boolean fetchBooleanValue(Map content, String key) { + return fetchBooleanValue(content, key, false); + } + + /** + * Fetch value from a map. + * + * @param content + * @param key + * @return null if content does not contain a value for the key, else the value for the key. + */ + public static String fetchValue(Map content, String key) { + if (content == null) { + return null; + } + Object value = content.get(key); + if (value == null) { + return null; + } else if (value instanceof String) { + return (String) content.get(key); + } else { + return content.get(key).toString(); + } + } + + public static String fetchValue(Map content, String key, String defaultValue) { + if (content == null) { + return null; + } + Object value = content.get(key); + if (value == null) { + return defaultValue; + } else if (value instanceof String) { + return (String) content.get(key); + } else { + return content.get(key).toString(); + } + } + + + public static boolean isValuePresent(Map content, String key) { + if (content == null) { + return false; + } + Object value = content.get(key); + return value != null; + } + + public static String fetchValueDefault(Map map, String key, String defaultValue) { + String value = fetchValue(map, key); + if (value == null) { + value = defaultValue; + } + return value; + } + + public static Integer fetchIntValue(Map map, String key, int defaultValue) { + Integer value = null; + if (map != null) { + value = (Integer) map.get(key); + } + if (value != null) { + try { + return value; + } catch (Exception e) { + syntaxError("Expecting number after " + key + ": field, found '" + value + "'"); + } + } + return defaultValue; + } + + public static Long fetchLongValue(Map map, String key, long defaultValue) { + Long value = null; + if (map != null) { + value = (Long) map.get(key); + } + if (value != null) { + try { + return value; + } catch (Exception e) { + syntaxError("Expecting number after " + key + ": field, found '" + value + "'"); + } + } + return defaultValue; + } + + public static Double fetchDoubleValue(Map map, String key) { + Double value = null; + if (map != null) { + Object oVal = map.get(key); + if(oVal instanceof Integer) { + value = Double.valueOf(((Integer)oVal).doubleValue()); + } else { + value = (Double) oVal; + } + if (value != null) { + try { + return value; + } catch (Exception e) { + syntaxError("Expecting double after " + key + ": field, found '" + value + "'"); + } + } + } + return value; + } + + + public static Integer fetchIntValue(Map map, String key) { + if (map == null) { + // SOMEDAY figure out why map would every be null + return null; + } + Integer value = (Integer) map.get(key); + if (value != null) { + try { + return value; + } catch (Exception e) { + syntaxError("Expecting number after " + key + ": field, found '" + value + "'"); + } + } + return null; + } + + @SuppressWarnings("unchecked") + public static Map fetchMap(Map content, String key) { + Object object = content.get(key); + if (object != null) { + if (object instanceof Map) { + return (Map) content.get(key); + } else { + return null; + } + } else { + return null; + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static String[] fetchStringList(String[] yamlLines, String key) { + Map content = loadYaml(null, yamlLines); + ArrayList list = fetchList(content, key); + if (list == null) { + return new String[0]; + } else { + return (String[]) list.toArray(new String[list.size()]); + } + } + + + public static Map getContent(String filename) { + return(loadYaml(filename)); + } + + @SuppressWarnings("rawtypes") + public static ArrayList fetchList(Map content, String key) { + return (ArrayList) content.get(key); + } + + public static String fetchValue(File file, String key) { + Map content = getContent(file.getAbsolutePath()); + return (String) content.get(key); + } + + public static String fetchFileValue(String filename, String key) { + return fetchValue(new File(filename), key); + } + + public static void syntaxError(String string) { + YamlLoadException exception = new YamlLoadException("Syntax error: " + string); + throw exception; + } + @SuppressWarnings("unchecked") + public static Map loadYaml(String filename) { + try { + Yaml yaml = new Yaml(); + return (Map) yaml.load(new FileInputStream(filename)); + } catch (MarkedYAMLException e) { + throw new YamlLoadException(getSnakeParserDetails(e), e, filename); + } catch (FileNotFoundException e) { + throw new YamlLoadException("File not found " + filename, e, filename); + } + } + + @SuppressWarnings("unchecked") + public static Map loadYaml(String filename, String[] yamlLines) { + try { + Yaml yaml = new Yaml(); + String fullString = StringUtilities.join("\n", yamlLines); + InputStream stream = new ByteArrayInputStream(fullString.getBytes(StandardCharsets.UTF_8)); + return (Map) yaml.load(stream); + } catch (MarkedYAMLException e) { + throw new YamlLoadException(getSnakeParserDetails(e), e, filename); + } + } + + /** + * read directory and return list of files with given extension + * @param directoryName to search + * @param extension to look for + * @return array of filenames + */ + public static ArrayList getTestCaseFileNames(String directoryName) { + + ArrayList list = new ArrayList(); + File dir = new File(directoryName); + + String[] entries = dir.list(); + HashSet fileNames = new HashSet(); + String ansFile; + + if (entries != null) { + Arrays.sort(entries); + + for (String name : entries) { + fileNames.add(name); + } + for (String name : entries) { + if (name.endsWith(TestCaseInfo.TEST_CASE_INPUT_EXTENSION)) { + // the answer file better be there + ansFile = name.replaceAll(TestCaseInfo.TEST_CASE_INPUT_EXTENSION + "$", TestCaseInfo.TEST_CASE_ANSWER_EXTENSION); + if(!fileNames.contains(ansFile)) { + throw new YamlLoadException("Missing answer file " + ansFile + " for input file " + name); + } + list.add(new TestCaseInfo(name, ansFile, null)); + } + } + } + return list; + } + + /** + * Create a simple string with parse info. + * + * @param markedYAMLException + * @return + */ + public static String getSnakeParserDetails(MarkedYAMLException markedYAMLException) { + + Mark mark = markedYAMLException.getProblemMark(); + + int lineNumber = mark.getLine() + 1; // starts at zero + int columnNumber = mark.getColumn() + 1; // starts at zero + + return "Parse error at line=" + lineNumber + " column=" + columnNumber + " message=" + markedYAMLException.getProblem(); + + } + + +} diff --git a/src/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoader.java b/src/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoader.java index 94a58d810..2f487a599 100644 --- a/src/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoader.java +++ b/src/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoader.java @@ -1,13 +1,9 @@ // Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.imports.ccs; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -23,10 +19,6 @@ import javax.xml.bind.DatatypeConverter; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.error.Mark; -import org.yaml.snakeyaml.error.MarkedYAMLException; - import edu.csus.ecs.pc2.core.Constants; import edu.csus.ecs.pc2.core.JudgementLoader; import edu.csus.ecs.pc2.core.StringUtilities; @@ -76,58 +68,13 @@ */ public class ContestSnakeYAMLLoader implements IContestLoader { - /** - * Full content of yaml file. - */ - private Map fullYamlContent = null; + private String judgesCDPDataPath = null; /** * Load Problem Data File Contents */ private boolean loadProblemDataFiles = true; - @SuppressWarnings("unchecked") - public Map loadYaml(String filename) { - try { - Yaml yaml = new Yaml(); - return (Map) yaml.load(new FileInputStream(filename)); - } catch (MarkedYAMLException e) { - throw new YamlLoadException(getSnakeParserDetails(e), e, filename); - } catch (FileNotFoundException e) { - throw new YamlLoadException("File not found " + filename, e, filename); - } - } - - @SuppressWarnings("unchecked") - public Map loadYaml(String filename, String[] yamlLines) { - try { - Yaml yaml = new Yaml(); - String fullString = StringUtilities.join("\n", yamlLines); - InputStream stream = new ByteArrayInputStream(fullString.getBytes(StandardCharsets.UTF_8)); - return (Map) yaml.load(stream); - } catch (MarkedYAMLException e) { - throw new YamlLoadException(getSnakeParserDetails(e), e, filename); - } - } - - /** - * Create a simple string with parse info. - * - * @param markedYAMLException - * @return - */ - - String getSnakeParserDetails(MarkedYAMLException markedYAMLException) { - - Mark mark = markedYAMLException.getProblemMark(); - - int lineNumber = mark.getLine() + 1; // starts at zero - int columnNumber = mark.getColumn() + 1; // starts at zero - - return "Parse error at line=" + lineNumber + " column=" + columnNumber + " message=" + markedYAMLException.getProblem(); - - } - @Override public IInternalContest fromYaml(IInternalContest contest, String directoryName) { return fromYaml(contest, directoryName, true); @@ -219,34 +166,20 @@ public String getContestTitle(String contestYamlFilename) throws IOException { File contestYaml = new File(contestYamlFilename); // Try CLICS name first. Fun fact: CLICS_CONTEST_NAME == CONTEST_NAME_KEY, but may not someday - String contestTitle = fetchValue(contestYaml, IContestLoader.CLICS_CONTEST_NAME); + String contestTitle = ContestImportUtilities.fetchValue(contestYaml, IContestLoader.CLICS_CONTEST_NAME); // only if the CLICS name isn't there do we try the old one. non-null means it is there. if(contestTitle == null) { - contestTitle = fetchValue(contestYaml, IContestLoader.CONTEST_NAME_KEY); + contestTitle = ContestImportUtilities.fetchValue(contestYaml, IContestLoader.CONTEST_NAME_KEY); } return(contestTitle); } - protected String fetchValue(File file, String key) { - Map content = getContent(file.getAbsolutePath()); - return (String) content.get(key); - } - - private Map getContent(String filename) { - if (fullYamlContent == null) { - fullYamlContent = loadYaml(filename); - } - - return fullYamlContent; - } - @Override public String getJudgesCDPBasePath(String contestYamlFilename) throws IOException { - return fetchFileValue(contestYamlFilename, JUDGE_CONFIG_PATH_KEY); - } - - private String fetchFileValue(String filename, String key) { - return fetchValue(new File(filename), key); + if(judgesCDPDataPath == null) { + judgesCDPDataPath = ContestImportUtilities.fetchFileValue(contestYamlFilename, JUDGE_CONFIG_PATH_KEY); + } + return(judgesCDPDataPath); } /** @@ -424,6 +357,11 @@ private void setContestStartDateTime(IInternalContest contest, Date date) { contestInformation.setAutoStartContest(isBeforeNow(date)); } + private void setContestScoreboardType(IInternalContest contest, String type) { + ContestInformation contestInformation = contest.getContestInformation(); + contestInformation.setScoreboardType(type); + } + /** * Input date before now, aka current date/time. * @@ -444,7 +382,7 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S String contestFileName = getContestYamlFilename(directoryName); - Map content = loadYaml(contestFileName, yamlLines); + Map content = ContestImportUtilities.loadYaml(contestFileName, yamlLines); if (content == null) { return contest; @@ -452,20 +390,20 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S setTitle(contest, null); - String contestTitle = fetchValue(content, CONTEST_NAME_KEY); + String contestTitle = ContestImportUtilities.fetchValue(content, CONTEST_NAME_KEY); if (contestTitle != null) { setTitle(contest, contestTitle); } - boolean ccsTestMode = fetchBooleanValue(content, CCS_TEST_MODE, false); + boolean ccsTestMode = ContestImportUtilities.fetchBooleanValue(content, CCS_TEST_MODE, false); if (ccsTestMode) { setCcsTestMode(contest, ccsTestMode); } - boolean loadSamples = fetchBooleanValue(content, LOAD_SAMPLE_JUDGES_DATA, true); + boolean loadSamples = ContestImportUtilities.fetchBooleanValue(content, LOAD_SAMPLE_JUDGES_DATA, true); setLoadSampleJudgesData(contest, loadSamples); - boolean stopOnFirstFail = fetchBooleanValue(content, STOP_ON_FIRST_FAILED_TEST_CASE_KEY, false); + boolean stopOnFirstFail = ContestImportUtilities.fetchBooleanValue(content, STOP_ON_FIRST_FAILED_TEST_CASE_KEY, false); setStopOnFirstFailedTestCase (contest, stopOnFirstFail); /** @@ -475,65 +413,65 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S ContestInformation contestInformation = getContestInformation(contest); //set allow-multiple-team-logins mode - boolean allowMultipleTeamLogins = fetchBooleanValue(content, ALLOW_MULTIPLE_TEAM_LOGINS_KEY, contestInformation.isAllowMultipleLoginsPerTeam()); + boolean allowMultipleTeamLogins = ContestImportUtilities.fetchBooleanValue(content, ALLOW_MULTIPLE_TEAM_LOGINS_KEY, contestInformation.isAllowMultipleLoginsPerTeam()); contestInformation.setAllowMultipleLoginsPerTeam(allowMultipleTeamLogins); // Load team scoreboard string (the one with variables) - String teamScoreboadDisplayString = fetchValue(content, TEAM_SCOREBOARD_DISPLAY_FORMAT_STRING, contestInformation.getTeamScoreboardDisplayFormat()); + String teamScoreboadDisplayString = ContestImportUtilities.fetchValue(content, TEAM_SCOREBOARD_DISPLAY_FORMAT_STRING, contestInformation.getTeamScoreboardDisplayFormat()); contestInformation.setTeamScoreboardDisplayFormat(teamScoreboadDisplayString); // enable shadow mode - boolean shadowMode = fetchBooleanValue(content, SHADOW_MODE_KEY, contestInformation.isShadowMode()); + boolean shadowMode = ContestImportUtilities.fetchBooleanValue(content, SHADOW_MODE_KEY, contestInformation.isShadowMode()); contestInformation.setShadowMode(shadowMode); - String altAccountsLoadFilename = fetchValue(content, LOAD_ACCOUNTS_FILE_KEY, null); + String altAccountsLoadFilename = ContestImportUtilities.fetchValue(content, LOAD_ACCOUNTS_FILE_KEY, null); contestInformation.setOverrideLoadAccountsFilename(altAccountsLoadFilename); // base URL for CCS REST service - String ccsUrl= fetchValue(content, CCS_URL_KEY, contestInformation.getPrimaryCCS_URL()); + String ccsUrl= ContestImportUtilities.fetchValue(content, CCS_URL_KEY, contestInformation.getPrimaryCCS_URL()); contestInformation.setPrimaryCCS_URL(ccsUrl); // CCS REST login - String ccsLogin = fetchValue(content, CCS_LOGIN_KEY, contestInformation.getPrimaryCCS_user_login()); + String ccsLogin = ContestImportUtilities.fetchValue(content, CCS_LOGIN_KEY, contestInformation.getPrimaryCCS_user_login()); contestInformation.setPrimaryCCS_user_login(ccsLogin); // CCS REST password - String ccsPassoword = fetchValue(content, CCS_PASSWORD_KEY, contestInformation.getPrimaryCCS_user_pw()); + String ccsPassoword = ContestImportUtilities.fetchValue(content, CCS_PASSWORD_KEY, contestInformation.getPrimaryCCS_user_pw()); contestInformation.setPrimaryCCS_user_pw(ccsPassoword); - String lastEventId = fetchValue(content, CCS_LAST_EVENT_ID_KEY, contestInformation.getLastShadowEventID()); + String lastEventId = ContestImportUtilities.fetchValue(content, CCS_LAST_EVENT_ID_KEY, contestInformation.getLastShadowEventID()); contestInformation.setLastShadowEventID(lastEventId); - String executeDir = fetchValue(content, EXECUTE_FOLDER, contestInformation.getExecuteFolder()); + String executeDir = ContestImportUtilities.fetchValue(content, EXECUTE_FOLDER, contestInformation.getExecuteFolder()); contestInformation.setExecuteFolder(executeDir); // save ContesInformation to model contest.updateContestInformation(contestInformation); - String judgeCDPath = fetchValue(content, JUDGE_CONFIG_PATH_KEY); + String judgeCDPath = ContestImportUtilities.fetchValue(content, JUDGE_CONFIG_PATH_KEY); if (judgeCDPath != null) { setCDPPath(contest, judgeCDPath); } else { setCDPPath(contest, directoryName); } - Integer defaultTimeout = fetchIntValue(content, TIMEOUT_KEY, DEFAULT_TIME_OUT); + Integer defaultTimeout = ContestImportUtilities.fetchIntValue(content, TIMEOUT_KEY, DEFAULT_TIME_OUT); int currentGlobalMemoryLimit = getMemoryLimitMB(contest); - Integer globalMemoryLimit = fetchIntValue(content, MEMORY_LIMIT_IN_MEG_KEY, currentGlobalMemoryLimit); + Integer globalMemoryLimit = ContestImportUtilities.fetchIntValue(content, MEMORY_LIMIT_IN_MEG_KEY, currentGlobalMemoryLimit); if(currentGlobalMemoryLimit != globalMemoryLimit) { setMemoryLimitMB(contest, globalMemoryLimit); } int currentSandboxGraceTime = getSandboxGraceTimeSecs(contest); - Integer sandboxGraceTime = fetchIntValue(content, SANDBOX_GRACE_TIME, currentSandboxGraceTime); + Integer sandboxGraceTime = ContestImportUtilities.fetchIntValue(content, SANDBOX_GRACE_TIME, currentSandboxGraceTime); if(currentSandboxGraceTime != sandboxGraceTime) { setSandboxGraceTimeSecs(contest, sandboxGraceTime); } int currentSandboxIntMult = getSandboxInteractiveTimeMultiplier(contest); - Integer sandboxIntMult = fetchIntValue(content, SANDBOX_INTERACTIVE_GRACE_MULTIPLIER, currentSandboxIntMult); + Integer sandboxIntMult = ContestImportUtilities.fetchIntValue(content, SANDBOX_INTERACTIVE_GRACE_MULTIPLIER, currentSandboxIntMult); if(currentSandboxIntMult != sandboxIntMult) { setSandboxInteractiveTimeMultiplier(contest, sandboxIntMult); } @@ -545,9 +483,9 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S } } - loadDataFileContents = fetchBooleanValue(content, PROBLEM_LOAD_DATA_FILES_KEY, loadDataFileContents); + loadDataFileContents = ContestImportUtilities.fetchBooleanValue(content, PROBLEM_LOAD_DATA_FILES_KEY, loadDataFileContents); - String shortContestName = fetchValue(content, CLICS_CONTEST_ID); + String shortContestName = ContestImportUtilities.fetchValue(content, CLICS_CONTEST_ID); // Check if id is CLICS compliant if (!StringUtilities.isEmpty(shortContestName)) { @@ -563,13 +501,13 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S } } else { // only if CLICS id is not present do we try older key `short-name` - shortContestName = fetchValue(content, SHORT_NAME_KEY); + shortContestName = ContestImportUtilities.fetchValue(content, SHORT_NAME_KEY); shortContestName = StringUtilities.makeStringCLICSCompliant(shortContestName); } // only if both CLICS id and `short-name` is not present do we try the key `name` if (StringUtilities.isEmpty(shortContestName)) { - shortContestName = fetchValue(content, CLICS_CONTEST_NAME); + shortContestName = ContestImportUtilities.fetchValue(content, CLICS_CONTEST_NAME); shortContestName = StringUtilities.makeStringCLICSCompliant(shortContestName); } // only set short name if string is present AND not empty @@ -579,25 +517,25 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S StaticLog.warning("None of CLICS id, name and short-name is present. Contest Identifier will be set as Default-{:random_number}."); } - if (null != fetchValue(content, AUTO_STOP_CLOCK_AT_END_KEY)) { + if (null != ContestImportUtilities.fetchValue(content, AUTO_STOP_CLOCK_AT_END_KEY)) { // only set value if key present - boolean autoStopClockAtEnd = fetchBooleanValue(content, AUTO_STOP_CLOCK_AT_END_KEY, false); + boolean autoStopClockAtEnd = ContestImportUtilities.fetchBooleanValue(content, AUTO_STOP_CLOCK_AT_END_KEY, false); setAutoStopClockAtEnd(contest, autoStopClockAtEnd); } - String contestLength = fetchValue(content, CLICS_CONTEST_DURATION); + String contestLength = ContestImportUtilities.fetchValue(content, CLICS_CONTEST_DURATION); // if CLICS duration not present, try old duration. // note as of CLICS spec 2022-07, the CLICS key is the same as the old one // we leave the old test here in case the CLICS key changes at some point. if(contestLength == null) { - contestLength = fetchValue(content, CONTEST_DURATION_KEY); + contestLength = ContestImportUtilities.fetchValue(content, CONTEST_DURATION_KEY); } if (contestLength != null) { setContestLength(contest, contestLength); } - boolean isRunning = fetchBooleanValue(content, "running", false); + boolean isRunning = ContestImportUtilities.fetchBooleanValue(content, "running", false); if (isRunning){ ContestTime time = contest.getContestTime(); if (time == null) { @@ -611,16 +549,16 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S // There are several ways to specify the freeze length. We try them here // in order of preference: CLICS, old, new (does that make sense? shouldn't // new be tried before old? -- JB) - String scoreboardFreezeTime = fetchValue(content, CLICS_CONTEST_FREEZE_DURATION); + String scoreboardFreezeTime = ContestImportUtilities.fetchValue(content, CLICS_CONTEST_FREEZE_DURATION); // This is absolutely ridiculous, but backward compatible *sigh* if(scoreboardFreezeTime == null) { // Old yaml name - scoreboardFreezeTime = fetchValue(content, SCOREBOARD_FREEZE_KEY); + scoreboardFreezeTime = ContestImportUtilities.fetchValue(content, SCOREBOARD_FREEZE_KEY); if(scoreboardFreezeTime == null) { // New yaml name - scoreboardFreezeTime = fetchValue(content, SCOREBOARD_FREEZE_LENGTH_KEY); + scoreboardFreezeTime = ContestImportUtilities.fetchValue(content, SCOREBOARD_FREEZE_LENGTH_KEY); } } // Only set time if not null or empty @@ -628,10 +566,10 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S setScoreboardFreezeTime(contest, scoreboardFreezeTime); } - Object startTimeObject = fetchObjectValue(content, CLICS_CONTEST_START_TIME); + Object startTimeObject = ContestImportUtilities.fetchObjectValue(content, CLICS_CONTEST_START_TIME); // if clics start time not present(!), try the old one if(startTimeObject == null) { - startTimeObject = fetchObjectValue(content, CONTEST_START_TIME_KEY); + startTimeObject = ContestImportUtilities.fetchObjectValue(content, CONTEST_START_TIME_KEY); } Date date = null; @@ -639,10 +577,10 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S setContestStartDateTime(contest, (Date) startTimeObject); } else { - String startTime = fetchValue(content, CLICS_CONTEST_START_TIME); + String startTime = ContestImportUtilities.fetchValue(content, CLICS_CONTEST_START_TIME); // only if CLICS start time is NOT there do we try the old one if(startTime == null) { - startTime = fetchValue(content, CONTEST_START_TIME_KEY); + startTime = ContestImportUtilities.fetchValue(content, CONTEST_START_TIME_KEY); } if (startTime != null) { @@ -680,11 +618,20 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S } // If the contest type is present in contest.yaml, verify it - String scoreType = fetchValue(content, CLICS_CONTEST_SCOREBOARD_TYPE); - if(scoreType != null && !scoreType.equals("pass-fail")) { - throw new YamlLoadException("Invalid " + CLICS_CONTEST_SCOREBOARD_TYPE + ": " + scoreType + ", expected pass-fail"); + String scoreType = ContestImportUtilities.fetchValue(content, CLICS_CONTEST_SCOREBOARD_TYPE); + if(scoreType != null) { + if(!scoreType.equals(CLICS_CONTEST_SCOREBOARD_TYPE_PASSFAIL) + && !scoreType.equals(CLICS_CONTEST_SCOREBOARD_TYPE_SCORE)) { + throw new YamlLoadException("Invalid " + CLICS_CONTEST_SCOREBOARD_TYPE + ": " + + scoreType + ", expected " + + CLICS_CONTEST_SCOREBOARD_TYPE_PASSFAIL + + " or " + + CLICS_CONTEST_SCOREBOARD_TYPE_SCORE); + } + setContestScoreboardType(contest, scoreType); } - Object privatehtmlOutputDirectory = fetchObjectValue(content, OUTPUT_PRIVATE_SCORE_DIR_KEY); + + Object privatehtmlOutputDirectory = ContestImportUtilities.fetchObjectValue(content, OUTPUT_PRIVATE_SCORE_DIR_KEY); if (privatehtmlOutputDirectory != null) { if (privatehtmlOutputDirectory instanceof String) { setScoringPropertyValue(contest, DefaultScoringAlgorithm.JUDGE_OUTPUT_DIR, (String) privatehtmlOutputDirectory); @@ -693,7 +640,7 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S } } - Object publichtmlOutputDirectory = fetchObjectValue(content, OUTPUT_PUBLIC_SCORE_DIR_KEY); + Object publichtmlOutputDirectory = ContestImportUtilities.fetchObjectValue(content, OUTPUT_PUBLIC_SCORE_DIR_KEY); if (publichtmlOutputDirectory != null) { if (publichtmlOutputDirectory instanceof String) { setScoringPropertyValue(contest, DefaultScoringAlgorithm.PUBLIC_OUTPUT_DIR, (String) publichtmlOutputDirectory); @@ -702,7 +649,7 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S } } - Object maxOutputSize = fetchObjectValue(content, MAX_OUTPUT_SIZE_K_KEY); + Object maxOutputSize = ContestImportUtilities.fetchObjectValue(content, MAX_OUTPUT_SIZE_K_KEY); if (maxOutputSize != null) { if (maxOutputSize instanceof Integer) { @@ -722,9 +669,9 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S contest.addLanguage(language); } - String defaultValidatorCommandLine = fetchValue(content, DEFAULT_VALIDATOR_KEY); + String defaultValidatorCommandLine = ContestImportUtilities.fetchValue(content, DEFAULT_VALIDATOR_KEY); - String overrideValidatorCommandLine = fetchValue(content, OVERRIDE_VALIDATOR_KEY); + String overrideValidatorCommandLine = ContestImportUtilities.fetchValue(content, OVERRIDE_VALIDATOR_KEY); if (overrideValidatorCommandLine == null) { // if no override defined, then maybe use mtsv @@ -739,7 +686,7 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S } boolean overrideUsePc2Validator = false; - String usingValidator = fetchValue(content, IContestLoader.USING_PC2_VALIDATOR); + String usingValidator = ContestImportUtilities.fetchValue(content, IContestLoader.USING_PC2_VALIDATOR); if (usingValidator != null && usingValidator.equalsIgnoreCase("true")) { overrideUsePc2Validator = true; @@ -753,9 +700,9 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S @SuppressWarnings("unchecked") LinkedHashMap judgingTypeContent = (LinkedHashMap) content.get(JUDGING_TYPE_KEY); if (judgingTypeContent != null) { - manualReviewOverride = fetchBooleanValue(judgingTypeContent, MANUAL_REVIEW_KEY, false); + manualReviewOverride = ContestImportUtilities.fetchBooleanValue(judgingTypeContent, MANUAL_REVIEW_KEY, false); } else { - manualReviewOverride = fetchBooleanValue(content, MANUAL_REVIEW_KEY, false); + manualReviewOverride = ContestImportUtilities.fetchBooleanValue(content, MANUAL_REVIEW_KEY, false); } //get the current default global output size for the contest so getProblems() can use it if no @@ -812,17 +759,17 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S Account[] accounts = getAccounts(yamlLines); - Map passwordYamlMap = fetchMap(content, "passwords"); + Map passwordYamlMap = ContestImportUtilities.fetchMap(content, "passwords"); if (passwordYamlMap != null) { - String passTypeString = fetchValueDefault(passwordYamlMap, "type", PasswordType2.LETTERS_AND_DIGITS.toString()); + String passTypeString = ContestImportUtilities.fetchValueDefault(passwordYamlMap, "type", PasswordType2.LETTERS_AND_DIGITS.toString()); PasswordType2 passwordType = PasswordType2.valueOf(passTypeString.toUpperCase()); - String lengthString = fetchValueDefault(passwordYamlMap, "length", "8"); + String lengthString = ContestImportUtilities.fetchValueDefault(passwordYamlMap, "length", "8"); int length = getIntegerValue(lengthString, 8); - String prefix = fetchValueDefault(passwordYamlMap, "prefix", ""); + String prefix = ContestImportUtilities.fetchValueDefault(passwordYamlMap, "prefix", ""); /** * Assign team passwords @@ -837,9 +784,9 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S /** * Override output directory for files */ - targetDirectory = fetchValueDefault(passwordYamlMap, "outdirname", targetDirectory); + targetDirectory = ContestImportUtilities.fetchValueDefault(passwordYamlMap, "outdirname", targetDirectory); - String passfilename = fetchValueDefault(passwordYamlMap, "passfile", targetDirectory + File.separator + MailMergeFile.PASSWORD_LIST_FILENNAME); + String passfilename = ContestImportUtilities.fetchValueDefault(passwordYamlMap, "passfile", targetDirectory + File.separator + MailMergeFile.PASSWORD_LIST_FILENNAME); /** * Write OS login passwords file (just a list of passwords in a text file) @@ -847,7 +794,7 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S generateOSPasswords(passfilename, updatedAccounts.length, passwordType, length, prefix); - String mergefilename = fetchValueDefault(passwordYamlMap, "mergefile", targetDirectory + File.separator + MailMergeFile.DEFAULT_MERGE_OUTPUT_FILENAME); + String mergefilename = ContestImportUtilities.fetchValueDefault(passwordYamlMap, "mergefile", targetDirectory + File.separator + MailMergeFile.DEFAULT_MERGE_OUTPUT_FILENAME); try { /** @@ -891,7 +838,7 @@ public IInternalContest fromYaml(IInternalContest contest, String[] yamlLines, S contest.updateAccount(account); // System.out.println("debug Added proxy account "+account.getClientId().toString()); } else { - syntaxError("No such account for proxy of " + clientId.getClientType().toString() + " " + clientId.getClientNumber() + " at site " + clientId.getSiteNumber()); + ContestImportUtilities.syntaxError("No such account for proxy of " + clientId.getClientType().toString() + " " + clientId.getClientNumber() + " at site " + clientId.getSiteNumber()); ; } } @@ -1069,7 +1016,7 @@ private void setScoreboardFreezeTime(IInternalContest contest, String scoreboard if (scoreboardFreezeTime.length() > 2) { long seconds = parseTimeIntoSeconds(scoreboardFreezeTime, -1); if(seconds == -1) { - syntaxError("Failed to parse scoreboard freeze time `" + scoreboardFreezeTime + "`, expected seconds or HH:MM:SS"); + ContestImportUtilities.syntaxError("Failed to parse scoreboard freeze time `" + scoreboardFreezeTime + "`, expected seconds or HH:MM:SS"); } scoreboardFreezeTime = ContestTime.formatTime(seconds); } @@ -1128,29 +1075,6 @@ private void setShortContestNameAndIdentifier(IInternalContest contest, String s contest.setContestIdentifier(shortContestName); } - /** - * Get boolean value for input key in map. - * - * Returns defaultVaue if no entry matches key. - * - * @param content - * @param key - * @param defaultValue - * @return defaultValue or value from item in map. - */ - private boolean fetchBooleanValue(Map content, String key, boolean defaultValue) { - Object object = content.get(key); - Boolean value = false; - if (object == null) { - return defaultValue; - } else if (object instanceof Boolean) { - value = (Boolean) content.get(key); - } else if (object instanceof String) { - value = getBooleanValue((String) content.get(key), defaultValue); - } - return value; - } - private void addAutoJudgeSetting(IInternalContest contest, AutoJudgeSetting auto) { Account account = contest.getAccount(auto.getClientId()); @@ -1176,21 +1100,21 @@ private Account[] getAccounts(String[] yamlLines) { Vector accountVector = new Vector(); AccountList accountList = new AccountList(); - Map yamlContent = loadYaml(null, yamlLines); - ArrayList> list = fetchList(yamlContent, ACCOUNTS_KEY); + Map yamlContent = ContestImportUtilities.loadYaml(null, yamlLines); + ArrayList> list = ContestImportUtilities.fetchList(yamlContent, ACCOUNTS_KEY); if (list != null) { for (Object object : list) { Map map = (Map) object; - String accountType = fetchValue(map, "account"); + String accountType = ContestImportUtilities.fetchValue(map, "account"); checkField(accountType, "Account Type"); ClientType.Type type = ClientType.Type.valueOf(accountType.trim()); - Integer startNumber = fetchIntValue(map, "start", 1); - Integer count = fetchIntValue(map, "count", 1); - Integer siteNumber = fetchIntValue(map, "site", 1); + Integer startNumber = ContestImportUtilities.fetchIntValue(map, "start", 1); + Integer count = ContestImportUtilities.fetchIntValue(map, "count", 1); + Integer siteNumber = ContestImportUtilities.fetchIntValue(map, "site", 1); /** *

@@ -1221,82 +1145,29 @@ private Account[] getAccounts(String[] yamlLines) {
         return loader.getAccountsArray();
 
     }
-
-    private Object fetchObjectValue(Map content, String key) {
-        if (content == null) {
-            return null;
-        }
-        Object value = content.get(key);
-        return value;
-    }
-
-    /**
-     * Fetch value from a map.
-     *
-     * @param content
-     * @param key
-     * @return null if content does not contain a value for the key, else the value for the key.
-     */
-    private String fetchValue(Map content, String key) {
-        if (content == null) {
-            return null;
-        }
-        Object value = content.get(key);
-        if (value == null) {
-            return null;
-        } else if (value instanceof String) {
-            return (String) content.get(key);
-        } else {
-            return content.get(key).toString();
-        }
-    }
-
-    private String fetchValue(Map content, String key, String defaultValue) {
-        if (content == null) {
-            return null;
-        }
-        Object value = content.get(key);
-        if (value == null) {
-            return defaultValue;
-        } else if (value instanceof String) {
-            return (String) content.get(key);
-        } else {
-            return content.get(key).toString();
-        }
-    }
-
-
-    private boolean isValuePresent(Map content, String key) {
-        if (content == null) {
-            return false;
-        }
-        Object value = content.get(key);
-        return value != null;
-    }
-
     @SuppressWarnings("unchecked")
     @Override
     public PlaybackInfo getReplaySettings(String[] yamlLines) {
 
         PlaybackInfo info = new PlaybackInfo();
 
-        Map yamlContent = loadYaml(null, yamlLines);
-        ArrayList> list = fetchList(yamlContent, REPLAY_KEY);
+        Map yamlContent = ContestImportUtilities.loadYaml(null, yamlLines);
+        ArrayList> list = ContestImportUtilities.fetchList(yamlContent, REPLAY_KEY);
 
         if (list != null) {
             Map map = list.get(0);
 
-            String siteTitle = fetchValue(map, "title");
+            String siteTitle = ContestImportUtilities.fetchValue(map, "title");
 
-            String filename = fetchValue(map, "file");
+            String filename = ContestImportUtilities.fetchValue(map, "file");
 
-            boolean started = fetchBooleanValue(map, "auto_start", false);
+            boolean started = ContestImportUtilities.fetchBooleanValue(map, "auto_start", false);
 
-            Integer waitTimeBetweenEventsMS = fetchIntValue(map, "pacingMS", 1000);
+            Integer waitTimeBetweenEventsMS = ContestImportUtilities.fetchIntValue(map, "pacingMS", 1000);
 
-            Integer minEvents = fetchIntValue(map, "minevents", 1);
+            Integer minEvents = ContestImportUtilities.fetchIntValue(map, "minevents", 1);
 
-            Integer siteNumber = fetchIntValue(map, "site");
+            Integer siteNumber = ContestImportUtilities.fetchIntValue(map, "site");
 
             // Site site = new Site(siteTitle, siteNumber);
 
@@ -1312,17 +1183,12 @@ public PlaybackInfo getReplaySettings(String[] yamlLines) {
 
     }
 
-    @SuppressWarnings("unused")
-    private boolean fetchBooleanValue(Map content, String key) {
-        return fetchBooleanValue(content, key, false);
-    }
-
     @SuppressWarnings("unchecked")
     @Override
     public Site[] getSites(String[] yamlLines) {
 
-        Map yamlContent = loadYaml(null, yamlLines);
-        ArrayList> list = fetchList(yamlContent, SITES_KEY);
+        Map yamlContent = ContestImportUtilities.loadYaml(null, yamlLines);
+        ArrayList> list = ContestImportUtilities.fetchList(yamlContent, SITES_KEY);
         ArrayList sitesVector = new ArrayList();
 
         if (list != null) {
@@ -1333,16 +1199,16 @@ public Site[] getSites(String[] yamlLines) {
                  * 
 sites: - number: 1 name: Site 1 IP: localhost port: 50002 
*/ - String siteTitle = fetchValue(map, "name"); + String siteTitle = ContestImportUtilities.fetchValue(map, "name"); - Integer siteNumber = fetchIntValue(map, "number"); + Integer siteNumber = ContestImportUtilities.fetchIntValue(map, "number"); Site site = new Site(siteTitle, siteNumber); - String hostName = fetchValueDefault(map, "IP", ""); - Integer portString = fetchIntValue(map, "port"); + String hostName = ContestImportUtilities.fetchValueDefault(map, "IP", ""); + Integer portString = ContestImportUtilities.fetchIntValue(map, "port"); - String password = fetchValue(map, "password"); + String password = ContestImportUtilities.fetchValue(map, "password"); if (password == null) { password = "site" + siteNumber.toString(); } @@ -1364,74 +1230,6 @@ public Site[] getSites(String[] yamlLines) { } - private String fetchValueDefault(Map map, String key, String defaultValue) { - String value = fetchValue(map, key); - if (value == null) { - value = defaultValue; - } - return value; - } - - private Integer fetchIntValue(Map map, String key, int defaultValue) { - Integer value = null; - if (map != null) { - value = (Integer) map.get(key); - } - if (value != null) { - try { - return value; - } catch (Exception e) { - syntaxError("Expecting number after " + key + ": field, found '" + value + "'"); - } - } - return defaultValue; - } - - private Long fetchLongValue(Map map, String key, long defaultValue) { - Long value = null; - if (map != null) { - value = (Long) map.get(key); - } - if (value != null) { - try { - return value; - } catch (Exception e) { - syntaxError("Expecting number after " + key + ": field, found '" + value + "'"); - } - } - return defaultValue; - } - - private Integer fetchIntValue(Map map, String key) { - if (map == null) { - // SOMEDAY figure out why map would every be null - return null; - } - Integer value = (Integer) map.get(key); - if (value != null) { - try { - return value; - } catch (Exception e) { - syntaxError("Expecting number after " + key + ": field, found '" + value + "'"); - } - } - return null; - } - - @SuppressWarnings("unchecked") - private Map fetchMap(Map content, String key) { - Object object = content.get(key); - if (object != null) { - if (object instanceof Map) { - return (Map) content.get(key); - } else { - return null; - } - } else { - return null; - } - } - @Override public void loadProblemInformationAndDataFiles(IInternalContest contest, String baseDirectoryName, Problem problem, boolean overrideUsePc2Validator) { loadProblemInformationAndDataFiles(contest, baseDirectoryName, problem, overrideUsePc2Validator, false); @@ -1450,11 +1248,11 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String String problemYamlFilename = problemDirectory + File.separator + DEFAULT_PROBLEM_YAML_FILENAME; - Map content = loadYaml(problemYamlFilename); + Map content = ContestImportUtilities.loadYaml(problemYamlFilename); String problemLaTexFilename = problemDirectory + File.separator + "problem_statement" + File.separator + DEFAULT_PROBLEM_LATEX_FILENAME; - String problemTitle = fetchValue(content, PROBLEM_NAME_KEY); + String problemTitle = ContestImportUtilities.fetchValue(content, PROBLEM_NAME_KEY); if (new File(problemLaTexFilename).isFile()) { problemTitle = getProblemNameFromLaTex(problemLaTexFilename); @@ -1466,14 +1264,14 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String } boolean usingCustomValidator = false; - Map validatorContent = fetchMap(content, VALIDATOR_KEY); + Map validatorContent = ContestImportUtilities.fetchMap(content, VALIDATOR_KEY); if (validatorContent != null) { - usingCustomValidator = fetchBooleanValue(validatorContent, IContestLoader.USING_CUSTOM_VALIDATOR, false); + usingCustomValidator = ContestImportUtilities.fetchBooleanValue(validatorContent, IContestLoader.USING_CUSTOM_VALIDATOR, false); } // check for CLICS "validation" property; provides an alternate way to specify a customer validator and, // the ONLY way to specify if the problem is interactive. - String validationType = fetchValue(content, VALIDATION_TYPE); + String validationType = ContestImportUtilities.fetchValue(content, VALIDATION_TYPE); boolean isInteractive = false; if (validationType != null) { // validationType is a list of validation options @@ -1488,21 +1286,26 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String // Note that interactive problems require a custom validator isInteractive = true; } else if(valOpts[1].equals(Constants.VALIDATION_SCORE)) { - syntaxError("Unsupported validation type: custom score"); +// ContestImportUtilities.syntaxError("Unsupported validation type: custom score"); + if(problemTitle == null) { + System.out.println("Custom validation type 'score' specified for untitled problem"); + } else { + System.out.println("Custom validation type 'score' specified for problem " + problemTitle); + } } else { - syntaxError("Unknown valudation type: custom " + valOpts[1]); + ContestImportUtilities.syntaxError("Unknown valudation type: custom " + valOpts[1]); } } } else if(!valOpts[0].equals(Constants.VALIDATION_DEFAULT)) { - syntaxError("Unknown validation type " + valOpts[0] + " specified"); + ContestImportUtilities.syntaxError("Unknown validation type " + valOpts[0] + " specified"); } } else { - syntaxError(VALIDATION_TYPE + " property found but no type was specified"); + ContestImportUtilities.syntaxError(VALIDATION_TYPE + " property found but no type was specified"); } } boolean pc2FormatProblemYamlFile = false; - String usingValidator = fetchValue(validatorContent, IContestLoader.USING_PC2_VALIDATOR); + String usingValidator = ContestImportUtilities.fetchValue(validatorContent, IContestLoader.USING_PC2_VALIDATOR); if (usingValidator != null && usingValidator.equalsIgnoreCase("true")) { pc2FormatProblemYamlFile = true; @@ -1514,24 +1317,24 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String // TODO: I am not sure why this test is contingent on pc2FormatProblemYamlFile. (JB) if (problemTitle == null && (pc2FormatProblemYamlFile)) { - problemTitle = fetchValue(content, "name"); + problemTitle = ContestImportUtilities.fetchValue(content, "name"); } if (problemTitle == null) { - syntaxError("No problem name found for " + problem.getShortName() + " in " + problemLaTexFilename); + ContestImportUtilities.syntaxError("No problem name found for " + problem.getShortName() + " in " + problemLaTexFilename); } problem.setDisplayName(problemTitle); - String dataFileBaseDirectory = problemDirectory + File.separator + "data" + File.separator + "secret"; + String dataFileBaseDirectory = problemDirectory + File.separator + "data"; ProblemDataFiles problemDataFiles = new ProblemDataFiles(problem); if (pc2FormatProblemYamlFile) { - String dataFileName = fetchValue(content, "datafile"); - String answerFileName = fetchValue(content, "answerfile"); + String dataFileName = ContestImportUtilities.fetchValue(content, "datafile"); + String answerFileName = ContestImportUtilities.fetchValue(content, "answerfile"); - loadPc2ProblemFiles(contest, dataFileBaseDirectory, problem, problemDataFiles, dataFileName, answerFileName); + loadPc2ProblemFiles(contest, dataFileBaseDirectory + File.separator + "secret", problem, problemDataFiles, dataFileName, answerFileName); } else { loadCCSProblemFiles(contest, dataFileBaseDirectory, problem, problemDataFiles); } @@ -1572,25 +1375,25 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String } //read any PC2-format limits specified at the top level of the problem.yaml file - Integer timeoutSecs = fetchIntValue(content, TIMEOUT_KEY); + Integer timeoutSecs = ContestImportUtilities.fetchIntValue(content, TIMEOUT_KEY); if (timeoutSecs != null) { problem.setTimeOutInSeconds(timeoutSecs); } - Integer maxOutputPC2 = fetchIntValue(content, MAX_OUTPUT_SIZE_K_KEY); + Integer maxOutputPC2 = ContestImportUtilities.fetchIntValue(content, MAX_OUTPUT_SIZE_K_KEY); if (maxOutputPC2 != null) { problem.setMaxOutputSizeKB(maxOutputPC2); } - Integer memoryLimit = fetchIntValue(content, MEMORY_LIMIT_IN_MEG_KEY, Problem.DEFAULT_MEMORY_LIMIT_MB); + Integer memoryLimit = ContestImportUtilities.fetchIntValue(content, MEMORY_LIMIT_IN_MEG_KEY, Problem.DEFAULT_MEMORY_LIMIT_MB); problem.setMemoryLimitMB(memoryLimit); - String sandboxCommandLine = fetchValue(content, SANDBOX_COMMAND_LINE_KEY, ""); + String sandboxCommandLine = ContestImportUtilities.fetchValue(content, SANDBOX_COMMAND_LINE_KEY, ""); problem.setSandboxCmdLine(sandboxCommandLine); - String sandboxProgramName = fetchValue(content, SANDBOX_PROGRAM_NAME_KEY, ""); + String sandboxProgramName = ContestImportUtilities.fetchValue(content, SANDBOX_PROGRAM_NAME_KEY, ""); problem.setSandboxProgramName(sandboxProgramName); - String sandboxTypeString = fetchValue(content, SANDBOX_TYPE_KEY); + String sandboxTypeString = ContestImportUtilities.fetchValue(content, SANDBOX_TYPE_KEY); if (sandboxTypeString != null) { try { @@ -1606,7 +1409,7 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String } //get the map (if any) of the CLICS "limits" section in the problem.yaml file - Map limitsContent = fetchMap(content, LIMITS_KEY); + Map limitsContent = ContestImportUtilities.fetchMap(content, LIMITS_KEY); //if there is a CLICS "limits" section in the problem.yaml, read any values in that section and use // them to override any PC2-formatted values (just read in, above) @@ -1626,12 +1429,12 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String // which DO have CLICS "time_multiplier" and/or "time_saftety_margin" entries in them (even though PC2 doesn't currently // support those attibutes). The problem is that the presence of those attributes in these JUnit test files results // in those JUnits throwing YamlLoadExceptions. -// Integer clics_time_multiplier = fetchIntValue(limitsContent, CLICS_TIME_MULTIPLIER_KEY); +// Integer clics_time_multiplier = ContestImportUtilities.fetchIntValue(limitsContent, CLICS_TIME_MULTIPLIER_KEY); // if (clics_time_multiplier != null) { // //TODO: replace the following exception with code to properly handle the CLICS time_multiplier value. // throw new YamlLoadException("Unsupported CLICS attribute in " + problemYamlFilename + " 'limits' section: " + CLICS_TIME_MULTIPLIER_KEY); // } -// Integer clics_time_safety_margin = fetchIntValue(limitsContent, CLICS_TIME_SAFETY_MARGIN_KEY); +// Integer clics_time_safety_margin = ContestImportUtilities.fetchIntValue(limitsContent, CLICS_TIME_SAFETY_MARGIN_KEY); // if (clics_time_safety_margin != null) { // //TODO: replace the following exception with code to properly handle the CLICS time_safety_margin value. // throw new YamlLoadException("Unsupported CLICS attribute in " + problemYamlFilename + " 'limits' section: " + CLICS_TIME_SAFETY_MARGIN_KEY); @@ -1640,18 +1443,18 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String //check for a timeout limit within the CLICS "limits:" section. // Note that the presence of a "timeout:" entry within a CLICS // "limits:" section is non-CLICS standard -- but we want to support it in PC2. - Integer clicsTimeout = fetchIntValue(limitsContent, TIMEOUT_KEY); + Integer clicsTimeout = ContestImportUtilities.fetchIntValue(limitsContent, TIMEOUT_KEY); if (clicsTimeout != null) { problem.setTimeOutInSeconds(clicsTimeout); } //check for a CLICS maxoutput limit - the value is in MiB - Integer clicsMaxOutput = fetchIntValue(limitsContent, CLICS_MAX_OUTPUT_KEY); + Integer clicsMaxOutput = ContestImportUtilities.fetchIntValue(limitsContent, CLICS_MAX_OUTPUT_KEY); if (clicsMaxOutput != null) { problem.setMaxOutputSizeKB(clicsMaxOutput * Constants.KIBIBYTE_PER_MEBIBYTE); } - Integer clicsMemoryLimit = fetchIntValue(limitsContent, MEMORY_LIMIT_CLICS, Problem.DEFAULT_MEMORY_LIMIT_MB); + Integer clicsMemoryLimit = ContestImportUtilities.fetchIntValue(limitsContent, MEMORY_LIMIT_CLICS, Problem.DEFAULT_MEMORY_LIMIT_MB); problem.setMemoryLimitMB(clicsMemoryLimit); } @@ -1674,7 +1477,7 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String } } else { // using Custom Output Validator - String outputValidatorNameFromYaml = fetchValue(validatorContent, "validatorProg"); + String outputValidatorNameFromYaml = ContestImportUtilities.fetchValue(validatorContent, "validatorProg"); if (outputValidatorNameFromYaml != null) { Problem cleanProblem = contest.getProblem(problem.getElementId()); @@ -1701,7 +1504,7 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String contest.updateProblem(cleanProblem, problemDataFile); } else { // Halt loading and throw YamlLoadException - syntaxError("Error: problem " + problem.getLetter() + " - " + problem.getShortName() + " custom validator import failed: " + outputValidatorFile.getErrorMessage()); + ContestImportUtilities.syntaxError("Error: problem " + problem.getLetter() + " - " + problem.getShortName() + " custom validator import failed: " + outputValidatorFile.getErrorMessage()); } } @@ -1710,21 +1513,21 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String boolean globlStopOnFirstFail = contest.getContestInformation().isStopOnFirstFailedtestCase(); - boolean stopOnFirstFail = fetchBooleanValue(content, STOP_ON_FIRST_FAILED_TEST_CASE_KEY, globlStopOnFirstFail); + boolean stopOnFirstFail = ContestImportUtilities.fetchBooleanValue(content, STOP_ON_FIRST_FAILED_TEST_CASE_KEY, globlStopOnFirstFail); problem.setStopOnFirstFailedTestCase(stopOnFirstFail); assignJudgingType(content, problem, overrideManualReview); - boolean showOutputWindow = fetchBooleanValue(content, SHOW_OUTPUT_WINDOW, true); + boolean showOutputWindow = ContestImportUtilities.fetchBooleanValue(content, SHOW_OUTPUT_WINDOW, true); problem.setHideOutputWindow(!showOutputWindow); - boolean showCompareWindow = fetchBooleanValue(content, SHOW_COMPARE_WINDOW, false); + boolean showCompareWindow = ContestImportUtilities.fetchBooleanValue(content, SHOW_COMPARE_WINDOW, false); problem.setShowCompareWindow(showCompareWindow); - boolean hideProblem = fetchBooleanValue(content, HIDE_PROBLEM, false); + boolean hideProblem = ContestImportUtilities.fetchBooleanValue(content, HIDE_PROBLEM, false); problem.setActive(!hideProblem); - boolean showValidationResults = fetchBooleanValue(content, SHOW_VALIDATION_RESULTS, true); + boolean showValidationResults = ContestImportUtilities.fetchBooleanValue(content, SHOW_VALIDATION_RESULTS, true); problem.setShowValidationToJudges(showValidationResults); @SuppressWarnings("unchecked") @@ -1734,17 +1537,17 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String } // override CCS standard read input data from stdin - Map problemInputContent = fetchMap(content, PROBLEM_INPUT_KEY); + Map problemInputContent = ContestImportUtilities.fetchMap(content, PROBLEM_INPUT_KEY); if (problemInputContent != null) { - boolean readFromSTDIN = fetchBooleanValue(problemInputContent, READ_FROM_STDIN_KEY, true); + boolean readFromSTDIN = ContestImportUtilities.fetchBooleanValue(problemInputContent, READ_FROM_STDIN_KEY, true); problem.setReadInputDataFromSTDIN(readFromSTDIN); } - String groupListString = fetchValue(content, GROUPS_KEY); + String groupListString = ContestImportUtilities.fetchValue(content, GROUPS_KEY); if (groupListString != null) { if (groupListString.trim().length() == 0) { - syntaxError("Empty group list"); + ContestImportUtilities.syntaxError("Empty group list"); } String[] fields = groupListString.split(";"); @@ -1757,9 +1560,9 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String Group group = lookupGroupInfo(groups, groupInfo); if (group == null) { if (groups == null || groups.length == 0) { - syntaxError("For "+problem.getShortName()+" ERROR No groups defined. (groups.tsv not loaded?), error when trying to find group for '" + groupInfo + "' from yaml value '" + groupListString + "' "); + ContestImportUtilities.syntaxError("For "+problem.getShortName()+" ERROR No groups defined. (groups.tsv not loaded?), error when trying to find group for '" + groupInfo + "' from yaml value '" + groupListString + "' "); } else { - syntaxError("Undefined group '" + groupInfo + "' for group list '" + groupListString + "' "); + ContestImportUtilities.syntaxError("Undefined group '" + groupInfo + "' for group list '" + groupListString + "' "); } } @@ -1768,7 +1571,7 @@ public void loadProblemInformationAndDataFiles(IInternalContest contest, String } // SOMEDAY CCS - send preliminary - add bug - fix. - // boolean sendPreliminary = fetchBooleanValue(content, SEND_PRELIMINARY_JUDGEMENT_KEY, false); + // boolean sendPreliminary = ContestImportUtilities.fetchBooleanValue(content, SEND_PRELIMINARY_JUDGEMENT_KEY, false); // if (sendPreliminary){ // problem.setPrelimaryNotification(true); // } @@ -1799,16 +1602,16 @@ protected void assignValidatorSettings(Map content, Problem prob @SuppressWarnings("unchecked") LinkedHashMap map = (LinkedHashMap) object; // fetchList(content, VALIDATOR_KEY); - boolean customType = fetchBooleanValue(map, IContestLoader.USING_CUSTOM_VALIDATOR, false); - // boolean pc2Type = fetchBooleanValue(map, IContestLoader.USING_PC2_VALIDATOR, false); - String validatorProg = fetchValue(map, "validatorProg"); + boolean customType = ContestImportUtilities.fetchBooleanValue(map, IContestLoader.USING_CUSTOM_VALIDATOR, false); + // boolean pc2Type = ContestImportUtilities.fetchBooleanValue(map, IContestLoader.USING_PC2_VALIDATOR, false); + String validatorProg = ContestImportUtilities.fetchValue(map, "validatorProg"); - String validatorCmd = fetchValue(map, "validatorCmd"); + String validatorCmd = ContestImportUtilities.fetchValue(map, "validatorCmd"); // junit does not expect NONE to be set.... // if (pc2Type) { problem.setValidatorType(VALIDATOR_TYPE.PC2VALIDATOR); problem.setOutputValidatorProgramName(validatorProg); - String validatorOption = fetchValue(map, "validatorOption"); + String validatorOption = ContestImportUtilities.fetchValue(map, "validatorOption"); PC2ValidatorSettings settings = new PC2ValidatorSettings(); if (validatorOption != null) { @@ -1826,7 +1629,7 @@ protected void assignValidatorSettings(Map content, Problem prob problem.setValidatorType(VALIDATOR_TYPE.CUSTOMVALIDATOR); problem.setOutputValidatorProgramName(validatorProg); CustomValidatorSettings customSettings = new CustomValidatorSettings(); - boolean clicsMode = fetchBooleanValue(map, IContestLoader.USE_CLICS_CUSTOM_VALIDATOR_INTERFACE, true); + boolean clicsMode = ContestImportUtilities.fetchBooleanValue(map, IContestLoader.USE_CLICS_CUSTOM_VALIDATOR_INTERFACE, true); if (clicsMode) { customSettings.setUseClicsValidatorInterface(); } else { @@ -1843,7 +1646,7 @@ protected void assignValidatorSettings(Map content, Problem prob customSettings.setValidatorProgramName(validatorProg); problem.setCustomOutputValidatorSettings(customSettings); } - // String usingInternal = fetchValue(map, "usingInternal"); + // String usingInternal = ContestImportUtilities.fetchValue(map, "usingInternal"); return; // =================== RETURN } @@ -1852,7 +1655,7 @@ protected void assignValidatorSettings(Map content, Problem prob // validator_flags: options are: [case_sensitive] [space_change_sensitive] [float_absolute_tolerance FLOAT] [float_tolerance FLOAT] // ex. validator_flags: float_tolerance 1e-6 - String validatorFlags = fetchValue(content, IContestLoader.VALIDATOR_FLAGS_KEY); + String validatorFlags = ContestImportUtilities.fetchValue(content, IContestLoader.VALIDATOR_FLAGS_KEY); if (validatorFlags != null && validatorFlags.trim().length() > 0) { try { @@ -1869,7 +1672,7 @@ protected void assignValidatorSettings(Map content, Problem prob // validator: options are: [case_sensitive] [space_change_sensitive] [float_absolute_tolerance FLOAT] [float_tolerance FLOAT] // ex. validator: float_tolerance 1e-6 - String validatorParameters = fetchValue(content, IContestLoader.VALIDATOR_KEY); + String validatorParameters = ContestImportUtilities.fetchValue(content, IContestLoader.VALIDATOR_KEY); if (validatorParameters != null && validatorParameters.trim().length() > 0) { try { ClicsValidatorSettings settings = new ClicsValidatorSettings(validatorParameters); @@ -1905,40 +1708,24 @@ public Problem addDefaultPC2Validator(Problem problem, int optionNumber) { @Override public String[] loadGeneralClarificationAnswers(String[] yamlLines) { - return fetchStringList(yamlLines, CLAR_CATEGORIES_KEY); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public String[] fetchStringList(String[] yamlLines, String key) { - Map content = loadYaml(null, yamlLines); - ArrayList list = fetchList(content, key); - if (list == null) { - return new String[0]; - } else { - return (String[]) list.toArray(new String[list.size()]); - } + return ContestImportUtilities.fetchStringList(yamlLines, CLAR_CATEGORIES_KEY); } @Override public String[] getGeneralAnswers(String[] yamlLines) { - return fetchStringList(yamlLines, DEFAULT_CLARS_KEY); + return ContestImportUtilities.fetchStringList(yamlLines, DEFAULT_CLARS_KEY); } @Override public String[] getClarificationCategories(String[] yamlLines) { - return fetchStringList(yamlLines, CLAR_CATEGORIES_KEY); - } - - @SuppressWarnings("rawtypes") - private ArrayList fetchList(Map content, String key) { - return (ArrayList) content.get(key); + return ContestImportUtilities.fetchStringList(yamlLines, CLAR_CATEGORIES_KEY); } public ClientId [] getShadowProxyClientIds(String[] yamlLines) { ArrayList clientIdList = new ArrayList(); - Map yamlContent = loadYaml(null, yamlLines); + Map yamlContent = ContestImportUtilities.loadYaml(null, yamlLines); @SuppressWarnings("unchecked") - ArrayList> list = fetchList(yamlContent, "team-proxy-accounts"); + ArrayList> list = ContestImportUtilities.fetchList(yamlContent, "team-proxy-accounts"); if (list != null) { for (Object object : list) { @@ -1946,12 +1733,12 @@ private ArrayList fetchList(Map content, String key) { @SuppressWarnings("unchecked") Map map = (Map) object; - String accountType = fetchValue(map, "account"); + String accountType = ContestImportUtilities.fetchValue(map, "account"); checkField(accountType, "Account Type"); ClientType.Type type = ClientType.Type.valueOf(accountType.trim()); - Integer siteNumber = fetchIntValue(map, "site", 1); - String numberString = fetchValue(map, "number"); + Integer siteNumber = ContestImportUtilities.fetchIntValue(map, "site", 1); + String numberString = ContestImportUtilities.fetchValue(map, "number"); int[] clientNumbers = getNumberList(numberString.trim()); @@ -1974,24 +1761,24 @@ public Language[] getLanguages(String[] yamlLines) { // System.out.println(Utilities.join("\n", yamlLines)); - Map yamlContent = loadYaml(null, yamlLines); - ArrayList> list = fetchList(yamlContent, LANGUAGE_KEY); + Map yamlContent = ContestImportUtilities.loadYaml(null, yamlLines); + ArrayList> list = ContestImportUtilities.fetchList(yamlContent, LANGUAGE_KEY); if (list != null) { for (Object object : list) { Map map = (Map) object; - String name = fetchValue(map, "name"); + String name = ContestImportUtilities.fetchValue(map, "name"); if (name == null) { - syntaxError("Language name field missing in languages section"); + ContestImportUtilities.syntaxError("Language name field missing in languages section"); } else { Language language = new Language(name); Language lookedupLanguage = LanguageAutoFill.languageLookup(name); - String compilerName = fetchValue(map, "compiler"); - String pc2CompilerCommandLine = fetchValue(map, PC2_COMPILER_CMD); + String compilerName = ContestImportUtilities.fetchValue(map, "compiler"); + String pc2CompilerCommandLine = ContestImportUtilities.fetchValue(map, PC2_COMPILER_CMD); @@ -2000,10 +1787,10 @@ public Language[] getLanguages(String[] yamlLines) { if (compilerName != null) { // CLICS Language - compilerName = fetchValue(map, "compiler"); - String compilerArgs = fetchValue(map, "compiler-args"); - String runner = fetchValue(map, "runner"); - String runnerArgs = fetchValue(map, "runner-args"); + compilerName = ContestImportUtilities.fetchValue(map, "compiler"); + String compilerArgs = ContestImportUtilities.fetchValue(map, "compiler-args"); + String runner = ContestImportUtilities.fetchValue(map, "runner"); + String runnerArgs = ContestImportUtilities.fetchValue(map, "runner-args"); checkField(compilerName, "Language \"" + name + "\" missing compiler key/value"); @@ -2041,43 +1828,43 @@ public Language[] getLanguages(String[] yamlLines) { language.setCompileCommandLine(pc2CompilerCommandLine); - String programExecuteCommandLine = fetchValue(map, PC2_EXEC_CMD); + String programExecuteCommandLine = ContestImportUtilities.fetchValue(map, PC2_EXEC_CMD); language.setProgramExecuteCommandLine(programExecuteCommandLine); - String exeMask = fetchValue(map, "exemask"); + String exeMask = ContestImportUtilities.fetchValue(map, "exemask"); language.setExecutableIdentifierMask(exeMask); } else if (lookedupLanguage != null) { language = lookedupLanguage; } else { - syntaxError("Language \"" + name + "\" missing language definition (compiler command line and program execution command line)"); + ContestImportUtilities.syntaxError("Language \"" + name + "\" missing language definition (compiler command line and program execution command line)"); } checkField(language.getCompileCommandLine(), "Language \"" + name + "\" missing compiler command line"); checkField(language.getProgramExecuteCommandLine(), "Language \"" + name + "\" missing programm execution command line"); - boolean active = fetchBooleanValue(map, "active", true); + boolean active = ContestImportUtilities.fetchBooleanValue(map, "active", true); language.setActive(active); - boolean useJudgeCommand = fetchBooleanValue(map, USE_JUDGE_COMMAND_KEY, false); + boolean useJudgeCommand = ContestImportUtilities.fetchBooleanValue(map, USE_JUDGE_COMMAND_KEY, false); language.setUsingJudgeProgramExecuteCommandLine(useJudgeCommand); - boolean isInterpreted = fetchBooleanValue(map, INTERPRETED_LANGUAGE_KEY, language.isInterpreted()); + boolean isInterpreted = ContestImportUtilities.fetchBooleanValue(map, INTERPRETED_LANGUAGE_KEY, language.isInterpreted()); language.setInterpreted(isInterpreted); - String judgeExecuteCommandLine = fetchValue(map, JUDGE_EXECUTE_COMMAND_KEY); + String judgeExecuteCommandLine = ContestImportUtilities.fetchValue(map, JUDGE_EXECUTE_COMMAND_KEY); if (judgeExecuteCommandLine != null) { language.setJudgeProgramExecuteCommandLine(judgeExecuteCommandLine); } else { language.setUsingJudgeProgramExecuteCommandLine(false); } - String clicsLanguageId = fetchValue(map, CLICS_LANG_ID); + String clicsLanguageId = ContestImportUtilities.fetchValue(map, CLICS_LANG_ID); if (clicsLanguageId != null){ language.setID(clicsLanguageId); } - Object exts = fetchObjectValue(map, LANG_EXTENSIONS); + Object exts = ContestImportUtilities.fetchObjectValue(map, LANG_EXTENSIONS); if(exts != null && exts instanceof ArrayList) { language.setExtensions((ArrayList)exts); } @@ -2102,13 +1889,13 @@ public Problem[] getProblems(String[] yamlLines, int seconds, long maxOutputByte Vector problemList = new Vector(); - Map yamlContent = loadYaml(null, yamlLines); + Map yamlContent = ContestImportUtilities.loadYaml(null, yamlLines); // use problemset yaml key - ArrayList> list = fetchList(yamlContent, PROBLEMS_KEY); + ArrayList> list = ContestImportUtilities.fetchList(yamlContent, PROBLEMS_KEY); if (list == null) { // use problems yaml key - list = fetchList(yamlContent, PROBLEMSET_PROBLEMS_KEY); + list = ContestImportUtilities.fetchList(yamlContent, PROBLEMSET_PROBLEMS_KEY); } //at this point if "list" is not null then it should contain an entry for each problem defined in @@ -2123,9 +1910,9 @@ public Problem[] getProblems(String[] yamlLines, int seconds, long maxOutputByte Map problemMap = (Map) object; //make sure the problem has a "short-name" - String problemKeyName = fetchValue(problemMap, SHORT_NAME_KEY); + String problemKeyName = ContestImportUtilities.fetchValue(problemMap, SHORT_NAME_KEY); if (problemKeyName == null) { - syntaxError("Missing " + SHORT_NAME_KEY + " in probset section"); + ContestImportUtilities.syntaxError("Missing " + SHORT_NAME_KEY + " in probset section"); } /** @@ -2141,7 +1928,7 @@ public Problem[] getProblems(String[] yamlLines, int seconds, long maxOutputByte */ //get the problem name - String problemTitle = fetchValue(problemMap, PROBLEM_NAME_KEY); + String problemTitle = ContestImportUtilities.fetchValue(problemMap, PROBLEM_NAME_KEY); if (problemTitle == null) { problemTitle = problemKeyName; } @@ -2153,12 +1940,12 @@ public Problem[] getProblems(String[] yamlLines, int seconds, long maxOutputByte //set problem time limit. If the problem.yaml file for the current problem (codified in the "problemMap") // contains a "TIMEOUT_KEY", use the timeout value from the problem.yaml; otherwise use the passed-in default. - int actSeconds = fetchIntValue(problemMap, TIMEOUT_KEY, seconds); + int actSeconds = ContestImportUtilities.fetchIntValue(problemMap, TIMEOUT_KEY, seconds); problem.setTimeOutInSeconds(actSeconds); //set problem output limit. If the problem.yaml file for the current problem (codified in the "problemMap") // contains an "OUTPUT" key, use the timeout value from the problem.yaml; otherwise use the passed-in default. - Long actualMaxOutputBytes = fetchLongValue(problemMap, MAX_OUTPUT_SIZE_K_KEY, maxOutputBytes); + Long actualMaxOutputBytes = ContestImportUtilities.fetchLongValue(problemMap, MAX_OUTPUT_SIZE_K_KEY, maxOutputBytes); problem.setMaxOutputSizeKB(actualMaxOutputBytes/Constants.BYTES_PER_KIBIBYTE); //TODO: add code to check for the CLICS-compliant key "output:" in the "limits: section @@ -2171,9 +1958,9 @@ public Problem[] getProblems(String[] yamlLines, int seconds, long maxOutputByte throw new YamlLoadException("Invalid short problem name '" + problemKeyName + "'"); } - String problemLetter = fetchValue(problemMap, "letter"); - String colorName = fetchValue(problemMap, "color"); - String colorRGB = fetchValue(problemMap, "rgb"); + String problemLetter = ContestImportUtilities.fetchValue(problemMap, "letter"); + String colorName = ContestImportUtilities.fetchValue(problemMap, "color"); + String colorRGB = ContestImportUtilities.fetchValue(problemMap, "rgb"); // SOMEDAY CCS assign Problem variables for color and letter problem.setLetter(problemLetter); @@ -2194,12 +1981,12 @@ public Problem[] getProblems(String[] yamlLines, int seconds, long maxOutputByte //if the problem.yaml file for the current problem (codified in the "problemMap") has a "load-data-files" // key, use that to set the "loadFilesFlag"; if not, use the passed-in default. - boolean loadFilesFlag = fetchBooleanValue(problemMap, PROBLEM_LOAD_DATA_FILES_KEY, loadDataFileContents); + boolean loadFilesFlag = ContestImportUtilities.fetchBooleanValue(problemMap, PROBLEM_LOAD_DATA_FILES_KEY, loadDataFileContents); problem.setUsingExternalDataFiles(!loadFilesFlag); //if the problem.yaml file for the current problem (codified in the "problemMap") has a VALIDATOR_KEY // key, use that to obtain the "outputValidatorCommandLine"; if not, use the passed-in defaults. - String outputValidatorCommandLine = fetchValue(problemMap, VALIDATOR_KEY); + String outputValidatorCommandLine = ContestImportUtilities.fetchValue(problemMap, VALIDATOR_KEY); if (outputValidatorCommandLine == null) { outputValidatorCommandLine = defaultValidatorCommand; @@ -2252,10 +2039,10 @@ protected void assignInputValidators(IInternalContest contest, Problem problem, //get the entire problem.yaml file as a YAML map String problemYamlFileName = probDir + File.separator + DEFAULT_PROBLEM_YAML_FILENAME; - Map problemYamlMap = loadYaml(problemYamlFileName); + Map problemYamlMap = ContestImportUtilities.loadYaml(problemYamlFileName); //get the "input_validator" section from the problem.yaml file map - Map inputValidatorMap = fetchMap(problemYamlMap, INPUT_VALIDATOR_KEY); + Map inputValidatorMap = ContestImportUtilities.fetchMap(problemYamlMap, INPUT_VALIDATOR_KEY); //we haven't (yet) set the default Input Validator type boolean defaultInputValidatorTypeHasBeenSet = false; @@ -2272,7 +2059,7 @@ protected void assignInputValidators(IInternalContest contest, Problem problem, //yes; process the "input_validator" section settings, assigning defaults for unspecified settings // if there is a default Input Validator type (NONE, VIVA, or CUSTOM) specified, set that in the problem - String defaultIVType = fetchValue(inputValidatorMap, DEFAULT_INPUT_VALIDATOR_KEY); + String defaultIVType = ContestImportUtilities.fetchValue(inputValidatorMap, DEFAULT_INPUT_VALIDATOR_KEY); if (defaultIVType != null) { String defaultIVTypeIgnoreCase = defaultIVType.toLowerCase(); switch (defaultIVTypeIgnoreCase) { @@ -2286,14 +2073,14 @@ protected void assignInputValidators(IInternalContest contest, Problem problem, problem.setCurrentInputValidatorType(INPUT_VALIDATOR_TYPE.CUSTOM); break; default: - syntaxError("Unknown value for " + DEFAULT_INPUT_VALIDATOR_KEY + ": " + defaultIVType); + ContestImportUtilities.syntaxError("Unknown value for " + DEFAULT_INPUT_VALIDATOR_KEY + ": " + defaultIVType); } defaultInputValidatorTypeHasBeenSet = true; } // if there is a custom input validator command specified in the YAML map, set it in the problem - String customInputValidatorCommandLine = fetchValue(inputValidatorMap, CUSTOM_INPUT_VALIDATOR_COMMAND_LINE_KEY); + String customInputValidatorCommandLine = ContestImportUtilities.fetchValue(inputValidatorMap, CUSTOM_INPUT_VALIDATOR_COMMAND_LINE_KEY); if (customInputValidatorCommandLine != null) { problem.setCustomInputValidatorCommandLine(customInputValidatorCommandLine); } else { @@ -2301,7 +2088,7 @@ protected void assignInputValidators(IInternalContest contest, Problem problem, } // if there is a custom input validator program specified in the YAML map, attempt to read the file into a SerializedFile - String customInputValidatorProgName = fetchValue(inputValidatorMap, CUSTOM_INPUT_VALIDATOR_PROGRAM_NAME_KEY); + String customInputValidatorProgName = ContestImportUtilities.fetchValue(inputValidatorMap, CUSTOM_INPUT_VALIDATOR_PROGRAM_NAME_KEY); if (customInputValidatorProgName != null) { String pathToCustomProg = getInputValidatorDir(problemsBaseDir, problem) + File.separator + customInputValidatorProgName; @@ -2337,16 +2124,16 @@ protected void assignInputValidators(IInternalContest contest, Problem problem, // if there is a VIVA pattern file specified in the YAML map, attempt to read the file into a SerializedFile - String vivaPatternFileName = fetchValue(inputValidatorMap, VIVA_PATTERN_FILE_KEY); + String vivaPatternFileName = ContestImportUtilities.fetchValue(inputValidatorMap, VIVA_PATTERN_FILE_KEY); if (vivaPatternFileName != null) { SerializedFile vivaPatternSF = new SerializedFile(vivaPatternFileName); // check for errors/exceptions during file loading try { if (Utilities.serializedFileError(vivaPatternSF)) { - syntaxError("Unable to load VIVA pattern file '" + vivaPatternFileName + "': " + vivaPatternSF.getErrorMessage()); + ContestImportUtilities.syntaxError("Unable to load VIVA pattern file '" + vivaPatternFileName + "': " + vivaPatternSF.getErrorMessage()); } } catch (Exception e) { - syntaxError("Exception loading VIVA pattern file '" + vivaPatternFileName + "': " + e.getMessage()); + ContestImportUtilities.syntaxError("Exception loading VIVA pattern file '" + vivaPatternFileName + "': " + e.getMessage()); } // the Viva pattern file was successfully loaded; add it to the problem String[] patternLines = new String(vivaPatternSF.getBuffer()).split("\n"); @@ -2358,7 +2145,7 @@ protected void assignInputValidators(IInternalContest contest, Problem problem, // Note that this means a pattern directly specified in the YAML file supersedes any // reference to a pattern FILE also in the YAML (since that would have been loaded above and // this will override it). - String vivaPattern = fetchValue(inputValidatorMap, VIVA_PATTTERN_KEY); + String vivaPattern = ContestImportUtilities.fetchValue(inputValidatorMap, VIVA_PATTTERN_KEY); if (vivaPattern != null) { // a Viva pattern was found in the YAML file; add it to the problem String[] patternLines = vivaPattern.split("\n"); @@ -2510,8 +2297,8 @@ public AutoJudgeSetting[] getAutoJudgeSettings(String[] yamlLines, Problem[] pro ArrayList ajList = new ArrayList(); - Map yamlContent = loadYaml(null, yamlLines); - ArrayList> list = fetchList(yamlContent, AUTO_JUDGE_KEY); + Map yamlContent = ContestImportUtilities.loadYaml(null, yamlLines); + ArrayList> list = ContestImportUtilities.fetchList(yamlContent, AUTO_JUDGE_KEY); if (list != null) { @@ -2522,18 +2309,18 @@ public AutoJudgeSetting[] getAutoJudgeSettings(String[] yamlLines, Problem[] pro Map map = (Map) object; - String accountType = fetchValue(map, "account"); + String accountType = ContestImportUtilities.fetchValue(map, "account"); ClientType.Type type = ClientType.Type.valueOf(accountType.trim()); - int siteNumber = fetchIntValue(map, "site", 1); + int siteNumber = ContestImportUtilities.fetchIntValue(map, "site", 1); // SOMEDAY 669 check for syntax errors - // syntaxError(AUTO_JUDGE_KEY + " name field missing in languages section"); + // ContestImportUtilities.syntaxError(AUTO_JUDGE_KEY + " name field missing in languages section"); - String numberString = fetchValue(map, "number"); - String problemLettersString = fetchValue(map, "letters"); + String numberString = ContestImportUtilities.fetchValue(map, "number"); + String problemLettersString = ContestImportUtilities.fetchValue(map, "letters"); - boolean active = fetchBooleanValue(map, "active", true); + boolean active = ContestImportUtilities.fetchBooleanValue(map, "active", true); int[] judgeClientNumbers = null; if ("all".equalsIgnoreCase(numberString)) { @@ -2744,11 +2531,6 @@ protected String unquoteAll(String string) { } } - private void syntaxError(String string) { - YamlLoadException exception = new YamlLoadException("Syntax error: " + string); - throw exception; - } - /** * Adds the CLICS output validator as the output validator for the specified problem. * @@ -2997,11 +2779,11 @@ public void loadPc2ProblemFiles(IInternalContest contest, String dataFileBaseDir ProblemDataFiles problemDataFiles = new ProblemDataFiles(problem); if (dataFileName == null) { - syntaxError("Missing datafile for pc2 problem " + problem.getShortName()); + ContestImportUtilities.syntaxError("Missing datafile for pc2 problem " + problem.getShortName()); } if (answerFileName == null) { - syntaxError("Missing answerfile for pc2 problem " + problem.getShortName()); + ContestImportUtilities.syntaxError("Missing answerfile for pc2 problem " + problem.getShortName()); } addDataFiles(problem, problemDataFiles, dataFileBaseDirectory, dataFileName, answerFileName); @@ -3015,7 +2797,7 @@ public String getCCSDataFileDirectory(String yamlDirectory, Problem problem) { @Override public String getCCSDataFileDirectory(String yamlDirectory, String shortDirName) { - return yamlDirectory + File.separator + shortDirName + File.separator + "data" + File.separator + "secret"; + return yamlDirectory + File.separator + shortDirName + File.separator + "data"; } @Override @@ -3030,68 +2812,77 @@ public Problem loadCCSProblemFiles(IInternalContest contest, String dataFileBase */ boolean loadExternalFile = problem.isUsingExternalDataFiles(); - boolean loadSamples = contest.getContestInformation().isLoadSampleJudgesData(); - - String sampleDataDirectory = dataFileBaseDirectory.replaceAll("secret$", "sample"); + TestDataGroup mainDataGroup = new TestDataGroup("data", dataFileBaseDirectory, null); + if(mainDataGroup.readTestCases(StaticLog.getLog()) == false) { + throw new YamlLoadException("Could not read test cases for " + problem.getDisplayName() + " in dir " + dataFileBaseDirectory); + } - String[] inputFileNames = getFileNames(dataFileBaseDirectory, ".in"); + // Pretty much the same as the original code for now + ArrayList dataFiles = new ArrayList(); + ArrayList answerFiles = new ArrayList(); + ArrayList dataGroups = new ArrayList(); - String[] answerFileNames = getFileNames(dataFileBaseDirectory, ".ans"); + loadDataFiles(problem, dataFiles, answerFiles, dataGroups, mainDataGroup, loadExternalFile); - if (inputFileNames.length == 0) { - throw new YamlLoadException("No input (.in) file names found for " + problem.getDisplayName() + " in dir " + dataFileBaseDirectory); - } - - if (answerFileNames.length == 0) { - throw new YamlLoadException("No answer (.ans) file names found for " + problem.getDisplayName() + " in dir " + dataFileBaseDirectory); - } - if (inputFileNames.length == answerFileNames.length) { + if (dataFiles.size() > 0) { - ArrayList dataFiles = new ArrayList(); - ArrayList answerFiles = new ArrayList(); + SerializedFile[] data = dataFiles.toArray(new SerializedFile[dataFiles.size()]); + SerializedFile[] answer = answerFiles.toArray(new SerializedFile[answerFiles.size()]); - if (loadSamples) { - loadDataFiles(problem, dataFiles, answerFiles, sampleDataDirectory, loadExternalFile); - } + // dumpSerialzedFileList (problem, "Judges data", data); + // dumpSerialzedFileList (problem, "Judges answer", answer); - // Load all secret files - loadDataFiles(problem, dataFiles, answerFiles, dataFileBaseDirectory, loadExternalFile); + problemDataFiles.setJudgesDataFiles(data); + problemDataFiles.setJudgesAnswerFiles(answer); + problemDataFiles.setJudgesDataGroups(dataGroups); - if (dataFiles.size() > 0) { + // this is farce - I think it's just for the problem editor gui + problem.setDataFileName(problem.getDataFileName(1)); + problem.setAnswerFileName(problem.getAnswerFileName(1)); + } else { + syntaxWarning("There were no data files found/loaded for " + problem.getShortName()); + } - SerializedFile[] data = dataFiles.toArray(new SerializedFile[dataFiles.size()]); - SerializedFile[] answer = answerFiles.toArray(new SerializedFile[answerFiles.size()]); + problem.setReadInputDataFromSTDIN(true); - // dumpSerialzedFileList (problem, "Judges data", data); - // dumpSerialzedFileList (problem, "Judges answer", answer); + contest.addProblem(problem, problemDataFiles); - problemDataFiles.setJudgesDataFiles(data); - problemDataFiles.setJudgesAnswerFiles(answer); + validateCCSData(contest, problem); - problem.setDataFileName(inputFileNames[0]); - problem.setAnswerFileName(inputFileNames[0].replaceAll(".in$", ".ans")); - } else { - syntaxWarning("There were no data files found/loaded for " + problem.getShortName()); - } + return problem; + } - problem.setReadInputDataFromSTDIN(true); + /** + * Per problem, add files names into datafiles and answerfiles from the TestDataGroup. + * + * @param problem + * @param dataFiles input data files + * @param answerFiles input answer files + * @param dataGroups input data groups + * @param testDataGroup - main test data group (top level) + * @param loadExternalFile - load as external data fils + */ + protected void loadDataFiles(Problem problem, ArrayList dataFiles, ArrayList answerFiles, ArrayList dataGroups, TestDataGroup testDataGroup, boolean loadExternalFile) { - } else { - throw new YamlLoadException(" For " + problem.getShortName() + " Missing files - there are " + inputFileNames.length + " .in files and " + // - answerFileNames.length + " .ans files " + " in " + dataFileBaseDirectory); + ArrayList testCases = testDataGroup.getAllTestCaseInfo(); + if(testCases == null || testCases.size() == 0) { + throw new YamlLoadException("No test data available for " + problem.getDisplayName() + " in dir " + testDataGroup.getDataDirectoryName()); } - if (inputFileNames.length == 0) { - throw new YamlLoadException(" For " + problem.getShortName() + " Missing files - there are " + inputFileNames.length + " .in files and " + // - answerFileNames.length + " .ans files " + " in " + dataFileBaseDirectory); - } + problem.addTestCaseFilenames(testCases); + for (TestCaseInfo testCase : testCases) { - contest.addProblem(problem, problemDataFiles); + String dataFileName = testCase.getInputFileName(); + String answerFileName = testCase.getAnswerFileName(); - validateCCSData(contest, problem); + checkForFile(dataFileName, "Missing " + dataFileName + " file for " + problem.getShortName()); + checkForFile(answerFileName, "Missing " + answerFileName + " file for " + problem.getShortName()); - return problem; + dataFiles.add(new SerializedFile(dataFileName, loadExternalFile)); + answerFiles.add(new SerializedFile(answerFileName, loadExternalFile)); + dataGroups.add(testCase.getGroup()); + } } /** @@ -3233,14 +3024,14 @@ private boolean valid(Language language, String prefix) { */ private void checkField(String value, String fieldName) { if (value == null) { - syntaxError("Missing " + fieldName); + ContestImportUtilities.syntaxError("Missing " + fieldName); } else if (value.trim().length() == 0) { - syntaxError("Missing " + fieldName); + ContestImportUtilities.syntaxError("Missing " + fieldName); } } public void assignJudgingType(String[] yaml, Problem problem, boolean overrideManualReviewFlag) { - Map map = loadYaml(null, yaml); + Map map = ContestImportUtilities.loadYaml(null, yaml); assignJudgingType(map, problem, overrideManualReviewFlag); } @@ -3255,20 +3046,20 @@ protected void assignJudgingType(Map map, Problem problem, boole // System.out.println("debug problem "+problem.getShortName()+" "+map); // } - if (isValuePresent(map, SEND_PRELIMINARY_JUDGEMENT_KEY)) { - boolean sendPreliminary = fetchBooleanValue(map, SEND_PRELIMINARY_JUDGEMENT_KEY, false); + if (ContestImportUtilities.isValuePresent(map, SEND_PRELIMINARY_JUDGEMENT_KEY)) { + boolean sendPreliminary = ContestImportUtilities.fetchBooleanValue(map, SEND_PRELIMINARY_JUDGEMENT_KEY, false); problem.setPrelimaryNotification(sendPreliminary); } - if (isValuePresent(map, COMPUTER_JUDGING_KEY)) { - boolean computerJudged = fetchBooleanValue(map, COMPUTER_JUDGING_KEY, false); + if (ContestImportUtilities.isValuePresent(map, COMPUTER_JUDGING_KEY)) { + boolean computerJudged = ContestImportUtilities.fetchBooleanValue(map, COMPUTER_JUDGING_KEY, false); problem.setComputerJudged(computerJudged); } boolean manualReview = problem.isManualReview(); - if (isValuePresent(map, MANUAL_REVIEW_KEY)) { - manualReview = fetchBooleanValue(map, MANUAL_REVIEW_KEY, false); + if (ContestImportUtilities.isValuePresent(map, MANUAL_REVIEW_KEY)) { + manualReview = ContestImportUtilities.fetchBooleanValue(map, MANUAL_REVIEW_KEY, false); } if (overrideManualReviewFlag) { diff --git a/src/edu/csus/ecs/pc2/imports/ccs/IContestLoader.java b/src/edu/csus/ecs/pc2/imports/ccs/IContestLoader.java index 15065d1c7..e323dbff9 100644 --- a/src/edu/csus/ecs/pc2/imports/ccs/IContestLoader.java +++ b/src/edu/csus/ecs/pc2/imports/ccs/IContestLoader.java @@ -66,6 +66,9 @@ public interface IContestLoader { final String CLICS_CONTEST_DURATION = "duration"; final String CLICS_CONTEST_FREEZE_DURATION = "scoreboard_freeze_duration"; final String CLICS_CONTEST_SCOREBOARD_TYPE = "scoreboard_type"; + final String CLICS_CONTEST_SCOREBOARD_TYPE_PASSFAIL = "pass-fail"; + final String CLICS_CONTEST_SCOREBOARD_TYPE_SCORE = "score"; + // This is not currently used, but penalty_time SHOULD be used instead of // reading it from properties. It is here for completeness, and, it happens // to be a required value in the yaml, but we do not enforce that. diff --git a/src/edu/csus/ecs/pc2/imports/ccs/TestCaseInfo.java b/src/edu/csus/ecs/pc2/imports/ccs/TestCaseInfo.java new file mode 100644 index 000000000..7926c7d8f --- /dev/null +++ b/src/edu/csus/ecs/pc2/imports/ccs/TestCaseInfo.java @@ -0,0 +1,69 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.imports.ccs; + +import java.io.Serializable; + +/** + * Information about a specific test case + * + * @author John Buck, PC^2 Team, pc2@ecs.csus.edu + */ +public class TestCaseInfo implements Serializable { + + private static final long serialVersionUID = 1L; + + public static final String TEST_CASE_INPUT_EXTENSION = ".in"; + public static final String TEST_CASE_ANSWER_EXTENSION = ".ans"; + + private String inputFileName; + private String answerFileName; + private TestDataGroup group; + + public TestCaseInfo(String inFile, String ansFile, TestDataGroup group) { + inputFileName = inFile; + answerFileName = ansFile; + this.group = group; + } + + @Override + public String toString() { + String groupName; + if(group == null) { + groupName = "None"; + } else { + groupName = group.getGroupName(); + } + return "group: " + groupName + "; inputFileName: " + inputFileName + "; answerFileName: " + answerFileName; + } + /** + * @return the inputFileName + */ + public String getInputFileName() { + return inputFileName; + } + /** + * @param inputFileName the inputFileName to set + */ + public void setInputFileName(String inputFileName) { + this.inputFileName = inputFileName; + } + /** + * @return the answerFileName + */ + public String getAnswerFileName() { + return answerFileName; + } + /** + * @param answerFileName the answerFileName to set + */ + public void setAnswerFileName(String answerFileName) { + this.answerFileName = answerFileName; + } + + /** + * @return the TestDataGroup where this test case resides + */ + public TestDataGroup getGroup() { + return group; + } +} diff --git a/src/edu/csus/ecs/pc2/imports/ccs/TestDataGroup.java b/src/edu/csus/ecs/pc2/imports/ccs/TestDataGroup.java new file mode 100644 index 000000000..dba1571e9 --- /dev/null +++ b/src/edu/csus/ecs/pc2/imports/ccs/TestDataGroup.java @@ -0,0 +1,391 @@ +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.imports.ccs; + +import java.io.File; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; + +import org.yaml.snakeyaml.error.MarkedYAMLException; + +import edu.csus.ecs.pc2.core.exception.YamlLoadException; +import edu.csus.ecs.pc2.core.log.Log; + +/** + * Parameters for the representation of a group of test data + * + * @author John Buck, PC^2 Team, pc2@ecs.csus.edu + */ +public class TestDataGroup implements Serializable { + + private static final long serialVersionUID = 1L; + + public static final String SAMPLE_GROUP = "sample"; + public static final String SECRET_GROUP = "secret"; + public static final String TESTDATA_YAML = "testdata.yaml"; + private static final String ON_REJECT_KEY = "on_reject"; + private static final String GRADING_KEY = "grading"; + private static final String GRADER_FLAGS_KEY = "grader_flags"; + private static final String INPUT_VALIDATOR_FLAGS_KEY = "input_validator_flags"; + private static final String OUTPUT_VALIDATOR_FLAGS_KEY = "output_validator_flags"; + private static final String ACCEPT_SCORE_KEY = "accept_score"; + private static final String REJECT_SCORE_KEY = "reject_score"; + private static final String RANGE_KEY = "range"; + + public enum OnRejectTypes { + BREAK, + CONTINUE; + } + + public enum GradingTypes { + DEFAULT, + CUSTOM; + } + + // Defaults + private static final double DEFAULT_ACCEPT_SCORE = 1.0; + private static final double DEFAULT_REJECT_SCORE = 0.0; + private static final double DEFAULT_RANGE_MIN = Double.NEGATIVE_INFINITY; + private static final double DEFAULT_RANGE_MAX = Double.POSITIVE_INFINITY; + + + private OnRejectTypes on_reject = OnRejectTypes.BREAK; + private GradingTypes grading = GradingTypes.DEFAULT; + private String grader_flags = null; + private String input_validator_flags = null; + private String output_validator_flags = null; + private double accept_score = DEFAULT_ACCEPT_SCORE; + private double reject_score = DEFAULT_REJECT_SCORE; + private double range_min = DEFAULT_RANGE_MIN; + private double range_max = DEFAULT_RANGE_MAX; + + private String groupName = null; + + private String dataDirectoryName = null; + + private ArrayList subGroups = new ArrayList(); + private ArrayList testCases = new ArrayList(); + private TestDataGroup parent = null; + + // This is the total of all test cases in this group and all groups under it. + int totalTestCases = 0; + + public TestDataGroup(String groupName, String baseDataDirectoryName, TestDataGroup parentGroup) { + this.groupName = groupName; + this.parent = parentGroup; + this.dataDirectoryName = baseDataDirectoryName; + + if(parentGroup != null) { + on_reject = parentGroup.on_reject; + grading = parentGroup.grading; + input_validator_flags = parentGroup.input_validator_flags; + output_validator_flags = parentGroup.output_validator_flags; + accept_score = parentGroup.accept_score; + reject_score = parentGroup.reject_score; + range_min = parentGroup.range_min; + range_max = parentGroup.range_max; + } + } + + /** + * Read the data group's specification file + * @param testDataYamlFile - file name to read + * @return true if the file exists and was processed successfully + * false if no file exists + * @throws YamlLoadException on errors + */ + public boolean processDataYaml(String testDataYamlFile) { + boolean result = false; + Map content = null; + + if(new File(testDataYamlFile).isFile()) { + try { + content = ContestImportUtilities.loadYaml(testDataYamlFile); + } catch (MarkedYAMLException e) { + throw new YamlLoadException("DataGroup Yaml parsing error", e, testDataYamlFile); + } + if(content != null) { + + String val = ContestImportUtilities.fetchValue(content, ON_REJECT_KEY); + if(val != null) { + if(val.equalsIgnoreCase(OnRejectTypes.BREAK.toString())) { + on_reject = OnRejectTypes.BREAK; + } else if(val.equalsIgnoreCase(OnRejectTypes.CONTINUE.toString())) { + on_reject = OnRejectTypes.CONTINUE; + } else { + throw new YamlLoadException("TestDatagroup Yaml error: invalid value for '" + ON_REJECT_KEY + "' property for " + getGroupName() + " in " + testDataYamlFile); + } + } + + val = ContestImportUtilities.fetchValue(content, GRADING_KEY); + if(val != null) { + if(val.equalsIgnoreCase(GradingTypes.CUSTOM.toString())) { + grading = GradingTypes.CUSTOM; + } else if(val.equalsIgnoreCase(GradingTypes.DEFAULT.toString())) { + grading = GradingTypes.DEFAULT; + } else { + throw new YamlLoadException("TestDatagroup Yaml error: invalid value for '" + GRADING_KEY + "' property for " + getGroupName() + " in " + testDataYamlFile); + } + } + grader_flags = ContestImportUtilities.fetchValue(content, GRADER_FLAGS_KEY); + input_validator_flags = ContestImportUtilities.fetchValue(content, INPUT_VALIDATOR_FLAGS_KEY); + output_validator_flags = ContestImportUtilities.fetchValue(content, OUTPUT_VALIDATOR_FLAGS_KEY); + + Double dval = ContestImportUtilities.fetchDoubleValue(content, ACCEPT_SCORE_KEY); + if(dval != null) { + accept_score = dval.doubleValue(); + } + dval = ContestImportUtilities.fetchDoubleValue(content, REJECT_SCORE_KEY); + if(dval != null) { + reject_score = dval.doubleValue(); + } + + val = ContestImportUtilities.fetchValue(content, RANGE_KEY); + if(val != null) { + String [] rangeParts = val.split("\\s+"); + + if(rangeParts.length != 2) { + throw new YamlLoadException("TestDatagroup Yaml error: bad '" + RANGE_KEY + "' property for " + getGroupName() + " in " + testDataYamlFile); + } else { + double r1, r2; + try { + range_min = Double.parseDouble(rangeParts[0]); + range_max = Double.parseDouble(rangeParts[1]); + } catch(Exception e) { + throw new YamlLoadException("TestDatagroup Yaml error: bad values for '" + RANGE_KEY + "' property for " + getGroupName() + " in " + testDataYamlFile); + } + + } + } + result = true; + } + } + return(result); + } + + /** + * Processes the data directory (sample and secret groups) and recurses + * The dataDirectoryName is the "data" directory and may only have "sample" and "secret" and a + * testdata.yaml. Anything else is ignored (at the top level). + * + * @param dataDirectoryName - path to the "data" directory - can be relative. + * @param log - for logging + * @return true if all test cases were successfully read AND there ARE test cases. + */ + public boolean readTestCases(Log log) { + TestDataGroup group; + String testDataYaml; + + // First process top level testdata yaml file + testDataYaml = this.dataDirectoryName + File.separator + TESTDATA_YAML; + + if(processDataYaml(testDataYaml) == false) { + log.log(Log.INFO, "Did not find " + testDataYaml + " for top level data - using default"); + } + group = readTestDataGroup(SAMPLE_GROUP, this, log); + if(group == null) { + log.log(Log.INFO, "Did not find sample data group in " + this.dataDirectoryName); + } else { + subGroups.add(group); + totalTestCases += group.getTotalTestCases(); + } + group = readTestDataGroup(SECRET_GROUP, this, log); + if(group == null) { + throw new YamlLoadException("Did not find required secret data group in " + this.dataDirectoryName); + } + subGroups.add(group); + totalTestCases += group.getTotalTestCases(); + return(totalTestCases > 0); + } + + /** + * Reads the test cases for a group, processes its optional testdata.yaml, and + * processes subgroups. + * + * @param groupName path relative to "data" of the group + * @return TestDataGroup representing the groupName + */ + private TestDataGroup readTestDataGroup(String groupName, TestDataGroup parent, Log log) { + + TestDataGroup newGroup = null; + String groupDirectoryName = dataDirectoryName + File.separator + groupName; + File dir = new File(groupDirectoryName); + + if(dir.isDirectory()) { + // First process this group's testdata yaml file + String testDataYaml = groupDirectoryName + File.separator + TESTDATA_YAML; + newGroup = new TestDataGroup(groupName, dataDirectoryName, parent); + + if(newGroup.processDataYaml(testDataYaml) == false) { + log.log(Log.INFO, "Did not find " + testDataYaml + " for top level data - using default from " + parent.getGroupName()); + } + String [] files = dir.list(); + String ansFileName, inFileName; + + if(files != null) { + ArrayList subDirs = new ArrayList(); + HashSet ansFiles = new HashSet(); + + Arrays.sort(files); + // Make up a hash set of answer files + for(String file : files) { + if(file.endsWith(TestCaseInfo.TEST_CASE_ANSWER_EXTENSION)) { + ansFiles.add(file); + } + } + for(String file : files) { + inFileName = groupDirectoryName + File.separator + file; + if(file.endsWith(TestCaseInfo.TEST_CASE_INPUT_EXTENSION)) { + ansFileName = file.substring(0, file.lastIndexOf('.')) + TestCaseInfo.TEST_CASE_ANSWER_EXTENSION; + // Ignore .in files with no .ans file + if(!ansFiles.contains(ansFileName)) { + log.log(Log.WARNING, "There is an input file (" + file + ") with no answer file in " + groupDirectoryName); + continue; + } + // Make sure they are both text files + if(!(new File(inFileName).isFile())) { + log.log(Log.WARNING, "The input file (" + inFileName + ") is not a file - ignored"); + continue; + } + ansFileName = groupDirectoryName + File.separator + ansFileName; + if(!(new File(ansFileName).isFile())) { + log.log(Log.WARNING, "The answer file (" + ansFileName + ") is not a file - input and answer are ignored"); + continue; + } + newGroup.testCases.add(new TestCaseInfo(inFileName, ansFileName, newGroup)); + newGroup.totalTestCases++; + } else if(!ansFiles.contains(file)){ + // remember list of directories (new groups) we found - we do this after doing all the .in and .ans files + if(new File(inFileName).isDirectory()) { + subDirs.add(groupName + File.separator + file); + } + } + } + TestDataGroup newSubGroup; + // Process new subgroups + for(String subDir : subDirs) { + newSubGroup = newGroup.readTestDataGroup(subDir, newGroup, log); + if(newSubGroup != null) { + newGroup.subGroups.add(newSubGroup); + totalTestCases += newSubGroup.getTotalTestCases(); + } + } + } + } + return newGroup; + } + + /** + * Append all subcases in (and under) this testgroup to the supplied ArrayList + * + * @param arInfo List of all TestCaseInfo's in and under this group. + */ + private void appendTestCaseInfo(ArrayList arInfo) { + arInfo.addAll(testCases); + for(TestDataGroup group : subGroups) { + group.appendTestCaseInfo(arInfo); + } + } + + public ArrayList getAllTestCaseInfo() { + ArrayList arInfo = new ArrayList(); + appendTestCaseInfo(arInfo); + return(arInfo); + } + + public TestDataGroup getParent() { + return parent; + } + + public int getTotalTestCases() { + return totalTestCases; + } + + public String getDataDirectoryName() { + return dataDirectoryName; + } + + public ArrayList getTestDataGroups() { + return subGroups; + } + + public ArrayList getTestCaseInfo() { + return testCases; + } + + public boolean isOnRejectBreak() { + return on_reject.equals(OnRejectTypes.BREAK); + } + + public boolean isOnRejectContinue() { + return on_reject.equals(OnRejectTypes.CONTINUE); + } + + public boolean isGradingDefault() { + return grading.equals(GradingTypes.DEFAULT); + } + + public boolean isGradingCustom() { + return grading.equals(GradingTypes.CUSTOM); + } + + public void setAcceptScore(double score) { + accept_score = score; + } + + public double getAcceptScore() { + return accept_score; + } + + public void setRejectScore(double score) { + reject_score = score; + } + + public double getRejectScore() { + return reject_score; + } + + /** + * @return the Grader Flags + */ + public String getGraderFlags() { + return grader_flags; + } + + /** + * @return the Input Validator Flags + */ + public String getInputValidatorFlags() { + return input_validator_flags; + } + + /** + * @return the Output Validator Flags + */ + public String getOutputValidatorFlags() { + return output_validator_flags; + } + + /** + * @return the minimum score + */ + public double getRangeMin() { + return range_min; + } + + /** + * @return the maximum score + */ + public double getRangeMax() { + return range_max; + } + + /** + * @return the groupName + */ + public String getGroupName() { + return groupName; + } +} diff --git a/src/edu/csus/ecs/pc2/ui/MultipleDataSetPane.java b/src/edu/csus/ecs/pc2/ui/MultipleDataSetPane.java index 43da859d7..421e18766 100644 --- a/src/edu/csus/ecs/pc2/ui/MultipleDataSetPane.java +++ b/src/edu/csus/ecs/pc2/ui/MultipleDataSetPane.java @@ -3,7 +3,6 @@ import java.awt.Color; import java.awt.Component; -import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; @@ -18,6 +17,7 @@ import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; @@ -45,11 +45,9 @@ import edu.csus.ecs.pc2.core.model.SerializedFile; import edu.csus.ecs.pc2.core.model.inputValidation.InputValidationResult; -import javax.swing.JCheckBox; - /** * Multiple Test Data set UI. - * + * * @author pc2@ecs.csus.edu */ @@ -57,7 +55,7 @@ public class MultipleDataSetPane extends JPanePlugin { /** - * + * */ private static final long serialVersionUID = -5975163495479418935L; @@ -96,7 +94,7 @@ public class MultipleDataSetPane extends JPanePlugin { private final ButtonGroup inputStorageButtonGroup = new ButtonGroup(); private String loadDirectory = null; - + //John's local machine testing directory // private String loadDirectory = "C:\\clevengr\\contest\\PC2\\v9\\TestContests\\sumithello2\\config\\sumit"; @@ -109,7 +107,7 @@ public class MultipleDataSetPane extends JPanePlugin { /** * This method initializes - * + * */ public MultipleDataSetPane() { super(); @@ -119,7 +117,7 @@ public MultipleDataSetPane() { /** * This method initializes this - * + * */ private void initialize() { this.setSize(new Dimension(766, 526)); @@ -139,7 +137,7 @@ public String getPluginTitle() { /** * Clone data files and populate in pane. - * + * * @param aProblemDataFiles * @throws CloneNotSupportedException */ @@ -159,6 +157,7 @@ public void setProblemDataFiles(ProblemDataFiles datafiles) { this.problemDataFiles = datafiles; SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { populateUI(); } @@ -172,11 +171,11 @@ private void enableInputDataStoragePanel(boolean enable) { } /** - * This method updates the MultipleDataSetPane UI with the data currently specified in the + * This method updates the MultipleDataSetPane UI with the data currently specified in the * tableModel, problem, and problemDataFiles fields (variables within this class). */ protected void populateUI() { - + tableModel.setFiles(problemDataFiles); tableModel.fireTableDataChanged(); @@ -196,10 +195,10 @@ protected void populateUI() { } } enableInputDataStoragePanel(enable); - + //set the StopOnFirstFailedTestCase checkbox to match what is specified in the problem getChckbxStopOnFirstFailedTestCase().setSelected(problem.isStopOnFirstFailedTestCase()); - + getLoadSamplesFirstCheckbox().setSelected(problem.isLoadDataFilesSamplesFirst()); } @@ -228,10 +227,13 @@ public void resizeColumnWidth(JTable table) { width = 100; break; case 1: - width = 300; + width = 200; break; case 2: - width = 300; + width = 200; + break; + case 3: + width = 200; break; default: System.err.println("MultipleDataSetPane.resizeColumnWidthUnhandled col " + col); @@ -274,7 +276,7 @@ private void dump(ProblemDataFiles problemDataFiles2, String string) { /** * This method initializes centerPane - * + * * @return javax.swing.JPanel */ private JScrollPane getListBoxScrollPane() { @@ -287,7 +289,7 @@ private JScrollPane getListBoxScrollPane() { /** * This method initializes testDataSetsListBox - * + * * @return edu.csus.ecs.pc2.ui.MCLB */ public JTable getTestDataSetsListBox() { @@ -331,7 +333,7 @@ public void clearDataFiles() { /** * Compares current set of data sets to input datafiles. - * + * */ boolean hasChanged(ProblemDataFiles originalFiles) { @@ -363,7 +365,7 @@ boolean hasChanged(ProblemDataFiles originalFiles) { /** * Compare serializedfile arrays. - * + * * @param listOne * @param listTwo * @return 0 if identical, non-zero if different, returns 2 if either input are null. @@ -392,6 +394,7 @@ private JButton getBtnDelete() { btnDelete = new JButton("Delete"); btnDelete.setToolTipText("Delete selected data sets"); btnDelete.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { int rowNumber = testDataSetsListBox.getSelectedRow(); if (rowNumber != -1) { @@ -406,7 +409,7 @@ public void actionPerformed(ActionEvent e) { /** * Remove the specified ROW from the table model holding Test Cases. Note that while Test Cases are numbered starting from 1, ROW NUMBERS start from 0! - * + * * @param rowNumber * - the row to remove, where the first row is row 0 */ @@ -483,17 +486,17 @@ protected void loadDataFiles() { String sampleBaseDirectoryName = new File(baseDirectoryName).getParent() + File.separator +"sample"; boolean ishere = new File(sampleBaseDirectoryName).isDirectory(); - + if (loadSamplesFirstCheckbox.isSelected() && new File(sampleBaseDirectoryName).isDirectory()) { getLog().info("Loading sample files from sample dir " + sampleBaseDirectoryName); - // load sample files first + // load sample files first problemDataFiles = loadDataFiles(problem, problemDataFiles, sampleBaseDirectoryName, ".in", ".ans", externalFiles); } problemDataFiles = loadDataFiles(problem, problemDataFiles, baseDirectoryName, ".in", ".ans", externalFiles); - + } catch (Exception e) { getController().getLog().log(Log.INFO, e.getMessage(), e); showMessage(this, "Import Failed", e.getMessage()); @@ -502,24 +505,24 @@ protected void loadDataFiles() { dump(problemDataFiles, "debug after load"); } - //Populate the MultipleDataSetPane + //Populate the MultipleDataSetPane populateUI(); // Populate General Pane data and answer files too editProblemPane.setJudgingTestSetOne(tableModel.getFiles()); - + //update the Input Validator status: since we're loading new data files, any prior "I.V. Run Results" are invalid getEditProblemPane().getInputValidatorPane().setCustomInputValidatorResults(new InputValidationResult[0]); getEditProblemPane().getInputValidatorPane().setCustomInputValidationStatus(InputValidationStatus.NOT_TESTED); getEditProblemPane().getInputValidatorPane().setCustomInputValidatorHasBeenRun(false); - + getEditProblemPane().getInputValidatorPane().setVivaInputValidatorResults(new InputValidationResult[0]); getEditProblemPane().getInputValidatorPane().setVivaInputValidationStatus(InputValidationStatus.NOT_TESTED); getEditProblemPane().getInputValidatorPane().setVivaInputValidatorHasBeenRun(false); - + //update the Results table: since we're loading new data files, any prior results are invalid getEditProblemPane().getInputValidatorPane().updateResultsTable(new InputValidationResult[0]); - + //ask the user if they want to run the currently-selected Input Validator (if any) if (okToRunInputValidator()) { INPUT_VALIDATOR_TYPE currentIV = getEditProblemPane().getInputValidatorPane().getCurrentInputValidatorType(); @@ -527,7 +530,7 @@ protected void loadDataFiles() { msg += "\nDo you want to run the currently-selected Input Validator on the new input data files?"; int result = JOptionPane.showConfirmDialog(this, msg, "Run Input Validator? ", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (result == JOptionPane.YES_OPTION) { - + //attempt to run the currently-selected Input Validator. Note that this method will fail with an error msg/ // dialog if there is no selected and properly configured Input Validator. getEditProblemPane().getInputValidatorPane().runCurrentlySelectedInputValidator(); @@ -535,7 +538,7 @@ protected void loadDataFiles() { } getEditProblemPane().enableUpdateButton(); } - + private boolean okToRunInputValidator() { return (getEditProblemPane().getInputValidatorPane().okToRunInputValidator()); } @@ -546,6 +549,7 @@ private JButton getBtnLoad() { btnLoad.setToolTipText("Load data sets from directory"); btnLoad.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { loadDataFiles(); } @@ -586,23 +590,23 @@ public ProblemDataFiles loadDataFiles(Problem aProblem, ProblemDataFiles files, throw new RuntimeException("Mismatch: expecting the same number of '" + dataExtension + "' and '" + answerExtension + "' files in " + dataFileBaseDirectory + "\n (found " + inputFileNames.length + " '" + dataExtension + "' files vs. " + answerFileNames.length + " '" + answerExtension + "' files)"); } - + SerializedFile[] inputFiles = Utilities.createSerializedFiles(dataFileBaseDirectory, inputFileNames, externalDataFiles); SerializedFile[] answertFiles = Utilities.createSerializedFiles(dataFileBaseDirectory, answerFileNames, externalDataFiles); SerializedFile [] existingFiles = files.getJudgesDataFiles(); - + if (existingFiles != null && existingFiles.length > 0){ /** * Existing files present, concatenate files */ - + Object[] newFiles = Utilities.concatenateArrays(existingFiles, inputFiles); inputFiles = (SerializedFile[]) newFiles; } - + existingFiles = files.getJudgesAnswerFiles(); - + if (existingFiles != null && existingFiles.length > 0){ /** * Existing files present, concatenate files @@ -628,7 +632,7 @@ public JPanel getInputDataStoragePanel() { inputDataStoragePanel.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Choose storage option before loading data files:", TitledBorder.LEADING, TitledBorder.TOP, new java.awt.Font("Dialog", java.awt.Font.BOLD, 12), new Color(0, 0, 0))); inputDataStoragePanel.setAlignmentX(0.0f); - inputDataStoragePanel.setLayout(new BoxLayout((Container) inputDataStoragePanel, BoxLayout.Y_AXIS)); + inputDataStoragePanel.setLayout(new BoxLayout(inputDataStoragePanel, BoxLayout.Y_AXIS)); inputDataStoragePanel.add(getRdbtnCopyDataFiles()); inputDataStoragePanel.add(getRdBtnKeepDataFilesExternal()); inputDataStoragePanel.add(getLblWhatsThis()); @@ -653,6 +657,7 @@ public JRadioButton getRdBtnKeepDataFilesExternal() { if (rdBtnKeepDataFilesExternal == null) { rdBtnKeepDataFilesExternal = new JRadioButton("Keep Data Files external to PC2 (requires you to copy files to Judge's machines)"); rdBtnKeepDataFilesExternal.addItemListener(new ItemListener() { + @Override public void itemStateChanged(ItemEvent e) { if (rdBtnKeepDataFilesExternal.isSelected()) { verifyJudgeDataPathIsSet(); @@ -733,7 +738,7 @@ private Component getVerticalStrut_2() { } return verticalStrut_2; } - + private JLabel getLblWhatsThis() { if (lblWhatsThis == null) { lblWhatsThis = new JLabel(""); @@ -748,7 +753,7 @@ public void mousePressed(MouseEvent e) { } return lblWhatsThis; } - + private String whatsThisMessage = "PC2 has two different ways of handling data files: Internal and External. " + "\n\n'Internal' means that PC2 will load the file data internally into PC2 memory. " + "\nThe advantage of this is that it allows PC2 to automatically transmit the file data to a Judge each time the Judge requests to judge a submission." @@ -773,12 +778,13 @@ private Component getHorizontalStrut_1() { horizontalStrut_1.setMinimumSize(new Dimension(30, 0)); } return horizontalStrut_1; - } - + } + protected JCheckBox getChckbxStopOnFirstFailedTestCase() { if (chckbxStopOnFirstFailedTestCase == null) { chckbxStopOnFirstFailedTestCase = new JCheckBox("Stop execution on first failed test case"); chckbxStopOnFirstFailedTestCase.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { getEditProblemPane().enableUpdateButton(); } @@ -803,7 +809,7 @@ private JCheckBox getLoadSamplesFirstCheckbox() { public boolean isLoadSamplesFirst() { return getLoadSamplesFirstCheckbox().isSelected(); } - + private Component getHorizontalStrut_2() { if (horizontalStrut_2 == null) { horizontalStrut_2 = Box.createHorizontalStrut(20); @@ -812,5 +818,5 @@ private Component getHorizontalStrut_2() { } return horizontalStrut_2; } - + } // @jve:decl-index=0:visual-constraint="10,10" diff --git a/src/edu/csus/ecs/pc2/ui/TestCaseTableModel.java b/src/edu/csus/ecs/pc2/ui/TestCaseTableModel.java index 53879592d..1ca8a9dc2 100644 --- a/src/edu/csus/ecs/pc2/ui/TestCaseTableModel.java +++ b/src/edu/csus/ecs/pc2/ui/TestCaseTableModel.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.ui; import java.util.Arrays; @@ -7,33 +7,34 @@ import javax.swing.table.DefaultTableModel; import edu.csus.ecs.pc2.core.model.ProblemDataFiles; +import edu.csus.ecs.pc2.imports.ccs.TestDataGroup; -// TODO need to figure out update key used when update data files +// TODO need to figure out update key used when update data files // to avoid duping ProblemDataFiles on update. /** - * + * * @author ICPC * */ public class TestCaseTableModel extends DefaultTableModel { - private static String[] colNames = { "Test Case", "Data File", "Answer File" }; + private static String[] colNames = { "Test Case", "Data Group", "Data File", "Answer File" }; private static Vector columnNames = new Vector(Arrays.asList(colNames)); private ProblemDataFiles files; /** - * + * */ private static final long serialVersionUID = 1L; - + public TestCaseTableModel(ProblemDataFiles files) { super(null, columnNames); setFiles(files); } - + public TestCaseTableModel() { super(null, columnNames); setRowCount(0); @@ -59,13 +60,21 @@ public Object getValueAt(int row, int column) { obj = "" + (row + 1); break; case 1: + TestDataGroup [] dataGroups = files.getJudgesDataGroups(); + if(dataGroups == null || row >= dataGroups.length) { + obj = "None"; + } else { + obj = dataGroups[row].getGroupName(); + } + break; + case 2: if (files == null || files.getJudgesDataFiles() == null || files.getJudgesDataFiles().length <= row ){ obj = null; } else { obj = files.getJudgesDataFiles()[row].getName(); } break; - case 2: + case 3: if (files == null || files.getJudgesAnswerFiles() == null || files.getJudgesAnswerFiles().length <= row ){ obj = null; } else { @@ -81,7 +90,7 @@ public Object getValueAt(int row, int column) { /** * Remove the specified row from the table. Note that row numbers start with zero! - * + * * @param row - the row number to be removed, where the first row is row zero */ @Override @@ -89,7 +98,7 @@ public void removeRow(int row) { files.removeDataSet(row); super.removeRow(row); } - + public ProblemDataFiles getFiles() { // TODO 917 populate files from table model. return files; diff --git a/test/edu/csus/ecs/pc2/convert/LoadRunsTest.java b/test/edu/csus/ecs/pc2/convert/LoadRunsTest.java index d17e29e2f..43d14d497 100644 --- a/test/edu/csus/ecs/pc2/convert/LoadRunsTest.java +++ b/test/edu/csus/ecs/pc2/convert/LoadRunsTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.convert; import java.io.File; @@ -40,7 +40,7 @@ /** * Unit Tests. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class LoadRunsTest extends AbstractTestCase { @@ -57,6 +57,12 @@ public LoadRunsTest(String name) { super(name); } + @Override + protected void setUp() throws Exception { + ensureStaticLog(); + super.setUp(); + } + public void testLoadCDP() throws Exception { String configDir = SampleCDP.getDir() + IContestLoader.CONFIG_DIRNAME; @@ -89,7 +95,7 @@ public void testLoadCDP() throws Exception { /** * Load contest from contest.yaml. - * + * * @param contest * @param configDir * @return @@ -112,7 +118,7 @@ private IInternalContest loadYaml(IInternalContest contest, String configDir) { /** * Test loading runs and EF runs content. - * + * * @throws Exception */ public void testLoadEFRuns() throws Exception { @@ -164,7 +170,7 @@ public void testLoadEFRuns() throws Exception { /** * Load contest runs based on yaml. - * + * * @throws Exception */ public void testLoadContestRuns() throws Exception { @@ -235,7 +241,7 @@ public void testLoadContestRuns() throws Exception { /** * Compare event feeds. - * + * * @param inputEventFeed * @param outputEventFeedFilename * @throws ParserConfigurationException @@ -338,7 +344,7 @@ private void loadDefaultJudgements(IInternalContest contest) { /** * Test loading runs into model but not from yaml. - * + * * @throws Exception */ public void testLoadRuns() throws Exception { @@ -458,7 +464,7 @@ public void testLoadRuns() throws Exception { /** * Load groups.tsv and teams.tsv files. - * + * * @param contest * @param dir * @throws Exception @@ -495,7 +501,7 @@ private void updateGroupAndTeams(IInternalContest contest, String dir) throws Ex /** * Update Groups */ - Group[] updatedGroups = (Group[]) groupList.toArray(new Group[groupList.size()]); + Group[] updatedGroups = groupList.toArray(new Group[groupList.size()]); for (Group group : updatedGroups) { // getController().updateGroup(group); contest.updateGroup(group); @@ -504,7 +510,7 @@ private void updateGroupAndTeams(IInternalContest contest, String dir) throws Ex /** * UpdateAccounts */ - Account[] updatedAccounts = (Account[]) accountList.toArray(new Account[accountList.size()]); + Account[] updatedAccounts = accountList.toArray(new Account[accountList.size()]); // getController().updateAccounts(updatedAccounts); for (Account account : updatedAccounts) { contest.updateAccount(account); @@ -514,7 +520,7 @@ private void updateGroupAndTeams(IInternalContest contest, String dir) throws Ex /** * Lookup group by externalId - * + * * @param contest2 * @param externalId * @return diff --git a/test/edu/csus/ecs/pc2/core/UtilitiesTest.java b/test/edu/csus/ecs/pc2/core/UtilitiesTest.java index 61d5c6b96..caa9d2b56 100644 --- a/test/edu/csus/ecs/pc2/core/UtilitiesTest.java +++ b/test/edu/csus/ecs/pc2/core/UtilitiesTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core; import java.io.File; @@ -25,6 +25,12 @@ */ public class UtilitiesTest extends AbstractTestCase { + @Override + protected void setUp() throws Exception { + ensureStaticLog(); + super.setUp(); + } + public void testOne() { char[] array1 = null; char[] array2 = null; @@ -203,7 +209,7 @@ private void createZeroByteFile(String filename) throws FileNotFoundException { } /** - * + * * @throws Exception */ public void testvalidateCDP() throws Exception { @@ -220,7 +226,7 @@ public void testvalidateCDP() throws Exception { // ContestYAMLLoader loader = new ContestYAMLLoader(); IContestLoader loader = new ContestSnakeYAMLLoader(); - + IInternalContest contest = loader.fromYaml(null, testDirectory); Problem[] problems = contest.getProblems(); @@ -260,7 +266,7 @@ public void testvalidateCDP() throws Exception { /** * test - * + * * @throws Exception */ public void testTestLocateFile() throws Exception { @@ -294,7 +300,7 @@ public void testTestLocateFile() throws Exception { /** * Test java.util.Arrays.equals. - * + * * @throws Exception */ public void testArrayCompare() throws Exception { @@ -322,15 +328,15 @@ public void testgetProblemLetter() throws Exception { problemNumber = 26; actual = Utilities.getProblemLetter(problemNumber); assertEquals("Expect letter for " + problemNumber, "Z", actual); - + problemNumber = 27; actual = Utilities.getProblemLetter(problemNumber); assertEquals("Expect letter for " + problemNumber, "AA", actual); - + problemNumber = 28; actual = Utilities.getProblemLetter(problemNumber); assertEquals("Expect letter for " + problemNumber, "AB", actual); - + problemNumber = 55; actual = Utilities.getProblemLetter(problemNumber); assertEquals("Expect letter for " + problemNumber, "BC", actual); @@ -358,26 +364,26 @@ public void testgetProblemNumber() throws Exception { expected = 0; assertEquals("Expeting number " + expected + " for problem " + probMissing, expected, actual2); } - + public void testUnixify() throws Exception { - + String input = ".\\testout\\ExportYAMLTest\\testOne\\sumit\\data\\secret\\sumit.dat"; String expected = "./testout/ExportYAMLTest/testOne/sumit/data/secret/sumit.dat"; - + String actual = Utilities.unixifyPath(input); - + assertEquals(expected, actual); - - + + } - + /** * Test OS Type. */ public void testGetGetOSType() { - + OSType type = Utilities.getOSType(); - + switch (type) { case UNCLASSIFIED: /** @@ -385,32 +391,32 @@ public void testGetGetOSType() { */ fail ("Expecting OS Type to not be "+OSType.UNCLASSIFIED); break; - + case UNDEFINED: /** * OS type must not be undefined. */ fail ("Expecting OS Type to not be "+OSType.UNDEFINED); break; - + default: break; } - + /** * Tests for when running unit test on Windows */ debugPrint("OS Type is: "+type); - + // assertEquals("Windows", type.toString()); // assertEquals(OSType.WINDOWS, type); - - + + } - + /** * Test for files that are considered executable. - * + * * @throws Exception */ public void testisExecutableExtension() throws Exception { @@ -435,7 +441,7 @@ public void testisExecutableExtension() throws Exception { /** * Test for files that are not considered executable. - * + * * @throws Exception */ public void testTwo() throws Exception { @@ -481,43 +487,43 @@ public void testgetFileBaseName() throws Exception { assertEquals("Expected basename ", expected, actual); } } - + /** * Test loadFile(String, long); - * + * * @throws Exception */ public void testloadFileWithNum() throws Exception { - + String filename = "samps/pc2v9.ini"; - + int num = 10; - + String[] lines = Utilities.loadFile(filename, num); assertEquals("Expecting "+num+" lines", num, lines.length); - + num = 36; lines = Utilities.loadFile(filename, num); assertEquals("Expecting "+num+" lines", num, lines.length); } - + /** * Test get data directory names. - * + * * @throws Exception */ public void testgetCDPDataDirectories() throws Exception { String dataFileBaseDirectory = "samps/contests/tenprobs/config"; List dataDirs = Utilities.getCDPDataDirectories(dataFileBaseDirectory); assertEquals("Expecting data dirs for under "+dataFileBaseDirectory, 10, dataDirs.size()); - + dataFileBaseDirectory = "samps/contests/sumithello/config"; dataDirs = Utilities.getCDPDataDirectories(dataFileBaseDirectory); assertEquals("Expecting data dirs for under "+dataFileBaseDirectory, 2, dataDirs.size()); - - + + } - + /** * Test where both arrays are null. * @throws Exception @@ -534,7 +540,7 @@ public void testBothNull() throws Exception { /** * Test where first array is null. - * + * * @throws Exception */ public void testCopyEmptyOneNull() throws Exception { @@ -554,7 +560,7 @@ public void testCopyEmptyOneNull() throws Exception { /** * Test where second array is null. - * + * * @throws Exception */ public void testCopyEmptyTwoNull() throws Exception { @@ -586,7 +592,7 @@ public void testCopyEmptyTwo() throws Exception { /** * Test copy non-empty arrays. - * + * * @throws Exception */ public void testCopyArrays() throws Exception { @@ -621,26 +627,26 @@ public void testCopy() throws Exception { } - - + + public void testLocatingSampleFiles() throws Exception { IInternalContest contest = loadFullSampleContest(null, "mini"); assertNotNull(contest); - + ContestInformation info = contest.getContestInformation(); - + String judgeCDP = info.getJudgeCDPBasePath(); - + Problem[] problems = contest.getProblems(); - + int totFiles = 0; for (Problem problem : problems) { totFiles += problem.getNumberTestCases(); } - + assertEquals("Expecting total data and sample files",10, totFiles); - + for (Problem problem : problems) { ProblemDataFiles problemDataFiles = contest.getProblemDataFile(problem); @@ -653,7 +659,7 @@ public void testLocatingSampleFiles() throws Exception { } } } - + /** * Test whether fullJudgesDataFilenames finds data files, esp. samples. */ diff --git a/test/edu/csus/ecs/pc2/core/execute/JudgementUtilitiesTest.java b/test/edu/csus/ecs/pc2/core/execute/JudgementUtilitiesTest.java index f7a87b4e7..99a84be4b 100644 --- a/test/edu/csus/ecs/pc2/core/execute/JudgementUtilitiesTest.java +++ b/test/edu/csus/ecs/pc2/core/execute/JudgementUtilitiesTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.execute; import java.util.List; @@ -19,13 +19,20 @@ /** * Unit test. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class JudgementUtilitiesTest extends AbstractTestCase { - + private SampleContest sample = new SampleContest(); -// + + @Override + protected void setUp() throws Exception { + ensureStaticLog(); + super.setUp(); + } + + // public String getDefaultJudgementAcronym(IInternalContest contest){ // return contest.getJudgements()[1].getAcronym(); // CE return contest.getJudgements()[2].getAcronym(); // WA @@ -33,75 +40,75 @@ public String getDefaultJudgementAcronym(IInternalContest contest){ /** * Test compile error. - * + * * @throws Exception */ public void testForCE() throws Exception { IInternalContest contest = createContest(); - + ExecutionData executionData = new ExecutionData(); - + Run[] runs = sample.createRandomRuns(contest, 12, true, true, true); Run run = runs[0]; - + /** * Test Compilation error */ executionData.setCompileSuccess(false); JudgementRecord judgementRecord = JudgementUtilities.createJudgementRecord(contest, run, executionData, "Works for me"); - + Judgement judgmeent = contest.getJudgement(judgementRecord.getJudgementId()); assertEquals(Judgement.ACRONYM_COMPILATION_ERROR, judgmeent.getAcronym()); } - + /** * Test for execute. - * + * * An exception is thrown during execute. - * + * * @throws Exception */ public void testForExec() throws Exception { IInternalContest contest = createContest(); - + ExecutionData executionData = new ExecutionData(); - + Run[] runs = sample.createRandomRuns(contest, 12, true, true, true); Run run = runs[0]; - + /** * Test Execution Error */ executionData.setCompileSuccess(true); - + executionData.setExecutionException(new Exception("unit test M1")); - + JudgementRecord judgementRecord = JudgementUtilities.createJudgementRecord(contest, run, executionData, "Works for me"); - + Judgement judgmeent = contest.getJudgement(judgementRecord.getJudgementId()); - + assertEquals(getDefaultJudgementAcronym(contest), judgmeent.getAcronym()); - + String expectedMessage = "Execption during execution unit test M1"; assertEquals(expectedMessage, judgementRecord.getValidatorResultString()); } - + /** * Test for validator. - * + * * Judgement matches judgement in list. - * + * * @throws Exception */ public void testforValidatePositive() throws Exception { IInternalContest contest = createContest(); - + ExecutionData executionData = new ExecutionData(); - + Run[] runs = sample.createRandomRuns(contest, 12, true, true, true); Run run = runs[0]; - + /** * Test compile ok, exec and validate not so much.. */ @@ -111,30 +118,30 @@ public void testforValidatePositive() throws Exception { Judgement judgement = contest.getJudgements()[4]; String expected = judgement.getDisplayName(); executionData.setValidationResults(expected); - + JudgementRecord judgementRecord = JudgementUtilities.createJudgementRecord(contest, run, executionData, executionData.getValidationResults()); Judgement judgmeent = contest.getJudgement(judgementRecord.getJudgementId()); assertEquals("WA2", judgmeent.getAcronym()); - + String expectedMessage = "You have no clue"; // this is the judgement! assertEquals(expectedMessage, judgementRecord.getValidatorResultString()); } - + /** * Test for validate. - * + * * Where judgement does not match any contest judgement name. * @throws Exception */ public void testforValidateNegative() throws Exception { - + IInternalContest contest = createContest(); - + ExecutionData executionData = new ExecutionData(); - + Run[] runs = sample.createRandomRuns(contest, 12, true, true, true); Run run = runs[0]; - + /** * Test compile ok, exec and validate not so much.. */ @@ -144,62 +151,62 @@ public void testforValidateNegative() throws Exception { Judgement judgement = contest.getJudgements()[4]; String expected = judgement.getDisplayName(); executionData.setValidationResults(expected); - + JudgementRecord judgementRecord = JudgementUtilities.createJudgementRecord(contest, run, executionData, "It's alright"); - + Judgement judgmeent = contest.getJudgement(judgementRecord.getJudgementId()); assertEquals(getDefaultJudgementAcronym(contest), judgmeent.getAcronym()); - + String expectedMessage = "Undetermined"; assertEquals(expectedMessage, judgementRecord.getValidatorResultString()); } - + /** * Test when validate nor execute was successful. - * + * * @throws Exception */ public void testForNoExecuteNoValidate() throws Exception { - + IInternalContest contest = createContest(); - + ExecutionData executionData = new ExecutionData(); - + Run[] runs = sample.createRandomRuns(contest, 12, true, true, true); Run run = runs[0]; - + /** * Test compile ok, exec and validate not so much.. */ executionData.setCompileSuccess(true); executionData.setExecuteSucess(false);; executionData.setValidationSuccess(false); - + JudgementRecord judgementRecord = JudgementUtilities.createJudgementRecord(contest, run, executionData, "Works for me"); - + Judgement judgmeent = contest.getJudgement(judgementRecord.getJudgementId()); assertEquals(getDefaultJudgementAcronym(contest), judgmeent.getAcronym()); - + String expectedMessage = "Undetermined"; assertEquals(expectedMessage, judgementRecord.getValidatorResultString()); } - + /** * Test when judgement is Yes/solved. - * + * * @throws Exception */ public void testYes() throws Exception { - - + + IInternalContest contest = createContest(); - + ExecutionData executionData = new ExecutionData(); - + Run[] runs = sample.createRandomRuns(contest, 12, true, true, true); Run run = runs[0]; - + /** * Test compile ok, exec and validate not so much.. */ @@ -208,75 +215,75 @@ public void testYes() throws Exception { executionData.setValidationSuccess(true); Judgement judgement22 = contest.getJudgements()[0]; executionData.setValidationResults("accepted"); - + JudgementRecord judgementRecord = JudgementUtilities.createJudgementRecord(contest, run, executionData, "accepted"); - + Judgement newJudgment = contest.getJudgement(judgementRecord.getJudgementId()); - + String expected = judgement22.getAcronym(); String actual = newJudgment.getAcronym(); - + assertEquals(expected, actual); - + String expectedMessage = "Yes."; assertEquals(expectedMessage, judgementRecord.getValidatorResultString()); } - + private IInternalContest createContest() { IInternalContest contest = sample.createStandardContest(); return contest; } - - + + public void testgetLastTestCaseJudgementList() throws Exception { IInternalContest contest = createContest(); - + Run[] runs = sample.createRandomRuns(contest, 12, true, true, true); Run run = runs[0]; - + Judgement noJudgement = contest.getJudgements()[4]; - + Problem problem = contest.getProblem(run.getProblemId()); - + for (int i = 0; i < 8; i++) { problem.addTestCaseFilenames("sumit.dat", "sumit.ans"); } - + assertTrue("Expecting more than four test cases, got "+problem.getNumberTestCases(), problem.getNumberTestCases() > 4); - + // Add all Yes judgements addAllTestCases(run, problem, sample.getYesJudgement(contest)); - + /** * Test case, base 1, to assign a NO judgement to. */ - + // add no judgement at test case 2 addTestCases(run, problem, 2, noJudgement, sample.getYesJudgement(contest)); - + int noTestCaseNumber = 4; // Add all yes, except a No at noTestCaseNumber addTestCases(run, problem, noTestCaseNumber, noJudgement, sample.getYesJudgement(contest)); List jList = JudgementUtilities.getLastTestCaseJudgementList(contest, run); assertNotNull (jList); - + int tcn = problem.getNumberTestCases(); assertEquals("test cases expected ", tcn, jList.size()); - + Judgement expectedNo = jList.get(noTestCaseNumber-1); assertEquals(noJudgement, expectedNo); - + int yesCount = 0; for (Judgement judgement : jList) { if (Judgement.ACRONYM_ACCEPTED.equals(judgement.getAcronym())){ yesCount++; } } - + assertEquals("Expected yes judgement count ", 7, yesCount); } @@ -287,7 +294,7 @@ public void testgetLastTestCaseJudgementList() throws Exception { * @param judgement */ private void addAllTestCases(Run run, Problem problem, Judgement judgement) { - + for (int i = 0; i < problem.getNumberTestCases(); i++) { boolean passed = Judgement.ACRONYM_ACCEPTED.equals(judgement.getAcronym()); ClientId judgerClientId = new ClientId(1, Type.JUDGE, 1); @@ -295,12 +302,12 @@ private void addAllTestCases(Run run, Problem problem, Judgement judgement) { RunTestCase runTestCaseResult = new RunTestCase(run, record, i, passed); run.addTestCase(runTestCaseResult); } - + } /** * Add judgements to run, add single NO to list of judgements - * + * * @param run * @param problem * @param testCaseNumberForNoJudgement test case number to assign judgement, base 1. @@ -309,13 +316,13 @@ private void addAllTestCases(Run run, Problem problem, Judgement judgement) { */ private void addTestCases(Run run, Problem problem, int testCaseNumberForNoJudgement, Judgement noJudgement, Judgement yesJudgement) { for (int i = 0; i < problem.getNumberTestCases(); i++) { - + Judgement judgement = yesJudgement; - + if (i + 1 == testCaseNumberForNoJudgement){ judgement = noJudgement; } - + boolean passed = Judgement.ACRONYM_ACCEPTED.equals(judgement.getAcronym()); ClientId judgerClientId = new ClientId(1, Type.JUDGE, 1); JudgementRecord record = new JudgementRecord(judgement.getElementId(), judgerClientId, passed, true); @@ -326,9 +333,9 @@ private void addTestCases(Run run, Problem problem, int testCaseNumberForNoJudge /** * Test with sample contest/default judgements. - * + * * By default will use the judgements from the current site (site number 3) - * + * * @throws Exception */ public void testgetSingleListofJudgements() throws Exception { @@ -351,9 +358,9 @@ public void testgetSingleListofJudgements() throws Exception { /** * Test to ensure that all judgements found are on site 1. - * + * * When there are judgements from site 1, use those judgements. - * + * * @throws Exception */ public void testgetSingleListofJudgementsWithSite1Judgements() throws Exception { @@ -426,7 +433,7 @@ public void testgetLastTestCaseArray() throws Exception { addRunTestCase(contest, run, testCaseNum, acJudgement, judges[0].getClientId()); } recs = JudgementUtilities.getLastTestCaseArray(contest, run); - + assertEquals("Expected test cases ", 10, recs.length); // Add second set of test cases - all WA for (int testCaseNum = 1; testCaseNum <= problem.getNumberTestCases(); testCaseNum++) { @@ -434,9 +441,9 @@ public void testgetLastTestCaseArray() throws Exception { } assertEquals("Expected total test cases ", 20, run.getRunTestCases().length); recs = JudgementUtilities.getLastTestCaseArray(contest, run); - + assertEquals("Expected test cases ", 10, recs.length); - + // Test that all judgements are WA for (RunTestCase runTestCase : recs) { ElementId judgementId = runTestCase.getJudgementId(); @@ -485,5 +492,5 @@ private void addRunTestCase(IInternalContest contest, Run run, int testNumber, J run.addTestCase(runTestCase); } - + } diff --git a/test/edu/csus/ecs/pc2/core/export/ExportYAMLTest.java b/test/edu/csus/ecs/pc2/core/export/ExportYAMLTest.java index 01631e556..56a8b4fb6 100644 --- a/test/edu/csus/ecs/pc2/core/export/ExportYAMLTest.java +++ b/test/edu/csus/ecs/pc2/core/export/ExportYAMLTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.export; import java.io.File; @@ -30,6 +30,7 @@ public class ExportYAMLTest extends AbstractTestCase { @Override protected void setUp() throws Exception { + ensureStaticLog(); super.setUp(); } diff --git a/test/edu/csus/ecs/pc2/core/imports/LoadICPCDataTest.java b/test/edu/csus/ecs/pc2/core/imports/LoadICPCDataTest.java index 88457f631..0d3faf80e 100644 --- a/test/edu/csus/ecs/pc2/core/imports/LoadICPCDataTest.java +++ b/test/edu/csus/ecs/pc2/core/imports/LoadICPCDataTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.imports; import java.io.File; @@ -10,15 +10,14 @@ import edu.csus.ecs.pc2.core.model.ClientType; import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.Site; +import edu.csus.ecs.pc2.core.util.AbstractTestCase; import edu.csus.ecs.pc2.core.util.JUnitUtilities; -import junit.framework.TestCase; - /** * @author PC2 * */ -public class LoadICPCDataTest extends TestCase { +public class LoadICPCDataTest extends AbstractTestCase { private String loadDir = "testdata"+File.separator; private Site[] sites = new Site[2]; @@ -31,6 +30,7 @@ public LoadICPCDataTest() { public LoadICPCDataTest(String arg0) { super(arg0); } + @Override protected void setUp() throws Exception { String projectPath=JUnitUtilities.locate(loadDir); if (projectPath == null) { @@ -47,6 +47,8 @@ protected void setUp() throws Exception { sites[1] = new Site("NORTH", 1); accountList.generateNewAccounts(ClientType.Type.TEAM, 45 ,PasswordType.JOE, 1, true); accountList.generateNewAccounts(ClientType.Type.TEAM, 45 ,PasswordType.JOE, 2, true); + ensureStaticLog(); + super.setUp(); } public void testOne() { @@ -92,7 +94,7 @@ public void testTwo() { break; } } - + } catch (Exception e) { e.printStackTrace(); assertTrue("exception", false); diff --git a/test/edu/csus/ecs/pc2/core/imports/LoadICPCTSVDataTest.java b/test/edu/csus/ecs/pc2/core/imports/LoadICPCTSVDataTest.java index f712813cb..f0cbc3186 100644 --- a/test/edu/csus/ecs/pc2/core/imports/LoadICPCTSVDataTest.java +++ b/test/edu/csus/ecs/pc2/core/imports/LoadICPCTSVDataTest.java @@ -27,6 +27,11 @@ // $HeadURL$ public class LoadICPCTSVDataTest extends AbstractTestCase { + @Override + protected void setUp() throws Exception { + ensureStaticLog(); + super.setUp(); + } public void testCheckFiles() throws Exception { LoadICPCTSVData load = new LoadICPCTSVData(); diff --git a/test/edu/csus/ecs/pc2/exports/ccs/ResultsFileTest.java b/test/edu/csus/ecs/pc2/exports/ccs/ResultsFileTest.java index 67701f66e..96cdd8502 100644 --- a/test/edu/csus/ecs/pc2/exports/ccs/ResultsFileTest.java +++ b/test/edu/csus/ecs/pc2/exports/ccs/ResultsFileTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.exports.ccs; import java.io.File; @@ -15,7 +15,7 @@ /** * Test ResultsFile class. - * + * * @author pc2@ecs.csus.edu * @version $Id: ResultsFileTest.java 193 2011-05-14 05:02:16Z laned $ */ @@ -25,9 +25,15 @@ public class ResultsFileTest extends AbstractTestCase { private final boolean debugMode = false; + @Override + protected void setUp() throws Exception { + ensureStaticLog(); + super.setUp(); + } + /** * Sample Finalize Data. - * + * * @param rank * @return */ @@ -57,33 +63,33 @@ private FinalizeData createSampFinalData(int rank) { /** * Test for results file with no runs. - * + * * @throws Exception */ public void testcreateTSVFileLinesEmpty() throws Exception { - + // If everybody solved 0 problems then they all should be Honorable String[] expectedResults = { // - "2020;;Honorable;0", // results - "2021;;Honorable;0", // results - "2022;;Honorable;0", // results - "2023;;Honorable;0", // results - "2024;;Honorable;0", // results - "2025;;Honorable;0", // results - "2026;;Honorable;0", // results - "2027;;Honorable;0", // results - "2028;;Honorable;0", // results - "2029;;Honorable;0", // results - "2030;;Honorable;0", // results - "2031;;Honorable;0", // results - "2032;;Honorable;0", // results - "2033;;Honorable;0", // results - "2034;;Honorable;0", // results - "2035;;Honorable;0", // results - "2036;;Honorable;0", // results - "2037;;Honorable;0", // results - "2038;;Honorable;0", // results - "2039;;Honorable;0", // results + "2020;;Honorable;0", // results + "2021;;Honorable;0", // results + "2022;;Honorable;0", // results + "2023;;Honorable;0", // results + "2024;;Honorable;0", // results + "2025;;Honorable;0", // results + "2026;;Honorable;0", // results + "2027;;Honorable;0", // results + "2028;;Honorable;0", // results + "2029;;Honorable;0", // results + "2030;;Honorable;0", // results + "2031;;Honorable;0", // results + "2032;;Honorable;0", // results + "2033;;Honorable;0", // results + "2034;;Honorable;0", // results + "2035;;Honorable;0", // results + "2036;;Honorable;0", // results + "2037;;Honorable;0", // results + "2038;;Honorable;0", // results + "2039;;Honorable;0", // results }; ResultsFile resultsFile = new ResultsFile(); @@ -96,25 +102,25 @@ public void testcreateTSVFileLinesEmpty() throws Exception { IInternalContest contest = sample.createContest(1, 1, numTeams, 12, true); SampleContest.assignReservationIds(contest, 2020); - + contest.setFinalizeData(finalizeData); String[] results = resultsFile.createTSVFileLines(contest); // using getStandingsRecords // String[] results = resultsFile.createTSVFileLinesTwo(contest); // using XML - + assertEquals("Number results file lines ", numTeams + 1, results.length); compareResults(results, expectedResults); } - + /** * Bug 1156 - tests places - * + * * Each team has its proper place (not individual rankings). */ public void testPlaces() throws Exception { - + String[] runsData = SampleContest.loadStringArrayFromCSV(getDataDirectory()+File.separator+"run_5_field.txt"); @@ -137,17 +143,17 @@ public void testPlaces() throws Exception { if (judgements.length == 0) { // copied from InternalController.loadDefaultJudgements String[] judgementNames = { // - "Yes", // - "No - Compilation Error", // - "No - Run-time Error", // - "No - Time Limit Exceeded", // - "No - Wrong Answer", // - "No - Excessive Output", // - "No - Output Format Error", // + "Yes", // + "No - Compilation Error", // + "No - Run-time Error", // + "No - Time Limit Exceeded", // + "No - Wrong Answer", // + "No - Excessive Output", // + "No - Output Format Error", // "No - Other - Contact Staff" // }; String [] judgementAcronyms = { - Judgement.ACRONYM_ACCEPTED, // + Judgement.ACRONYM_ACCEPTED, // Judgement.ACRONYM_COMPILATION_ERROR, // Judgement.ACRONYM_RUN_TIME_ERROR, // Judgement.ACRONYM_TIME_LIMIT_EXCEEDED, // @@ -156,7 +162,7 @@ public void testPlaces() throws Exception { Judgement.ACRONYM_OUTPUT_FORMAT_ERROR, // Judgement.ACRONYM_OTHER_CONTACT_STAFF, // }; - + int i = 0; for (String judgementName : judgementNames) { Judgement judgement = new Judgement(judgementName, judgementAcronyms[i]); @@ -166,7 +172,7 @@ public void testPlaces() throws Exception { } addRuns(contest, runsData); - + contest.setFinalizeData(finalizeData); ClientId clientId = new ClientId(1, Type.SCOREBOARD, 1); contest.setClientId(clientId); @@ -177,15 +183,15 @@ public void testPlaces() throws Exception { // printExpectedTestData(results); } - + compareResults(results, expectedResults); - - + + } /** * Test with 27 or more runs. - * + * * @throws Exception */ public void testcreateTSVFileLinesComplex() throws Exception { @@ -228,26 +234,26 @@ public void testcreateTSVFileLinesComplex() throws Exception { // for medal ranks 4, 8, 12 String[] expectedResults = { // - "2024;1;Gold Medal;2", // results - "2031;2;Gold Medal;2", // results - "2026;3;Gold Medal;2", // results - "2038;4;Gold Medal;2", // results - "2029;5;Silver Medal;1", // results - "2020;6;Silver Medal;1", // results - "2030;7;Silver Medal;1", // results - "2021;8;Silver Medal;1", // results - "2032;9;Bronze Medal;1", // results - "2033;10;Bronze Medal;1", // results - "2034;11;Bronze Medal;1", // results - "2022;12;Bronze Medal;1", // results - "2023;13;Ranked;1", // results - "2025;;Honorable;0", // results - "2027;;Honorable;0", // results - "2028;;Honorable;0", // results - "2035;;Honorable;0", // results - "2036;;Honorable;0", // results - "2037;;Honorable;0", // results - "2039;;Honorable;0", // results + "2024;1;Gold Medal;2", // results + "2031;2;Gold Medal;2", // results + "2026;3;Gold Medal;2", // results + "2038;4;Gold Medal;2", // results + "2029;5;Silver Medal;1", // results + "2020;6;Silver Medal;1", // results + "2030;7;Silver Medal;1", // results + "2021;8;Silver Medal;1", // results + "2032;9;Bronze Medal;1", // results + "2033;10;Bronze Medal;1", // results + "2034;11;Bronze Medal;1", // results + "2022;12;Bronze Medal;1", // results + "2023;13;Ranked;1", // results + "2025;;Honorable;0", // results + "2027;;Honorable;0", // results + "2028;;Honorable;0", // results + "2035;;Honorable;0", // results + "2036;;Honorable;0", // results + "2037;;Honorable;0", // results + "2039;;Honorable;0", // results }; // for medal ranks 3, 6, 10 @@ -288,7 +294,7 @@ public void testcreateTSVFileLinesComplex() throws Exception { SampleContest.assignReservationIds(contest, 2020); addRuns(contest, runsData); - + contest.setFinalizeData(finalizeData); String[] results = resultsFile.createTSVFileLines(contest); // using getStandingsRecords @@ -302,7 +308,7 @@ public void testcreateTSVFileLinesComplex() throws Exception { compareResults(results, expectedResults); } - + @SuppressWarnings("unused") private void printExpectedTestData(String[] expected) { @@ -327,7 +333,7 @@ private void dumpStringArray(String[] sa) { /** * Compare results file info to expected info. - * + * * @param results * @param expectedResults */ diff --git a/test/edu/csus/ecs/pc2/imports/ccs/ContestImportUtilitiesTest.java b/test/edu/csus/ecs/pc2/imports/ccs/ContestImportUtilitiesTest.java new file mode 100644 index 000000000..570ac73e0 --- /dev/null +++ b/test/edu/csus/ecs/pc2/imports/ccs/ContestImportUtilitiesTest.java @@ -0,0 +1,58 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.imports.ccs; + +import java.io.File; +import java.util.ArrayList; + +import edu.csus.ecs.pc2.core.exception.YamlLoadException; +import edu.csus.ecs.pc2.core.model.SampleContest; +import edu.csus.ecs.pc2.core.util.AbstractTestCase; +import edu.csus.ecs.pc2.core.util.JUnitUtilities; + +/** + * Test the Contest Import Utilities + * + * @author John Buck + * + */ +public class ContestImportUtilitiesTest extends AbstractTestCase { + private String loadDir = "testdata" + File.separator; + + protected void setUp() throws Exception { + String projectPath = JUnitUtilities.locate(loadDir); + if (projectPath == null) { + throw new Exception("Unable to locate " + loadDir); + } + File dir = new File(projectPath + File.separator + loadDir); + if (dir.exists()) { + loadDir = dir.toString() + File.separator; + } else { + System.err.println("could not find " + loadDir); + throw new Exception("Unable to locate " + loadDir); + } + super.setUp(); + } + + /** + * Tests whether parsing of the testdata.yaml files work + * + * @throws Exception + */ + public void testgetTestCaseFileNames() throws Exception { + String inputTestDirectory = getDataDirectory(this.getName()) + File.separator; + + ArrayList secret0 = ContestImportUtilities.getTestCaseFileNames(inputTestDirectory); + assertEquals("Expecting judge datafiles ", 0, secret0.size()); + + ArrayList secret1 = ContestImportUtilities.getTestCaseFileNames(inputTestDirectory + "secret1"); + assertEquals("Expecting judge datafiles for secret1 ", 34, secret1.size()); + + try { + ArrayList secret2 = ContestImportUtilities.getTestCaseFileNames(inputTestDirectory + "secret2"); + failTest("Expecting missing answer file, but none are missing for secret2"); + } catch (YamlLoadException e) { + assertEquals("Expecting missing answer ", "Missing answer file 02-loop-jump.ans for input file 02-loop-jump.in", e.getMessage()); + } + } + +} diff --git a/test/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoaderTest.java b/test/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoaderTest.java index fd090f2a5..80614cc5f 100644 --- a/test/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoaderTest.java +++ b/test/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoaderTest.java @@ -74,7 +74,7 @@ public class ContestSnakeYAMLLoaderTest extends AbstractTestCase { // generica contest loader private IContestLoader loader = new ContestSnakeYAMLLoader(); - + // specific snake loader private ContestSnakeYAMLLoader snake = new ContestSnakeYAMLLoader(); @@ -88,6 +88,7 @@ public ContestSnakeYAMLLoaderTest(String string) { @Override protected void setUp() throws Exception { + ensureStaticLog(); super.setUp(); // setDebugMode(true); // debug mode @@ -129,7 +130,7 @@ private void writeRow(PrintStream printWriter, Language language) { public void testLoaderMethods() throws Exception { String yamlFilename= getTestFilename("contest.jt.yaml"); - + // editFile(yamlFilename); String[] contents = Utilities.loadFile(yamlFilename); @@ -300,10 +301,10 @@ public void testDefaultJudgingTypes() throws Exception { IInternalContest contest = loader.fromYaml(null, lines, getDataDirectory()); assertNotNull(contest); - + // assertNull("Judge's config path ",contest.getContestInformation().getJudgeCDPBasePath()); assertEquals("Judge's config path ",getDataDirectory(), contest.getContestInformation().getJudgeCDPBasePath()); - + Problem[] problems = contest.getProblems(); @@ -354,7 +355,7 @@ public void testJudgementTypesinProblemYaml() throws Exception { assertJudgementTypes(problem2, false, true, true); } - + /** @@ -428,7 +429,7 @@ public void testLoader() throws Exception { * Test the start number for site 3 starts at 300. */ Vector site3teams = contest.getAccounts(Type.TEAM, 3); - Account[] account3 = (Account[]) site3teams.toArray(new Account[site3teams.size()]); + Account[] account3 = site3teams.toArray(new Account[site3teams.size()]); for (Account account : account3) { assertTrue("Expecting team numbers above 299 on site 3", account.getClientId().getClientNumber() > 299); } @@ -439,7 +440,7 @@ public void testLoader() throws Exception { assertEquals("Judge's config path ",getDataDirectory(), contest.getContestInformation().getJudgeCDPBasePath()); // assertNull("Judge's config path ",contest.getContestInformation().getJudgeCDPBasePath()); - + Problem[] problems = contest.getProblems(); assertEquals("Number of problems", 5, problems.length); @@ -470,7 +471,7 @@ public void testLoader() throws Exception { } /** - * + * * @param date * @return empty string if date is null, other wise */ @@ -559,7 +560,7 @@ public void testLoaderDoubleQuotedStrings() throws Exception { * Test the start number for site 3 starts at 300. */ Vector site3teams = contest.getAccounts(Type.TEAM, 3); - Account[] account3 = (Account[]) site3teams.toArray(new Account[site3teams.size()]); + Account[] account3 = site3teams.toArray(new Account[site3teams.size()]); for (Account account : account3) { assertTrue("Expecting team numbers above 299 on site 3", account.getClientId().getClientNumber() > 299); } @@ -567,7 +568,7 @@ public void testLoaderDoubleQuotedStrings() throws Exception { assertEquals("Number of accounts", 85, accounts.length); checkPermissions(accounts); - + assertEquals("Judge's config path ",getDataDirectory(), contest.getContestInformation().getJudgeCDPBasePath()); // assertNull("Judge's config path ",contest.getContestInformation().getJudgeCDPBasePath()); @@ -676,7 +677,7 @@ public void testProblemLoader() throws Exception { // startExplorer(getDataDirectory()); // editFile(getDataDirectory()+"/"+IContestLoader.DEFAULT_CONTEST_YAML_FILENAME); - + assertEquals("Judge's config path ",getDataDirectory(), contest.getContestInformation().getJudgeCDPBasePath()); // assertNull("Judge's config path ",contest.getContestInformation().getJudgeCDPBasePath()); @@ -734,11 +735,11 @@ public void testProblemLoader() throws Exception { // editFile(getDataDirectory() + "/" + firstProblem.getShortName() + "/" + IContestLoader.DEFAULT_PROBLEM_YAML_FILENAME); assertTrue ("Expecting problem "+firstProblem.getShortName()+" set stopOnFirstFailedTestCase", firstProblem.isStopOnFirstFailedTestCase()); - + assertFalse ("Expecting problem "+problems[3].getShortName()+" set stopOnFirstFailedTestCase", problems[3].isStopOnFirstFailedTestCase()); String[] basenames = { "bozo", "smart", "sumit" }; - + assertEquals("Judge's config path ",getDataDirectory(), contest.getContestInformation().getJudgeCDPBasePath()); // assertNull("Judge's config path ",contest.getContestInformation().getJudgeCDPBasePath()); @@ -811,33 +812,33 @@ public void testLoadLanguages() throws Exception { } } - + public void testCCSLanguageLoad() throws Exception { - + String sampleContestDirName = "ccs1"; String dirname = getContestSampleCDPConfigDirname(sampleContestDirName); - + IInternalContest contest = snake.fromYaml(null, dirname, false); // from ccs 1: // languages: // - name: C++ -// compiler: /usr/bin/g++ -// compiler-args: -O2 -Wall -o a.out -static {files} +// compiler: /usr/bin/g++ +// compiler-args: -O2 -Wall -o a.out -static {files} // // - name: C // compiler: /usr/bin/gcc // compiler-args: -O2 -Wall -std=gnu99 -o a.out -static {files} -lm -// +// // - name: Java // compiler: /usr/bin/javac // compiler-args: -O {files} // runner: /usr/bin/java // runner-args: - + Language[] languages = contest.getLanguages(); assertEquals("Expected 3 languages", 3, languages.length); - + for (Language language : languages) { switch (language.getDisplayName()){ case "Java": @@ -929,7 +930,7 @@ public void testgetProblemsFromLetters() throws Exception { SampleContest sample = new SampleContest(); IInternalContest contest = sample.createContest(1, 1, 12, 22, true); - + assertNull("Judge's config path ",contest.getContestInformation().getJudgeCDPBasePath()); Problem[] contestProblems = contest.getProblems(); @@ -1109,18 +1110,18 @@ public void testYamlLoad() throws IOException { ensureDirectory(getDataDirectory(this.getName())); // startExplorer(getDataDirectory(this.getName())); - - Map map = snake.loadYaml(contestYamlFilename); + + Map map = ContestImportUtilities.loadYaml(contestYamlFilename); // System.out.println("object is type "+out.getClass().getName()); assertNotNull("Expecting loaded map", map); Set set = map.keySet(); - String[] list = (String[]) set.toArray(new String[set.size()]); + String[] list = set.toArray(new String[set.size()]); assertTrue("Expecting more than one element ", list.length > 9); - + } public void testReplayLoad() throws Exception { @@ -1204,9 +1205,9 @@ public void testdoNotLoadExternalFile() throws Exception { // Load data files try { - + contest = loader.fromYaml(null, dirname, true); - + } catch (YamlLoadException e) { // System.out.println("failed loading in file "+e.getFilename()); // editFile(e.getFilename()); @@ -1220,11 +1221,11 @@ public void testdoNotLoadExternalFile() throws Exception { assertEquals("Expecting loaded answer files ", 12, dataFiles.getJudgesAnswerFiles().length); assertEquals("Expecting loaded data files ", 12, dataFiles.getJudgesDataFiles().length); SerializedFile[] ansfiles = dataFiles.getJudgesAnswerFiles(); - + for (SerializedFile serializedFile : ansfiles) { assertTrue("Expecting Loaded filee", serializedFile.getBuffer().length != 0); } - + } // Do not load data files @@ -1238,9 +1239,9 @@ public void testdoNotLoadExternalFile() throws Exception { for (SerializedFile serializedFile : ansfiles) { assertTrue("Expecting external answer file ", serializedFile.getBuffer().length == 0); } - + } - + assertNoAutoStart(contest); } @@ -1288,7 +1289,7 @@ public void testValidatorKeys() throws Exception { assertEquals("Problem letter ", letterList[idx], shortName); idx++; } - + assertAutoStart(contest, "2011-02-04 01:23", false); } @@ -1296,7 +1297,7 @@ public void testValidatorKeys() throws Exception { /** * Tests that the input_validator keys "defaultInputValidator", "vivaPattern", customInputValidatorProg", and * "customInputValidatorCmd" correctly assign the values specified in the problem.yaml file to a problem. - * + * * @throws Exception if the value associated with any of the above keys is not properly loaded into the problem. */ public void testInputValidatorKeys() throws Exception { @@ -1311,7 +1312,7 @@ public void testInputValidatorKeys() throws Exception { //check that the "defaultInputValidator" key worked assertEquals("Default custom validator setting: ", INPUT_VALIDATOR_TYPE.CUSTOM, prob.getCurrentInputValidatorType()); - + //check that the "vivaPattern" key worked String expectedPattern = "{x;}"; String [] pattern = prob.getVivaInputValidatorPattern(); @@ -1321,24 +1322,24 @@ public void testInputValidatorKeys() throws Exception { } String actualPattern = sb.toString(); assertEquals("Viva Pattern: ", expectedPattern, actualPattern); - + //check that the "customInputValidatorProg" key worked assertEquals("Custom Input Validator program: ", "SumitInputValidator.class", prob.getCustomInputValidatorProgramName()); //check that the "customInputValidatorCmd" key worked assertEquals("Custom Input Validator command: ", "java {:basename}", prob.getCustomInputValidatorCommandLine()); - + //extra checks: that the Problem Name and Letter got assigned correctly assertEquals("Problem letter: ", "S", prob.getLetter()); assertEquals("Problem name: ", "sumit", prob.getShortName()); - + } - + /** * Tests that if no "customInputValidatorProg" key is present in the problem.yaml file, but there is an "input_format_validators" - * folder in the problem description and it contains an Input Validator file, that a custom input validator is loaded from the + * folder in the problem description and it contains an Input Validator file, that a custom input validator is loaded from the * "input_format_validators" folder. - * + * * @throws Exception if the input_validator specifications in the problem.yaml file are not properly loaded into the problem. */ public void testLoadInputValidatorFromInputFormatValidatorsFolder() throws Exception { @@ -1353,23 +1354,23 @@ public void testLoadInputValidatorFromInputFormatValidatorsFolder() throws Excep //check that the "defaultInputValidator" key worked assertEquals("Default custom validator setting: ", INPUT_VALIDATOR_TYPE.CUSTOM, prob.getCurrentInputValidatorType()); - + //check that no "vivaPattern" has been set assertFalse("Problem has Viva Pattern: ", prob.isProblemHasVivaInputValidatorPattern()); - + //check that the "valid.bat" file in the "input_format_validators" folder was loaded assertEquals("Custom Input Validator program: ", "valid.bat", prob.getCustomInputValidatorProgramName()); - + //extra checks: that the Problem Name and Letter got assigned correctly assertEquals("Problem letter: ", "S", prob.getLetter()); assertEquals("Problem name: ", "sumit", prob.getShortName()); - + } - + /** * Tests that if a "defaultInputValidator" key is present in the problem.yaml file and specifies "NONE", * the problem gets assigned IV type "NONE" even if there is a Viva pattern defined. - * + * * @throws Exception if the input_validator specifications in the problem.yaml file are not properly loaded into the problem. */ public void testLoadDefaultInputValidatorTypeNONEOverridesVivaPattern() throws Exception { @@ -1391,16 +1392,16 @@ public void testLoadDefaultInputValidatorTypeNONEOverridesVivaPattern() throws E } String actualPattern = sb.toString(); assertEquals("Problem Viva Pattern: ", expectedPattern, actualPattern); - + //check that the "defaultInputValidator" key worked assertEquals("Input Validator type: ", INPUT_VALIDATOR_TYPE.NONE, prob.getCurrentInputValidatorType()); - + } - + /** * Tests that if a "defaultInputValidator" key is present in the problem.yaml file and specifies "NONE", * the problem gets assigned IV type "NONE" even if there is a Custom Input Validator defined. - * + * * @throws Exception if the input_validator specifications in the problem.yaml file are not properly loaded into the problem. */ public void testLoadDefaultInputValidatorTypeNONEOverridesCustomIV() throws Exception { @@ -1414,15 +1415,15 @@ public void testLoadDefaultInputValidatorTypeNONEOverridesCustomIV() throws Exce Problem prob = problems[0]; assertEquals("Problem Custom Input Validator: ", "valid.bat", prob.getCustomInputValidatorProgramName()); - + //check that the "defaultInputValidator" key worked assertEquals("Input Validator type: ", INPUT_VALIDATOR_TYPE.NONE, prob.getCurrentInputValidatorType()); } - + /** * Tests that if a "defaultInputValidator" key is present in the problem.yaml file and specifies "NONE", * the problem gets assigned IV type "NONE" even if there is a Custom Input Validator and a Viva Pattern defined. - * + * * @throws Exception if the input_validator specifications in the problem.yaml file are not properly loaded into the problem. */ public void testLoadDefaultInputValidatorTypeNONEOverridesVivaAndCustomIV() throws Exception { @@ -1444,17 +1445,17 @@ public void testLoadDefaultInputValidatorTypeNONEOverridesVivaAndCustomIV() thro } String actualPattern = sb.toString(); assertEquals("Problem Viva Pattern: ", expectedPattern, actualPattern); - + assertEquals("Problem Custom Input Validator: ", "valid.bat", prob.getCustomInputValidatorProgramName()); - + //check that the "defaultInputValidator" key worked assertEquals("Input Validator type: ", INPUT_VALIDATOR_TYPE.NONE, prob.getCurrentInputValidatorType()); } - + /** * Tests that if a "defaultInputValidator" key is present in the problem.yaml file and specifies "VIVA", * the problem gets assigned IV type "VIVA" even if there is a Custom Input Validator defined. - * + * * @throws Exception if the input_validator specifications in the problem.yaml file are not properly loaded into the problem. */ public void testLoadDefaultInputValidatorTypeVIVAOverridesCustomIV() throws Exception { @@ -1476,17 +1477,17 @@ public void testLoadDefaultInputValidatorTypeVIVAOverridesCustomIV() throws Exce } String actualPattern = sb.toString(); assertEquals("Problem Viva Pattern: ", expectedPattern, actualPattern); - + assertEquals("Problem Custom Input Validator: ", "valid.bat", prob.getCustomInputValidatorProgramName()); - + //check that the "defaultInputValidator" key worked assertEquals("Input Validator type: ", INPUT_VALIDATOR_TYPE.VIVA, prob.getCurrentInputValidatorType()); } - + /** * Tests that if a "defaultInputValidator" key is present in the problem.yaml file and specifies "CUSTOM", * the problem gets assigned IV type "CUSTOM" even if there is a Viva Input Validator Pattern defined. - * + * * @throws Exception if the input_validator specifications in the problem.yaml file are not properly loaded into the problem. */ public void testLoadDefaultInputValidatorTypeCUSTOMOverridesViva() throws Exception { @@ -1508,18 +1509,18 @@ public void testLoadDefaultInputValidatorTypeCUSTOMOverridesViva() throws Except } String actualPattern = sb.toString(); assertEquals("Problem Viva Pattern: ", expectedPattern, actualPattern); - + assertEquals("Problem Custom Input Validator: ", "valid.bat", prob.getCustomInputValidatorProgramName()); - + //check that the "defaultInputValidator" key worked assertEquals("Input Validator type: ", INPUT_VALIDATOR_TYPE.CUSTOM, prob.getCurrentInputValidatorType()); } - + /** - * Tests that the proper Input Validator default settings are set if there is no "input_validator:" section in the problem.yaml + * Tests that the proper Input Validator default settings are set if there is no "input_validator:" section in the problem.yaml * file and also there is no "input_format_validators" folder in the problem. Specifically, this condition should result * in no Viva pattern, no Custom Input Validator, and a default setting of "NONE" for the Input Validator. - * + * * @throws Exception if the correct default Input Validator settings are not loaded into the problem. */ public void testInputValidatorDefaultSettingsWithNoIVSectionAndNoDefaultCustomValidator() throws Exception { @@ -1533,13 +1534,13 @@ public void testInputValidatorDefaultSettingsWithNoIVSectionAndNoDefaultCustomVa Problem prob = problems[0]; assertFalse("Problem has Viva Pattern: ", prob.isProblemHasVivaInputValidatorPattern()); - + assertFalse("Problem has Custom Input Validator: ", prob.isProblemHasCustomInputValidator()); - + //check that the "defaultInputValidator" key worked assertEquals("Input Validator type: ", INPUT_VALIDATOR_TYPE.NONE, prob.getCurrentInputValidatorType()); } - + // SOMEDAY get this JUnit working public void aTestOverRideValidator() throws Exception { @@ -1577,7 +1578,7 @@ public void testMultipleDataSetsCCS() throws Exception { // startExplorer(dirname); // editFile(yamlFileName); - + assertFileExists(yamlFileName); @@ -1586,7 +1587,7 @@ public void testMultipleDataSetsCCS() throws Exception { IInternalContest contest = loader.fromYaml(null, lines, dirname); Problem[] problems = contest.getProblems(); - + assertEquals("Judge's config path ",dirname, contest.getContestInformation().getJudgeCDPBasePath()); // assertNull("Judge's config path ",contest.getContestInformation().getJudgeCDPBasePath()); @@ -1597,7 +1598,7 @@ public void testMultipleDataSetsCCS() throws Exception { loader.loadCCSProblemFiles(contest, secretDataDir, problem, problemDataFiles); assertTrue("Expecting more than one data set in " + secretDataDir, problemDataFiles.getJudgesDataFiles().length > 1); - assertTrue("Expecting more than on data set in " + secretDataDir, problemDataFiles.getJudgesAnswerFiles().length > 1); + assertTrue("Expecting more than one data set in " + secretDataDir, problemDataFiles.getJudgesAnswerFiles().length > 1); } } @@ -1631,7 +1632,7 @@ public void testIncludeFile() throws Exception { // dumpLines ("", lines, true); assertEquals("expected number of lines ", 51, lines.length); - + } /** @@ -1737,7 +1738,7 @@ public void testCCSLoad() throws Exception { // editFile(inputYamlFilename); assertFileExists(inputYamlFilename); - + String[] contents = Utilities.loadFile(getProblemSetYamlTestFileName()); assertFileExists(getProblemSetYamlTestFileName()); @@ -1748,7 +1749,7 @@ public void testCCSLoad() throws Exception { IInternalContest contest = loader.fromYaml(null, contents, getDataDirectory(), true); assertNotNull(contest); - + assertNoAutoStart (contest); Problem[] problems = contest.getProblems(); @@ -1875,34 +1876,34 @@ private void assertNoAutoStart(IInternalContest contest) { /** * Test auto start settings. - * + * * @param contest * @param expectedTimeString - time string in format: "yyyy-MM-dd HH:mm" * @param shouldAutoStart - set true if expecting to auto start contest */ private void assertAutoStart(IInternalContest contest, String expectedTimeString, boolean shouldAutoStart) { - + String scheduledStart = null; Boolean actualShouldAutoStart = new Boolean(false); ContestInformation info = contest.getContestInformation(); if (info != null){ // start-time: 2011-02-04 01:23Z // 2011-02-04 01:23Z -// +// SimpleDateFormat formatter = new SimpleDateFormat(YYYY_MM_DD_FORMAT1); if (info.getScheduledStartDate() != null){ - + scheduledStart = formatter.format(info.getScheduledStartDate()); actualShouldAutoStart = info.isAutoStartContest(); } } - + // System.out.println("Auto start at: "+scheduledStart); // System.out.println("Will auto start: "+actualShouldAutoStart); - + assertEquals("Expecting auto start time ", expectedTimeString, scheduledStart); assertEquals("Expecting auto start ", new Boolean( shouldAutoStart), actualShouldAutoStart); - + } public void testYamlWriteAndLoad() throws Exception { @@ -1921,10 +1922,10 @@ public void testYamlWriteAndLoad() throws Exception { // TODO fix exportFiles to export properly Python spaced YAML. exportYAML.exportFiles(testDirectory, originalContest); - + String filename = testDirectory + File.separator + IContestLoader.DEFAULT_CONTEST_YAML_FILENAME; // editFile(filename); - + validateYamlFile(filename); exportYAML = null; @@ -1962,7 +1963,7 @@ public void testYamlWriteAndLoad() throws Exception { assertEquals("Expected validator command ", Constants.DEFAULT_CLICS_VALIDATOR_COMMAND, problem.getOutputValidatorCommandLine()); } - + String getSnakeParserDetails(MarkedYAMLException markedYAMLException) { // from ContetYamlLoader @@ -1976,12 +1977,12 @@ String getSnakeParserDetails(MarkedYAMLException markedYAMLException) { /** * validates yaml. - * + * * @param filename * @throws YamlLoadException */ private void validateYamlFile(String filename) throws YamlLoadException { - + try { Yaml yaml = new Yaml(); @SuppressWarnings("unchecked") @@ -1992,7 +1993,7 @@ private void validateYamlFile(String filename) throws YamlLoadException { } catch (FileNotFoundException e) { throw new YamlLoadException("File not found " + filename); } - + } public void testUnQuote() throws Exception { @@ -2221,39 +2222,39 @@ private void failIfInDebugMode() { fail(); } } - + /** * Test load sample CCS contest. - * + * * @throws Exception */ public void testCCS1Load() throws Exception { - + String entryLocation = "ccs1"; - + InternalContest contest = new InternalContest(); ensureStaticLog(); loader.initializeContest(contest, new File(entryLocation)); - + // System.out.println("Loaded CDP/config values from " + entryLocation); - + // File cdpConfigDir = loader.findCDPConfigDirectory(new File(entryLocation)); -// String yamlFilename =cdpConfigDir.getAbsolutePath() + File.separator + IContestLoader.DEFAULT_CONTEST_YAML_FILENAME; +// String yamlFilename =cdpConfigDir.getAbsolutePath() + File.separator + IContestLoader.DEFAULT_CONTEST_YAML_FILENAME; // editFile(yamlFilename); // System.out.println("cds config dir = "+cdpConfigDir); - + assertAutoStart(contest, "2060-02-04 01:23", true); - + Language[] languages = contest.getLanguages(); assertEquals("Number of languages", 3, languages.length); - + Account[] accounts = contest.getAccounts(); assertEquals("Number of accounts", 80, accounts.length); Site[] sites = contest.getSites(); assertEquals("Number of sites", 1, sites.length); - + Problem[] problems = contest.getProblems(); assertEquals("Number of problems", 5, problems.length); @@ -2272,17 +2273,17 @@ public void testCCS1Load() throws Exception { assertJudgementTypes(problem, true, false, false); - + } - - + + /** * Test testFindCDPPaths. - * + * * @throws Exception */ public void testFindCDPPaths() throws Exception { - + ContestSnakeYAMLLoader snake = new ContestSnakeYAMLLoader(); String [] dirs = { @@ -2291,38 +2292,38 @@ public void testFindCDPPaths() throws Exception { "ccs2", // "sumithello", // // "valtest", // - + // Doug's Local test directories // "c:\\test\\cdps\\sum1\\config\\contest.yaml", // // "/test/cdps/spring2015/config/contest.yaml", // // "c:\\test\\cdps\\spring2015\\config\\contest.yaml", // // "c:\\test\\cdps\\spring2015", // }; - + for (String name : dirs) { - + File actual = snake.findCDPConfigDirectory(new File(name)); - + if (actual == null){ System.err.println("For "+name+" expected to find file "+snake.getSampleContesYaml(name)); System.err.println("CWD is "+Utilities.getCurrentDirectory()); } - + assertNotNull(actual); assertTrue("Is a config directory? ", actual.isDirectory()); // System.out.println("For "+name+" found "+actual); } } - + /** * Test non-existent CDP directories. - * + * * @throws Exception */ public void testFindCDPPathsNegatives() throws Exception { String[] dirs = { // - "/home/pc2/bad", // + "/home/pc2/bad", // "/fargo", // "/tmp2", // }; @@ -2331,31 +2332,31 @@ public void testFindCDPPathsNegatives() throws Exception { File actual = loader.findCDPConfigDirectory(new File(name)); // System.out.println("For "+name+" found "+actual); - + assertNull(actual); } } - + public void testBeforeNow() throws Exception { Date date = new Date(); - + // Not before now, just after now. assertFalse ("Expected not to be before date "+date,snake.isBeforeNow(date)); - - // very much after now + + // very much after now date.setTime(date.getTime() + 30000); assertTrue ("Expected not to be before date "+date,snake.isBeforeNow(date)); // very much before now date.setTime(date.getTime() - 60000); assertFalse ("Expected not to be before date "+date,snake.isBeforeNow(date)); - + } - + /** * Test data parsers. - * + * * @throws Exception */ public void testDateParsers() throws Exception { @@ -2376,13 +2377,13 @@ public void testDateParsers() throws Exception { // assertEquals(expected, d.getTime()); } - + /** * Test ISO Start time * Bug 1122 - Import contest yaml does not support start-time in ISO 8601 format * @throws Exception */ - + public void testISO8601StartTime() throws Exception { String testDirectoryName = getDataDirectory(this.getName()); @@ -2404,12 +2405,12 @@ public void testISO8601StartTime() throws Exception { long expected = 1477713600000L; assertEquals(expected, date.getTime()); } - + /** * Using problem key for problems unit test. - * + * * Bug 1177 - When loading yaml allow problems or problemset (key). - * + * * @throws Exception */ public void testProblemsYamlKey() throws Exception { @@ -2439,13 +2440,13 @@ public void testProblemsYamlKey() throws Exception { assertEquals("Problem short name apl", "apl", problems[0].getShortName()); assertEquals("Problem short name barcode", "barcodes", problems[1].getShortName()); } - + public void testProblemsYamlKeyFromFile() throws Exception { String dirname = getDataDirectory(this.getName()); // ensureDirectory(dirname); // startExplorer(dirname);; - + String yamlFilename= dirname + File.separator + "contest.ps.yaml"; // editFile(yamlFilename); @@ -2461,13 +2462,13 @@ public void testProblemsYamlKeyFromFile() throws Exception { assertEquals("Problem short name apl", "apl", problems[0].getShortName()); assertEquals("Problem short name barcode", "barcodes", problems[1].getShortName()); } - + /** - * Test loading of + * Test loading of * @throws Exception */ public void testCLICSJudgementOptionsLoad() throws Exception { - + String dir = getDataDirectory(this.getName()); String problemShortName = "prob1"; @@ -2491,10 +2492,10 @@ public void testCLICSJudgementOptionsLoad() throws Exception { boolean overrideUsePc2Validator = false; loader.loadProblemInformationAndDataFiles(contest, dir, problem, overrideUsePc2Validator); - + Problem problemDos = new Problem("Le Deux"); problemDos.setShortName("prob2"); - + loader.loadProblemInformationAndDataFiles(contest, dir, problemDos, overrideUsePc2Validator); Problem[] problems = contest.getProblems(); @@ -2507,7 +2508,7 @@ public void testCLICSJudgementOptionsLoad() throws Exception { // validator_flags: float_tolerance 1e-6 String expecting = "float_absolute_tolerance 1.0E-6 float_relative_tolerance 1.0E-6"; - + assertEquals(expecting, problem1.getClicsValidatorSettings().toString()); ClicsValidatorSettings settings = problem1.getClicsValidatorSettings(); @@ -2515,10 +2516,10 @@ public void testCLICSJudgementOptionsLoad() throws Exception { assertTrue("float_absolute_tolerance specified", settings.isFloatAbsoluteToleranceSpecified()); assertTrue("float_relative_tolerance", settings.isFloatRelativeToleranceSpecified()); assertFalse("space_change_sensitive ", settings.isSpaceSensitive()); - + assertEquals(1.0E-6, settings.getFloatAbsoluteTolerance()); assertEquals(1.0E-6, settings.getFloatRelativeTolerance()); - + Problem prob2 = problems[1]; @@ -2533,29 +2534,29 @@ public void testCLICSJudgementOptionsLoad() throws Exception { assertTrue("float_absolute_tolerance specified", settings.isFloatAbsoluteToleranceSpecified()); assertTrue("float_relative_tolerance", settings.isFloatRelativeToleranceSpecified()); assertTrue("space_change_sensitive ", settings.isSpaceSensitive()); - + assertEquals(4.0011122, settings.getFloatAbsoluteTolerance()); assertEquals(4.0042354, settings.getFloatRelativeTolerance()); } - + /** - * Test load input validator command line and validator. - * + * Test load input validator command line and validator. + * * @throws Exception */ @Test public void testLoadInputValidator() throws Exception { - + // String inputYamlFilenameOld = getProblemSetYamlTestFileName(); // editFile(inputYamlFilenameOld); - + // String datadir = getDataDirectory(); // startExplorer(datadir); - + String inputDir = getDataDirectory(this.getName()); ensureDirectory(inputDir); // startExplorer(inputDir); - + // short-name: apl // short-name: barcodes // short-name: biobots @@ -2563,9 +2564,9 @@ public void testLoadInputValidator() throws Exception { // short-name: channel String configDir = inputDir + File.separator + IContestLoader.CONFIG_DIRNAME; - + // startExplorer(configDir); - + IInternalContest contest = loader.fromYaml(null, configDir); assertNotNull(contest); @@ -2574,7 +2575,7 @@ public void testLoadInputValidator() throws Exception { String[] validatorNames = { // // - + "", // "", // "", // @@ -2582,7 +2583,7 @@ public void testLoadInputValidator() throws Exception { "", // "", // }; - + String[] validatorCommandLine = { // "", // @@ -2592,53 +2593,53 @@ public void testLoadInputValidator() throws Exception { "", // "", // }; - + // String INPUT_VALIDATOR_NAME_KEY = "inputValidatorName"; // String INPUT_VALIDATOR_COMMAND_LINE_KEY = "inputValidatorCommandLine"; - + int idx = 0; for (Problem problem2 : problems) { assertNotNull("Expected problem short name ", problem2.getShortName()); - + String expectedInputValidatorName = validatorNames[idx]; String expectedInputValidatorCommandLine = validatorCommandLine[idx]; idx ++; - + assertEquals("Expected input validator name ", expectedInputValidatorName, problem2.getCustomInputValidatorProgramName()); assertEquals("Expected input validator command ", expectedInputValidatorCommandLine, problem2.getCustomInputValidatorCommandLine()); } } - + public void testfindInputValidator() throws Exception { - + String dataDir = getDataDirectory(this.getName()); ensureDirectory(dataDir); - + String shortDirName = "one"; Problem problem = new Problem("Title 1"); problem.setShortName(shortDirName); - - String inputFormatValidatorDir = snake.getInputValidatorDir(dataDir, problem ); + + String inputFormatValidatorDir = snake.getInputValidatorDir(dataDir, problem ); ensureDirectory(inputFormatValidatorDir); // startExplorer(inputFormatValidatorDir); - + String validatorProgramName = snake.findInputValidator(dataDir, problem); - + String expected = "testdata/ContestSnakeYAMLLoaderTest/testfindInputValidator/one/input_format_validators/one.sh"; - + assertEquals("Expecting input dir name ", expected, toUnixFS(validatorProgramName)); - + } /** * Test loading all sample contests, a type of smoke test. - * + * * @throws Throwable */ public void testLoadAllSampleCDPS() throws Throwable { String[] contestDirs = getSampleContestsDirs(); - + assertDirectoryExists(Utilities.getCurrentDirectory() + File.separator + getSampleContestsDirectory()); assertTrue("Expecting at least one sample contest directory at " + getSampleContestsDirectory(), contestDirs.length > 0); @@ -2646,16 +2647,16 @@ public void testLoadAllSampleCDPS() throws Throwable { for (String directoryName : contestDirs) { assertDirectoryExists(directoryName); - + if (directoryName.endsWith("valtest")){ // Need not test valtest, that is tested in a number of other junit methods // besides one must load groups.tsv before loading yaml for it to work. continue; } - + try { - + IInternalContest contest = null; String teamsTSVFilename = directoryName + File.separator + IContestLoader.CONFIG_DIRNAME + // @@ -2682,16 +2683,16 @@ public void testLoadAllSampleCDPS() throws Throwable { } } - + public void testSampleCDP() throws Throwable { - + String directoryName = getRootInputTestDataDirectory() + File.separator + "samplecdp"; - + assertDirectoryExists(directoryName); // startExplorer(directoryName); - + IInternalContest contest; - + try { contest = snake.fromYaml(null, directoryName + File.separator + IContestLoader.CONFIG_DIRNAME, false); @@ -2702,24 +2703,24 @@ public void testSampleCDP() throws Throwable { System.err.println("Failed to load config from " + directoryName + " " + e.getCause().getMessage()); throw e.getCause(); } - + Vector accounts = contest.getAccounts(Type.TEAM); Collections.sort(accounts, new AccountComparator()); - + int teamAccountNumber = 1; for (Account account : accounts) { assertEquals("Account number ", teamAccountNumber, account.getClientId().getClientNumber()); teamAccountNumber++; } } - - + + /** * Test pc2 validator section: validator * @throws Exception */ public void testpc2ValidatorSection() throws Exception { - + String [] section = { IContestLoader.VALIDATOR_KEY + ":", // " validatorProg: pc2.jar edu.csus.ecs.pc2.validator.Validator", // @@ -2727,60 +2728,60 @@ public void testpc2ValidatorSection() throws Exception { " usingInternal: true", // " validatorOption: 1", // }; - - - Map content = snake.loadYaml(null, section); + + + Map content = ContestImportUtilities.loadYaml(null, section); Problem problem = createNewProblem(this.getName()); snake.assignValidatorSettings(content, problem); - + assertEquals("validator type", VALIDATOR_TYPE.PC2VALIDATOR, problem.getValidatorType()); assertEquals("validator program name", Constants.PC2_VALIDATOR_NAME, problem.getOutputValidatorProgramName()); } - + /** * Test pc2 validator key: validator_flags * @throws Exception */ public void test3pc2ValidatorFlags() throws Exception { - + String [] section = { IContestLoader.VALIDATOR_FLAGS_KEY + ": float_tolerance 1e-6", // }; - - Map content = snake.loadYaml(null, section); + + Map content = ContestImportUtilities.loadYaml(null, section); Problem problem = createNewProblem(this.getName()); snake.assignValidatorSettings(content, problem); - + assertEquals("validator type", VALIDATOR_TYPE.CLICSVALIDATOR, problem.getValidatorType()); assertEquals("validator program name", Constants.CLICS_VALIDATOR_NAME, problem.getOutputValidatorProgramName()); - + // huh } - + /** * Test CLICS validator key: validator * @throws Exception */ public void testCLICSValidatorOptions() throws Exception { - + String [] section = { IContestLoader.VALIDATOR_KEY + ": float_tolerance 1e-6", // }; - - Map content = snake.loadYaml(null, section); + + Map content = ContestImportUtilities.loadYaml(null, section); Problem problem = createNewProblem(this.getName()); snake.assignValidatorSettings(content, problem); - + assertEquals("validator type", VALIDATOR_TYPE.CLICSVALIDATOR, problem.getValidatorType()); assertEquals("validator program name", Constants.CLICS_VALIDATOR_NAME, problem.getOutputValidatorProgramName()); - + } - + private Problem createNewProblem(String name) { Problem problem = new Problem(name); problem.setShortName(name); @@ -2790,7 +2791,7 @@ private Problem createNewProblem(String name) { /** - * + * * @param directoryName * @param extension * @return @@ -2812,7 +2813,7 @@ public String[] getFileNames(String directoryName, String extension) { } } - return (String[]) list.toArray(new String[list.size()]); + return list.toArray(new String[list.size()]); } public String[] getDirNames(String directoryName) { @@ -2831,7 +2832,7 @@ public String[] getDirNames(String directoryName) { } } - return (String[]) list.toArray(new String[list.size()]); + return list.toArray(new String[list.size()]); } /** @@ -2839,14 +2840,14 @@ public String[] getDirNames(String directoryName) { * @return list of directory names. */ private String[] getSampleContestsDirs() { - + String sampContestDir = getSampleContestsDirectory(); String[] sampContestDirNames = getDirNames(sampContestDir); return sampContestDirNames; } - + public void testCLICSLanguageLoad() throws Exception { - + String [] section = { // "languages:", // @@ -2857,52 +2858,52 @@ public void testCLICSLanguageLoad() throws Exception { "", // }; - + IInternalContest contest = loader.fromYaml(null, section, null, false); Language[] languages = contest.getLanguages(); - + assertEquals(1,languages.length); - + } - + /** * Test max-output-size-K. - * + * * Bug 1149. - * + * * @throws Exception */ public void testImportMayFileSize() throws Exception { - + String [] section = { // "max-output-size-K: 128", "", // }; - + IInternalContest contest = loader.fromYaml(null, section, null, false); - + ContestInformation info = contest.getContestInformation(); assertNotNull("Expecting ContestInformation ", info); assertEquals("Expected max file size ",128*1024,info.getMaxOutputSizeInBytes()); } - + /** * Test invalid max-output-size-K. - * + * * Bug 1149. - * + * * @throws Exception */ public void testImportMayFileSizeErrorHandling() throws Exception { - + String [] data = // { "max-output-size-K: 0", // "max-output-size-K: -21", }; - + for (String line : data) { String [] section = { line }; try { @@ -2914,10 +2915,10 @@ public void testImportMayFileSizeErrorHandling() throws Exception { } } } - + /** * Test load of input format validator using sumitMTC sample. - * + * * @throws Exception */ public void testLoadInputFormatValidator() throws Exception { @@ -2929,7 +2930,7 @@ public void testLoadInputFormatValidator() throws Exception { Problem[] problems = con.getProblems(); assertEquals("Problem count ", 1, problems.length); - + String valiatorFileName = "valid.bat"; String ifvfilename = "samps/contests/sumitMTC/config/sumit/input_format_validators/"+valiatorFileName; @@ -2962,12 +2963,12 @@ private IInternalContest loadSampleContest(IInternalContest contest, String samp throw e; } } - + /** * Validate and test contents for valtest sample contest. - * + * *
  • Tests groups: in problem.yaml - * + * * @throws Exception */ public void testLoadValidatorTestSampleContest() throws Exception { @@ -2976,60 +2977,60 @@ public void testLoadValidatorTestSampleContest() throws Exception { * Contest to use as input */ String sampleContestDirName = "valtest"; - + IInternalContest contest = new InternalContest(); - + /** * Load groups data. */ loadGroupsFromSampContest(contest, sampleContestDirName); assertEquals("Number ground",12,contest.getGroups().length); - -// + +// // LoadICPCData loadICPCData = new LoadICPCData(); // loadICPCData.setContestAndController(contest, controller); -// - +// + loadSampleContest(contest, sampleContestDirName); - + assertNotNull(contest); - + Problem[] problems = contest.getProblems(); assertEquals("Problem count ", 6, problems.length); - + for (Problem problem : problems) { assertTrue("Expecting using validator for "+problem, problem.isValidatedProblem()); } - + Language[] langs = contest.getLanguages(); assertEquals("Language count ", 6, problems.length); - + String [] langnames = { - // - "Java", // - "GNU C", // - "GNU C++", // - "Python2", // - "Python3", // - "C#", // + // + "Java", // + "GNU C", // + "GNU C++", // + "Python2", // + "Python3", // + "C#", // }; - + int i = 0; for (Language language : langs) { assertEquals(langnames[i], language.getDisplayName()); i++; } - - String [] validatorNames = { + + String [] validatorNames = { // - "edu.csus.ecs.pc2.validator.clicsValidator.ClicsValidator", // - "edu.csus.ecs.pc2.validator.pc2Validator.PC2Validator", // - "edu.csus.ecs.pc2.validator.pc2Validator.PC2Validator", // - "edu.csus.ecs.pc2.validator.clicsValidator.ClicsValidator", // - "edu.csus.ecs.pc2.validator.pc2Validator.PC2Validator", // - "edu.csus.ecs.pc2.validator.pc2Validator.PC2Validator", // + "edu.csus.ecs.pc2.validator.clicsValidator.ClicsValidator", // + "edu.csus.ecs.pc2.validator.pc2Validator.PC2Validator", // + "edu.csus.ecs.pc2.validator.pc2Validator.PC2Validator", // + "edu.csus.ecs.pc2.validator.clicsValidator.ClicsValidator", // + "edu.csus.ecs.pc2.validator.pc2Validator.PC2Validator", // + "edu.csus.ecs.pc2.validator.pc2Validator.PC2Validator", // }; - + int totalProblemFiles = 0; i = 0; for (Problem problem : problems) { @@ -3042,18 +3043,18 @@ public void testLoadValidatorTestSampleContest() throws Exception { } assertEquals("All problems data files count ", 36, totalProblemFiles ); - + String title = "Mini Contest Validator Combos"; assertEquals("contest title", title, contest.getContestInformation().getContestTitle()); - + } /** * Load groups from samps contest. - * + * * @param contest * @param contestDirName - * @throws Exception + * @throws Exception */ private void loadGroupsFromSampContest(IInternalContest contest, String contestDirName) throws Exception { String groupFile = getTestSampleContestDirectory(contestDirName) +File.separator+ IContestLoader.CONFIG_DIRNAME + File.separator + LoadICPCTSVData.GROUPS_FILENAME; @@ -3065,7 +3066,7 @@ private void loadGroupsFromSampContest(IInternalContest contest, String contestD /** * Quote string, output string for use in String []. - * + * * @param string * @return output: "string", // */ @@ -3076,64 +3077,64 @@ protected String qs(String string) { private String toUnixFS(String path) { return path.replace('\\', '/'); } - + /** * Test with halt at end set. * @throws Exception */ public void testHaltAtEnd() throws Exception { - String[] lines = { // + String[] lines = { // // - "# Contest configuration with auto stop example", // - "# $Id: contest.yaml 2704 2013-10-16 05:38:06Z laned $", // - "---", // - "name: ACM-ICPC World Finals 2011", // - "short-name: ICPC WF 2011", // - "start-time: 2011-02-04 01:23Z", // - "duration: 5:00:00", // - "scoreboard-freeze: 4:00:00", // - "", // - "auto-stop-clock-at-end: true", // - "", // - "default-clars:", // - " - No comment, read problem statement.", // - " - This will be answered during the answers to questions session.", // - "", // - "clar-categories:", // - " - General", // - " - SysOps", // + "# Contest configuration with auto stop example", // + "# $Id: contest.yaml 2704 2013-10-16 05:38:06Z laned $", // + "---", // + "name: ACM-ICPC World Finals 2011", // + "short-name: ICPC WF 2011", // + "start-time: 2011-02-04 01:23Z", // + "duration: 5:00:00", // + "scoreboard-freeze: 4:00:00", // + "", // + "auto-stop-clock-at-end: true", // + "", // + "default-clars:", // + " - No comment, read problem statement.", // + " - This will be answered during the answers to questions session.", // + "", // + "clar-categories:", // + " - General", // + " - SysOps", // " - Operations", // }; IInternalContest contest = loader.fromYaml(null, lines, null, false); assertTrue("Expecting halt at end to be set ", contest.getContestInformation().isAutoStopContest()); } - + /** * Test with no halt at end * @throws Exception */ public void testHaltAtEndMissing() throws Exception { - String[] lines = { // + String[] lines = { // // - "# Contest configuration with auto stop example", // - "# $Id: contest.yaml 2704 2013-10-16 05:38:06Z laned $", // - "---", // - "name: ACM-ICPC World Finals 2011", // - "short-name: ICPC WF 2011", // - "start-time: 2011-02-04 01:23Z", // - "duration: 5:00:00", // - "scoreboard-freeze: 4:00:00", // - "", // - "default-clars:", // - " - No comment, read problem statement.", // - " - This will be answered during the answers to questions session.", // - "", // - "clar-categories:", // - " - General", // - " - SysOps", // + "# Contest configuration with auto stop example", // + "# $Id: contest.yaml 2704 2013-10-16 05:38:06Z laned $", // + "---", // + "name: ACM-ICPC World Finals 2011", // + "short-name: ICPC WF 2011", // + "start-time: 2011-02-04 01:23Z", // + "duration: 5:00:00", // + "scoreboard-freeze: 4:00:00", // + "", // + "default-clars:", // + " - No comment, read problem statement.", // + " - This will be answered during the answers to questions session.", // + "", // + "clar-categories:", // + " - General", // + " - SysOps", // " - Operations", // }; @@ -3141,42 +3142,42 @@ public void testHaltAtEndMissing() throws Exception { assertFalse("Expecting no halt at end", contest.getContestInformation().isAutoStopContest()); } - - + + /** * Test default setting for using judge command line. - * + * * Tests for languages that do not have a use-judge-cmd: true - * + * * Bug 1278 test. - * + * * @throws Exception */ public void testLanguageLoadJudgeCmdLine() throws Exception { - + // String configDir = getTestSampleContestDirectory( "sumitMTC") + File.separator + IContestLoader.CONFIG_DIRNAME; // String yamlFile = configDir + File.separator + IContestLoader.DEFAULT_CONTEST_YAML_FILENAME; // editFile(yamlFile); IInternalContest contest = loadSampleContest(null, "sumitMTC"); assertNotNull(contest); - + Language[] languages = contest.getLanguages(); - + assertEquals("Expecting language count ", 7, languages.length); - + for (Language language : languages) { if ("Perl".equals(language.getDisplayName())){ assertFalse ("Expect NOT Using judges command line boolean "+language, language.isUsingJudgeProgramExecuteCommandLine()); } } } - + /** * Test default value for isUsingJudgeProgramExecuteCommandLine. - * + * * Bug 1278 test. - * + * * @throws Exception */ public void testLanguageLoad() throws Exception { @@ -3194,18 +3195,18 @@ public void testLanguageLoad() throws Exception { }; Language[] languages = loader.getLanguages(yamlLines); - + for (Language language : languages) { - + // Default should be isUsingJudgeProgramExecuteCommandLine is false. - + assertFalse ("Expect NOT Using judges command line boolean "+language, language.isUsingJudgeProgramExecuteCommandLine()); } } - + public void testLoadDefaultTitle() throws Exception { - + String [] section = { IContestLoader.VALIDATOR_KEY + ":", // " validatorProg: pc2.jar edu.csus.ecs.pc2.validator.Validator", // @@ -3213,19 +3214,19 @@ public void testLoadDefaultTitle() throws Exception { " usingInternal: true", // " validatorOption: 1", // }; - - + + IInternalContest contest = snake.fromYaml(null, section, null); - + ContestInformation info = contest.getContestInformation(); - + assertNull("Expecting null for title ", info.getContestTitle()); - + } - + /** * test judging-typ in contest yaml. - * + * * @throws Exception */ public void testJudgingTypeSectionProblemSet() throws Exception { @@ -3233,7 +3234,7 @@ public void testJudgingTypeSectionProblemSet() throws Exception { String[] yamlLines = { // IContestLoader.JUDGING_TYPE_KEY + ":", // " computer-judged: false", // - " manual-review: true", // + " manual-review: true", // " send-prelim-judgement: false", // "problemset:", // " - letter: A", // @@ -3246,33 +3247,33 @@ public void testJudgingTypeSectionProblemSet() throws Exception { int problemIndex = 0; Problem problem = problems[problemIndex++]; - + // dumpJudgingTypes(this.getName(), problem); - - assertFalse("For problem "+problem+" expecting NOT isComputerJudged", problem.isComputerJudged()); - assertTrue("For problem "+problem+" expecting isManualReview", problem.isManualReview()); - assertFalse("For problem "+problem+" expecting NOT isPrelimaryNotification", problem.isPrelimaryNotification()); + + assertFalse("For problem "+problem+" expecting NOT isComputerJudged", problem.isComputerJudged()); + assertTrue("For problem "+problem+" expecting isManualReview", problem.isManualReview()); + assertFalse("For problem "+problem+" expecting NOT isPrelimaryNotification", problem.isPrelimaryNotification()); } - + /** * test judging-typ in problem yaml. */ public void testJudgingTypeProblmYaml() throws Exception { - + String dataDir = getDataDirectory(this.getName()); - + // String inputYamlFile = getDataDirectory("contest.yaml"); // System.out.println("input Filename: "+inputYamlFile); - + // ensureDirectory(dataDir); // startExplorer(dataDir); IInternalContest contest = loader.fromYaml(null, dataDir); - + // String[] yamlLines = { // // IContestLoader.JUDGING_TYPE_KEY + ":", // // " computer-judged: false", // -// " manual-review: true", // +// " manual-review: true", // // " send-prelim-judgement: false", // // }; @@ -3282,16 +3283,16 @@ public void testJudgingTypeProblmYaml() throws Exception { Problem problem = problems[problemIndex++]; // dumpJudgingTypes (this.getName(), problem); - - assertTrue("For problem "+problem+" expecting isComputerJudged", problem.isComputerJudged()); - assertTrue("For problem "+problem+" expecting isManualReview", problem.isManualReview()); - assertTrue("For problem "+problem+" expecting isPrelimaryNotification", problem.isPrelimaryNotification()); + + assertTrue("For problem "+problem+" expecting isComputerJudged", problem.isComputerJudged()); + assertTrue("For problem "+problem+" expecting isManualReview", problem.isManualReview()); + assertTrue("For problem "+problem+" expecting isPrelimaryNotification", problem.isPrelimaryNotification()); problem = problems[problemIndex++]; - - assertFalse("For problem "+problem+" expecting NOT isComputerJudged", problem.isComputerJudged()); - assertTrue("For problem "+problem+" expecting isManualReview", problem.isManualReview()); - assertFalse("For problem "+problem+" expecting NOT isPrelimaryNotification", problem.isPrelimaryNotification()); + + assertFalse("For problem "+problem+" expecting NOT isComputerJudged", problem.isComputerJudged()); + assertTrue("For problem "+problem+" expecting isManualReview", problem.isManualReview()); + assertFalse("For problem "+problem+" expecting NOT isPrelimaryNotification", problem.isPrelimaryNotification()); } void dumpJudgingTypes(String message, Problem problem) { @@ -3300,44 +3301,44 @@ void dumpJudgingTypes(String message, Problem problem) { " manual " + problem.isManualReview() + // " notify " + problem.isPrelimaryNotification()); } - + /** * Test loading groups into Problem. - * + * * @throws Exception */ public void testLoadProblemGroups() throws Exception { - + /** * Contest to use as input */ String sampleContestDirName = "valtest"; - + IInternalContest contest = new InternalContest(); - + /** * Load groups data. */ loadGroupsFromSampContest(contest, sampleContestDirName); assertEquals("Number ground",12,contest.getGroups().length); - + loadSampleContest(contest, sampleContestDirName); - + Problem[] problems = contest.getProblems(); assertEquals("Expecting problems ",6,problems.length); - + for (Problem problem : problems) { List groups = problem.getGroups(); - + if ("sumit2".equals(problem.getShortName())){ assertEquals("Expecting number of groups for problem ", 5, groups.size()); } } } - + /** * Test override settings to assign passwords. - * + * * @throws Exception */ public void testTeamYamlPasswordsOverride() throws Exception { @@ -3363,28 +3364,28 @@ public void testTeamYamlPasswordsOverride() throws Exception { // editFile(yamlFileName); IInternalContest contest = loader.fromYaml(null, yamlLines, dirname); - + Account[] teams = getTeamAccounts(contest); - + assertEquals("Team count", 80, teams.length); - + for (Account account : teams) { assertFalse("Not expecting joe password "+account.getPassword(), account.getClientId().getName().equals(account.getPassword())); assertEquals("Expecting password with length 12", 12, account.getPassword().length()); assertEquals("Expecting password with prefix bark", "bark", account.getPassword().substring(0,4)); } } - + /** * Test default password generation and assignment. - * + * * @throws Exception */ public void testTeamYamlPasswordsDefault() throws Exception { - + String dirname = getOutputDataDirectory(getName()); ensureDirectory(dirname); - + String[] yamlLines = { // "passwords:", // "# length: 12", // @@ -3404,11 +3405,11 @@ public void testTeamYamlPasswordsDefault() throws Exception { // editFile(yamlFileName); IInternalContest contest = loader.fromYaml(null, yamlLines, dirname); - + Account[] teams = getTeamAccounts(contest); - + assertEquals("Team count", 80, teams.length); - + for (Account account : teams) { assertFalse("Not expecting joe password "+account.getPassword(), account.getClientId().getName().equals(account.getPassword())); assertEquals("Expecting password with length 8", 8, account.getPassword().length()); @@ -3416,7 +3417,7 @@ public void testTeamYamlPasswordsDefault() throws Exception { } } - + public void testGroupLookup() throws Exception { // TODO write unit test @@ -3425,19 +3426,19 @@ public void testGroupLookup() throws Exception { //# groups: 'Canada - University of British Columbia D1;Puget Sound - University of Puget Sound D1; N. California - UC Berkeley D1; Hawaii - BYUH, Laie, Oahu D2; Northeast - EWU, Cheney/Spokane D2;' // //# no commas, delimit , -//# groups: 'Canada - University of British Columbia D1,Puget Sound - University of Puget Sound D1, N. California - UC Berkeley D1' +//# groups: 'Canada - University of British Columbia D1,Puget Sound - University of Puget Sound D1, N. California - UC Berkeley D1' // //# bad group id 334 for groups: 312544,312545,312546,312547, 334 //# groups: 312544,312545,312546,312547 //# groups: 312544;312545;312546;312547 - + } - + /** - * Tests that YAML files correctly support allowing multiple logins by a given team using {@link IContestLoader#ALLOW_MULTIPLE_TEAM_LOGINS_KEY}. + * Tests that YAML files correctly support allowing multiple logins by a given team using {@link IContestLoader#ALLOW_MULTIPLE_TEAM_LOGINS_KEY}. */ public void testMultipleLoginSupport() throws Exception { - + // make sure we have a valid data directory for this test String dataDirName = getDataDirectory(getName()); Utilities.insureDir(dataDirName); @@ -3450,7 +3451,7 @@ public void testMultipleLoginSupport() throws Exception { IInternalContest contest = loader.fromYaml(null, contents, dataDirName); assertNotNull(contest); - + boolean defaultAllow = contest.getContestInformation().isAllowMultipleLoginsPerTeam(); assertFalse("Loading YAML file with no 'allow-multiple-logins' flag failed to default to 'do not allow'", defaultAllow); @@ -3461,10 +3462,10 @@ public void testMultipleLoginSupport() throws Exception { contest = loader.fromYaml(null, contents, dataDirName); assertNotNull(contest); - + boolean explicitlySetFalse = contest.getContestInformation().isAllowMultipleLoginsPerTeam(); assertFalse("Loading YAML file with explicit 'allow-multiple-logins: false' flag failed to set 'do not allow multiple logins'", explicitlySetFalse); - + //test that if the contest.yaml file specifies "allow-multiple-team-logins: true", multiple logins are allowed yamlFilename = getTestFilename(getName() + File.separator + "contest.allowMultipleFlagTrue.yaml"); contents = Utilities.loadFile(yamlFilename); @@ -3472,12 +3473,12 @@ public void testMultipleLoginSupport() throws Exception { contest = loader.fromYaml(null, contents, dataDirName); assertNotNull(contest); - + boolean explicitlySetTrue = contest.getContestInformation().isAllowMultipleLoginsPerTeam(); assertTrue("Loading YAML file with explicit 'allow-multiple-logins: true' flag failed to set 'allow multiple logins'", explicitlySetTrue); - + } - + public void testScoreboardHTMLLocations() throws Exception { String dataDirName = getDataDirectory(getName()); @@ -3514,12 +3515,12 @@ public void testScoreboardHTMLLocations() throws Exception { assertEquals("Public HTML Directory", "public_html_dir", publicDir); } - + public void testLoadDisplay() throws Exception { - + String dirname = getOutputDataDirectory(getName()); ensureDirectory(dirname); - + String[] yamlLines = { // "", "team-scoreboard-display-format-string : '{:teamname}'", // @@ -3532,15 +3533,15 @@ public void testLoadDisplay() throws Exception { IInternalContest contest = loader.fromYaml(null, yamlLines, dirname); - + Account[] teams = getTeamAccounts(contest); assertEquals("Team count", 22, teams.length); String displayString = contest.getContestInformation().getTeamScoreboardDisplayFormat(); assertEquals("Team getTeamDisplayOnScoreboard", "{:teamname}", displayString); - + /** - * + * * Client/Team number - {:clientnumber} = 514 * Country Code - {:countrycode} = CAN * CMS/External ID - {:externalid} = 309407 @@ -3557,23 +3558,23 @@ public void testLoadDisplay() throws Exception { String teamDisplayString = ScoreboardVariableReplacer.substituteDisplayNameVariables(displayString, contest, account20); assertEquals("Expected display ", "team21", teamDisplayString); - + yamlTestTeamDisplayOnBoard(dirname, teamDisplayString, "team21"); } - + public void testOne() throws Exception { - + String dataDirName = getDataDirectory(getName()); // Utilities.insureDir(dataDirName); // assertDirectoryExists(dataDirName); - + String teamDisplayString = "{:clientnumber} {:countrycode} {:groupid} {:groupname} {:externalid}"; String expected = "21 XXX {:groupid} {:groupname} 1021"; yamlTestTeamDisplayOnBoard(dataDirName, teamDisplayString, expected); } - + void yamlTestTeamDisplayOnBoard (String dirname, String teamDisplayString, String expectedString) { - + String[] yamlLines = { // "", "team-scoreboard-display-format-string : '"+teamDisplayString+"'", // @@ -3586,7 +3587,7 @@ void yamlTestTeamDisplayOnBoard (String dirname, String teamDisplayString, Strin IInternalContest contest = loader.fromYaml(null, yamlLines, dirname); - + Account[] teams = getTeamAccounts(contest); assertEquals("Team count", 22, teams.length); @@ -3599,15 +3600,15 @@ void yamlTestTeamDisplayOnBoard (String dirname, String teamDisplayString, Strin assertEquals("Expected display string for "+ciDisplayString, expectedString, actual); } - + public void testAllSubstitutions() throws Exception { - + IInternalContest contest = loadFullSampleContest(null, "tenprobs"); assertNotNull(contest); - + Account[] teams = getTeamAccounts(contest); assertEquals("Team count", 80, teams.length); - + String teamDisplayString = "Team {:clientnumber} {:teamname} and login: {:teamloginname} {:groupid}:{:groupname} long: {:longschoolname} short: {:shortschoolname} cms id: {:externalid}"; String ciDisplayString = contest.getContestInformation().getTeamScoreboardDisplayFormat(); @@ -3618,7 +3619,7 @@ public void testAllSubstitutions() throws Exception { String expectedString = "Team 21 Team21 and login: team21 100:North long: Long21 short: Short21 cms id: 1021"; String actual = ScoreboardVariableReplacer.substituteDisplayNameVariables(ciDisplayString, contest, account20); assertEquals("Expected display string for "+ciDisplayString, expectedString, actual); - + } private Account[] getSortedTeamAccounts(IInternalContest contest) { @@ -3626,80 +3627,80 @@ private Account[] getSortedTeamAccounts(IInternalContest contest) { Arrays.sort(accounts, new AccountComparator()); return accounts; } - - + + public void testTenProbsisStopOnFirstFailedTestCase() throws Exception { - + String sampleName = "tenprobs"; IInternalContest contest = fullLoadSampleContest(sampleName); assertNotNull(contest); Problem[] problems = contest.getProblems(); assertEquals("Num problems ",10, problems.length); - + for (Problem problem : problems) { assertTrue(problem.getShortName()+" stop on first ", problem.isStopOnFirstFailedTestCase()); } - + } - + public void testisStopOnFirstFailedTestCase() throws Exception { - + String sampleName = "problemflagtest"; // use CDP sample problemflagtest IInternalContest contest = fullLoadSampleContest(sampleName); assertNotNull(contest); Problem[] problems = contest.getProblems(); assertEquals("Num problems ", 8, problems.length); - + for (Problem problem : problems) { assertTrue(problem.getShortName()+" stop on first ", problem.isStopOnFirstFailedTestCase()); } - + } - - - + + + /** * Test load problem.yaml isStopOnFirstFailedTestCase. - * + * * @throws Exception */ public void testvaltesttStopOnFirstFailedTestCase() throws Exception { - + String sampleName = "valtest"; IInternalContest contest = fullLoadSampleContest(sampleName); - + assertNotNull(contest); Problem[] problems = contest.getProblems(); assertEquals("Num problems ",6, problems.length); - + for (Problem problem : problems) { assertFalse(problem.getShortName()+" stop on first ", problem.isStopOnFirstFailedTestCase()); } - + } - - + + /** * Test yaml import for memory-limit-in-Meg and sandbox. - * + * * @throws Exception */ - + public void testLoadSandboxAndMemoryLimit() throws Exception { /** * Contest to use as input */ String sampleContestDirName = "sumitMTC"; - + IInternalContest contest = new InternalContest(); - - + + loadSampleContest(contest, sampleContestDirName); - + assertNotNull(contest); ContestInformation info = contest.getContestInformation(); @@ -3714,9 +3715,9 @@ public void testLoadSandboxAndMemoryLimit() throws Exception { String expected = "{:sandboxprogramname} {:memlimit} {:timelimit}"; assertEquals("Sandbox command", expected, problem.getSandboxCmdLine()); - - - + + + } private IInternalContest fullLoadSampleContest(String sampleName) throws Exception { @@ -3725,43 +3726,43 @@ private IInternalContest fullLoadSampleContest(String sampleName) throws Excepti contest = loadSampleContest(contest, sampleName); return contest; } - - + + /** * Test halt-contest-clock-at-set to true. - * + * * @throws Exception */ public void testisHaltContestAtTimeZero() throws Exception { String sampleContestDirName = "ccs1"; String dirname = getContestSampleCDPConfigDirname(sampleContestDirName); - + IInternalContest contest = snake.fromYaml(null, dirname, false); assertNotNull("Expecting to load ccs1 contest",contest); assertTrue("Expected halt at end of contest ", contest.getContestInformation().isAutoStopContest()); } - - /** + + /** * Test halt-contest-clock-at-end value, for when missing key/value * @throws Exception */ public void testisHaltContestAtTimeZeroNegative() throws Exception { String sampleContestDirName = "ccs2"; String dirname = getContestSampleCDPConfigDirname(sampleContestDirName); - + IInternalContest contest = snake.fromYaml(null, dirname, false); assertNotNull("Expecting to load ccs2 contest",contest); assertFalse("Expected NO halt at end of contest ", contest.getContestInformation().isAutoStopContest()); } - + public String getTestDataDirname(String dirname) { String contestConfigDir = getRootInputTestDataDirectory() +File.separator + dirname +File.separator+ IContestLoader.CONFIG_DIRNAME; return contestConfigDir; } - + /** * Test loading problem title from problem.en.tex. - * + * * @throws Exception */ public void testProblemNameENTex() throws Exception { @@ -3792,7 +3793,7 @@ public void testProblemNameENTex() throws Exception { } } } - + /** * Find/match problem in contest by problem letter * @param inContest @@ -3801,21 +3802,21 @@ public void testProblemNameENTex() throws Exception { */ // TODO REFACTOR proomote/move getProblemByLetter into AbstractTestCase private Problem getProblemByLetter(IInternalContest inContest, String letter) { - + Problem[] problems = inContest.getProblems(); for (Problem problem : problems) { if (letter.equalsIgnoreCase(problem.getLetter())) { return problem; } - + } return null; } - - + + /** * Test loading of output validator. - * + * * @throws Exception */ public void testaddClicsOutputValidator() throws Exception { @@ -3856,7 +3857,7 @@ public void testaddClicsOutputValidator() throws Exception { } } } - + /** * Test loading both sample and secret files. * @throws Exception @@ -3880,53 +3881,53 @@ public void testLoadSampleFiles() throws Exception { assertEquals("In " + MINI_CONTEST_DIR + " expecting sample and secret data files", 10, totalTestCases); } - + /** * Test creating and loading accounts from tsv file. - * + * * @throws Exception */ public void testloadAccountLoadFile() throws Exception { - + String dataDir = getDataDirectory(this.getName()); - + ensureDirectory(dataDir); // startExplorer(dataDir); - + ensureStaticLog(); IInternalContest contest = loadFullSampleContest(null, "mini"); assertNotNull(contest); - + assertEquals("Team accounts ", 151, contest.getAccounts(ClientType.Type.TEAM).size()); ContestSnakeYAMLLoader loader = new ContestSnakeYAMLLoader(); assertNotNull(loader); - + String accountLoadFilename = dataDir + File.separator + "mini.load.accounts.up.tsv"; // editFile(accountLoadFilename); loader.loadAccountLoadFile(contest, accountLoadFilename); assertEquals("Team accounts ", 201, contest.getAccounts(ClientType.Type.TEAM).size()); - + Vector teams = contest.getAccounts(ClientType.Type.TEAM); - Account[] teamArr = (Account[]) teams.toArray(new Account[teams.size()]); - + Account[] teamArr = teams.toArray(new Account[teams.size()]); + // Test all added accounts from mini.load.accounts.up.tsv - + for (Account account : teamArr) { - + int num = account.getClientId().getClientNumber(); - + if ( num < 51 ) { // teams 1 - 50 loaded from mini.load.accounts.up.tsv, only test those accounts - + assertEquals("TeamName " + num, account.getDisplayName()); assertEquals("USA", account.getCountryCode()); assertEquals("pass" + num, account.getPassword()); num++; } } - + } } diff --git a/test/edu/csus/ecs/pc2/imports/ccs/ICPCTSVLoaderTest.java b/test/edu/csus/ecs/pc2/imports/ccs/ICPCTSVLoaderTest.java index 08fdbad19..d8f1bd5d0 100644 --- a/test/edu/csus/ecs/pc2/imports/ccs/ICPCTSVLoaderTest.java +++ b/test/edu/csus/ecs/pc2/imports/ccs/ICPCTSVLoaderTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.imports.ccs; import java.io.File; @@ -17,12 +17,18 @@ /** * Unit Test ICPCTSVLoader. - * + * */ public class ICPCTSVLoaderTest extends AbstractTestCase { private boolean debugMode = false; + @Override + protected void setUp() throws Exception { + ensureStaticLog(); + super.setUp(); + } + private String findTestDataFile(String filename) throws IOException { String name = getRootInputTestDataDirectory() + File.separator + "ccs" + File.separator + filename; @@ -46,7 +52,7 @@ private void testTeamFile(String groupFile, String teamFileName, int expectedAcc /** * Test load of groups and teams tsv files. - * + * * @throws Exception */ public void testLoad() throws Exception { @@ -65,26 +71,26 @@ public void testTeams2load() throws Exception { assertNotEquals("Missing InstituionCode", "", accounts[0].getInstitutionCode()); } - - + + /** * Test 7th and 8th column for teams.tsv. - * - * Bug 1229 + * + * Bug 1229 * @throws Exception */ public void testLoad78fields() throws Exception { - + String configDir = getTestSampleContestDirectory("sumitMTC") + File.separator + IContestLoader.CONFIG_DIRNAME; assertDirectoryExists(configDir); - + // startExplorer(configDir); String groupFile = configDir + File.separator + "groups.tsv"; String teamFileName = configDir + File.separator + "teams.tsv"; - + // editFile(teamFileName); - + ICPCTSVLoader.loadGroups(groupFile); Account[] accounts = ICPCTSVLoader.loadAccounts(teamFileName); @@ -132,9 +138,9 @@ public void testextractTeamNumber() throws Exception { /** * Test load accounts.tsv with teamN login names. - * + * * Bug 1241. - * + * * @throws Exception */ public void testTeamNumberfromAccountsTSVFile() throws Exception { @@ -154,12 +160,12 @@ public void testTeamNumberfromAccountsTSVFile() throws Exception { Vector va = contest.getAccounts(Type.TEAM); Account[] accounts = - (Account[]) va.toArray(new Account[va.size()]); + va.toArray(new Account[va.size()]); assertEquals("Expecting N accounts", 128, accounts.length); Arrays.sort(accounts, new AccountComparator()); - + for (int i = 1; i < accounts.length + 1; i++) { assertEquals("Expeting team number " + i, "team" + i, accounts[i - 1].getTeamName()); } @@ -168,7 +174,7 @@ public void testTeamNumberfromAccountsTSVFile() throws Exception { /** * Load contest from contest.yaml. - * + * * @param contest * @param configDir * @return diff --git a/test/edu/csus/ecs/pc2/imports/ccs/TestDataGroupsTest.java b/test/edu/csus/ecs/pc2/imports/ccs/TestDataGroupsTest.java new file mode 100644 index 000000000..44d6250ab --- /dev/null +++ b/test/edu/csus/ecs/pc2/imports/ccs/TestDataGroupsTest.java @@ -0,0 +1,184 @@ +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.imports.ccs; + +import java.io.File; +import java.util.ArrayList; + +import edu.csus.ecs.pc2.core.exception.YamlLoadException; +import edu.csus.ecs.pc2.core.log.StaticLog; +import edu.csus.ecs.pc2.core.model.SampleContest; +import edu.csus.ecs.pc2.core.util.AbstractTestCase; +import edu.csus.ecs.pc2.core.util.JUnitUtilities; + +/** + * Test the Data Groups classes + * + * @author John Buck + * + */ +public class TestDataGroupsTest extends AbstractTestCase { + private String loadDir = "testdata" + File.separator; + + private SampleContest sample = new SampleContest(); + + @Override + protected void setUp() throws Exception { + String projectPath = JUnitUtilities.locate(loadDir); + if (projectPath == null) { + throw new Exception("Unable to locate " + loadDir); + } + File dir = new File(projectPath + File.separator + loadDir); + if (dir.exists()) { + loadDir = dir.toString() + File.separator; + } else { + System.err.println("could not find " + loadDir); + throw new Exception("Unable to locate " + loadDir); + } + ensureStaticLog(); + super.setUp(); + } + + /** + * Tests whether parsing of the testdata.yaml files work + * + * @throws Exception + */ + public void testTestDataGroup() throws Exception { + String inputTestDirectory = getDataDirectory(this.getName()) + File.separator + "data"; + String testDir = getOutputDataDirectory(); + String secretDir = inputTestDirectory + File.separator + "secret" + File.separator; + String sampleDir = inputTestDirectory + File.separator + "sample" + File.separator; + String testDataFile = secretDir + TestDataGroup.TESTDATA_YAML; + String group1DataFile = secretDir + "group1" + File.separator + TestDataGroup.TESTDATA_YAML; + String group2DataFile = secretDir + "group2" + File.separator + TestDataGroup.TESTDATA_YAML; + String group3DataFile = secretDir + "group3" + File.separator + TestDataGroup.TESTDATA_YAML; + + removeDirectory(testDir); + ensureDirectory(testDir); + + TestDataGroup secret = new TestDataGroup("secret", inputTestDirectory, null); + assertEquals("Expecting test data group name ", "secret", secret.getGroupName()); + assertTrue("Expecting on_reject of break", secret.isOnRejectBreak()); + assertTrue("Expecting grading of default", secret.isGradingDefault()); + assertTrue("Expecting no output validator flags for secret group ", + secret.getOutputValidatorFlags() == null || secret.getOutputValidatorFlags().isEmpty()); + assertTrue("Expecting no input validator flags for secret group", + secret.getInputValidatorFlags() == null || secret.getInputValidatorFlags().isEmpty()); + assertEquals("Expecting accept_score of 1 ", 1.0, secret.getAcceptScore()); + assertEquals("Expecting reject_score of 0 ", 0.0, secret.getRejectScore()); + // read yaml file + if(!secret.processDataYaml(testDataFile)) { + this.failTest("Missing " + testDataFile); + } + assertTrue("Expecting on_reject of continue", secret.isOnRejectContinue()); + assertTrue("Expecting grading of custom", secret.isGradingCustom()); + assertEquals("Expecting output validator flags for secret group ", "nothing here either", secret.getOutputValidatorFlags()); + assertTrue("Expecting no input validator flags for secret group", secret.getInputValidatorFlags() == null || secret.getInputValidatorFlags().isEmpty()); + assertEquals("Expecting accept_score of 100 ", 100.0, secret.getAcceptScore()); + assertEquals("Expecting reject_score of 1 ", 1.0, secret.getRejectScore()); + assertEquals("Expecting min range to be 0 ", 0.0, secret.getRangeMin()); + assertEquals("Expecting maz range to be 100 ", 100.0, secret.getRangeMax()); + + + // group1 is under secret + TestDataGroup group1 = new TestDataGroup("secret/group1", inputTestDirectory, secret); + assertEquals("Expecting test data group name ", "secret/group1", group1.getGroupName()); + + // test inheritance + double defaultAcceptScore = secret.getAcceptScore(); + secret.setAcceptScore(12345.5); + group1 = new TestDataGroup("secret/group1", inputTestDirectory, secret); + assertEquals("Expecting test data group name ", "secret/group1", group1.getGroupName()); + assertEquals("Expecting accept_score to be 12345.5 ", 12345.5, group1.getAcceptScore()); + // set it back + secret.setAcceptScore(defaultAcceptScore); + assertEquals("Expecting group1 output validator flags ", "nothing here either", group1.getOutputValidatorFlags()); + + if(!group1.processDataYaml(group1DataFile)) { + this.failTest("Missing " + group1DataFile); + } + assertTrue("Expecting on_reject of break ", group1.isOnRejectBreak()); + assertEquals("Expecting accept_score of 145 ", 145.0, group1.getAcceptScore()); + + // now test to see if the basic stuff was updated after reading the testdata.yaml file + assertEquals("Expecting output validator flags ", "outputarg group1", group1.getOutputValidatorFlags()); + assertEquals("Expecting input validator flags ", "test arg group1", group1.getInputValidatorFlags()); + assertEquals("Expecting min range to be 0 ", 0.0, group1.getRangeMin()); + assertEquals("Expecting max range to be 25 ", 25.0, group1.getRangeMax()); + + + // group2 is under secret + TestDataGroup group2 = new TestDataGroup("secret/group2", inputTestDirectory, secret); + assertEquals("Expecting test data group name ", "secret/group2", group2.getGroupName()); + // test inheritance + defaultAcceptScore = secret.getAcceptScore(); + secret.setAcceptScore(12345.5); + group2 = new TestDataGroup("secret/group2", inputTestDirectory, secret); + assertEquals("Expecting test data group name ", "secret/group2", group2.getGroupName()); + assertEquals("Expecting accept_score to be 12345.5 ", 12345.5, group2.getAcceptScore()); + // set it back + secret.setAcceptScore(defaultAcceptScore); + assertEquals("Expecting group2 output validator flags ", "nothing here either", group2.getOutputValidatorFlags()); + + if(!group2.processDataYaml(group2DataFile)) { + this.failTest("Missing " + group2DataFile); + } + assertTrue("Expecting on_reject of continue ", group2.isOnRejectContinue()); + + // now test to see if the basic stuff was updated after reading the testdata.yaml file + assertEquals("Expecting accept_score of 75 ", 75.0, group2.getAcceptScore()); + assertEquals("Expecting output validator flags ", "outputarg group2", group2.getOutputValidatorFlags()); + assertEquals("Expecting input validator flags ", "group2 ival1arg ival2arg", group2.getInputValidatorFlags()); + assertEquals("Expecting min range to be 0 ", 0.0, group2.getRangeMin()); + assertEquals("Expecting max range to be 100000 ", 100000.0, group2.getRangeMax()); + + // group3 is under secret and has no testdata.yaml + TestDataGroup group3 = new TestDataGroup("secret/group3", inputTestDirectory, secret); + assertEquals("Expecting test data group name ", "secret/group3", group3.getGroupName()); + + try { + if(group3.processDataYaml(group3DataFile)) { + this.failTest("File is present and should not be " + group3DataFile); + } + } catch(YamlLoadException e) { + // This is good. + } + // these should be copied directly from secret group + assertTrue("Expecting on_reject of continue", group3.isOnRejectContinue()); + assertTrue("Expecting grading of custom", group3.isGradingCustom()); + assertEquals("Expecting output validator flags for secret group ", "nothing here either", group3.getOutputValidatorFlags()); + assertTrue("Expecting no input validator flags for secret group", group3.getInputValidatorFlags() == null || group3.getInputValidatorFlags().isEmpty()); + assertEquals("Expecting accept_score of 100 ", 100.0, group3.getAcceptScore()); + assertEquals("Expecting reject_score of 1 ", 1.0, group3.getRejectScore()); + assertEquals("Expecting min range to be 0 ", 0.0, group3.getRangeMin()); + assertEquals("Expecting maz range to be 100 ", 100.0, group3.getRangeMax()); + + } + + /** + * Tests whether reading a data folder and its subgroups works + * + * @throws Exception + */ + public void testReadDataGroups() throws Exception { + // Use same directory as for previous test + String inputTestDirectory = getDataDirectory("testTestDataGroup") + File.separator + "data"; + String testDir = getOutputDataDirectory(); + + TestDataGroup secret = new TestDataGroup("data", inputTestDirectory, null); + assertTrue(secret.readTestCases(StaticLog.getLog())); + assertEquals("Expecting total test case of 16 ", 16, secret.getTotalTestCases()); + ArrayList arGroups = secret.getTestDataGroups(); + assertEquals("Expecting subgroup count of 1 ", 1, arGroups.size()); + TestDataGroup group = arGroups.get(0); + assertEquals("Expecting 3 testcases in group 1 ", 3, group.getTestDataGroups().get(0).getTotalTestCases()); + assertEquals("Expecting 4 testcases in group 2 ", 4, group.getTestDataGroups().get(1).getTotalTestCases()); + assertEquals("Expecting 9 testcases in group 3 ", 9, group.getTestDataGroups().get(2).getTotalTestCases()); + +// ArrayList arInfo = secret.getAllTestCaseInfo(); +// System.out.println("There are " + arInfo.size() + " test cases:"); +// for(TestCaseInfo tc : arInfo) { +// System.out.println(tc.toString()); +// } + } +} diff --git a/test/edu/csus/ecs/pc2/shadow/RemoteEventFeedMonitorTest.java b/test/edu/csus/ecs/pc2/shadow/RemoteEventFeedMonitorTest.java index 0d336ee67..f9982af0f 100644 --- a/test/edu/csus/ecs/pc2/shadow/RemoteEventFeedMonitorTest.java +++ b/test/edu/csus/ecs/pc2/shadow/RemoteEventFeedMonitorTest.java @@ -1,3 +1,4 @@ +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.shadow; import java.io.File; @@ -18,14 +19,20 @@ /** * Unit test. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class RemoteEventFeedMonitorTest extends AbstractTestCase { + @Override + protected void setUp() throws Exception { + ensureStaticLog(); + super.setUp(); + } + /** * Contact app server? - * + * * true = yes, contact server at {@link #LOCALHOST_CONTEST_EVENT_FEED} * false = no, skip test. */ @@ -39,7 +46,7 @@ public class RemoteEventFeedMonitorTest extends AbstractTestCase { /** * Tests RemoteEventFeedMonitor using running pc2 server with feeder login. - * + * * @throws Exception */ public void testSubmmissonsLive() throws Exception { diff --git a/test/edu/csus/ecs/pc2/ui/team/QuickSubmitterTest.java b/test/edu/csus/ecs/pc2/ui/team/QuickSubmitterTest.java index 92dffa843..44133e3ea 100644 --- a/test/edu/csus/ecs/pc2/ui/team/QuickSubmitterTest.java +++ b/test/edu/csus/ecs/pc2/ui/team/QuickSubmitterTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.ui.team; import java.io.File; @@ -25,6 +25,12 @@ */ public class QuickSubmitterTest extends AbstractTestCase { + @Override + protected void setUp() throws Exception { + ensureStaticLog(); + super.setUp(); + } + void addOtherLangugages(IInternalContest contest) { diff --git a/test/edu/csus/ecs/pc2/util/ScoreboardVariableReplacerTest.java b/test/edu/csus/ecs/pc2/util/ScoreboardVariableReplacerTest.java index 04eeeb130..10bc45ff3 100644 --- a/test/edu/csus/ecs/pc2/util/ScoreboardVariableReplacerTest.java +++ b/test/edu/csus/ecs/pc2/util/ScoreboardVariableReplacerTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.util; import java.util.Arrays; @@ -17,6 +17,12 @@ */ public class ScoreboardVariableReplacerTest extends AbstractTestCase { + @Override + protected void setUp() throws Exception { + ensureStaticLog(); + super.setUp(); + } + /** * Test substituteDisplayNameVariables with var string, account and group. * diff --git a/test/edu/csus/pc2/Graders/LegacyGraderTest.java b/test/edu/csus/pc2/Graders/LegacyGraderTest.java new file mode 100644 index 000000000..7447fc02b --- /dev/null +++ b/test/edu/csus/pc2/Graders/LegacyGraderTest.java @@ -0,0 +1,125 @@ +// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.pc2.Graders; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.InputStream; +import java.io.PrintStream; + +import edu.csus.ecs.pc2.core.util.AbstractTestCase; +import edu.csus.ecs.pc2.core.util.JUnitUtilities; +import edu.csus.ecs.pc2.graders.LegacyGrader; + +public class LegacyGraderTest extends AbstractTestCase { + private static final String AllACFile = "allAC.txt"; + private static final String ACTLEWARTEFile = "acRTEWATLE.txt"; + private static final String IGNORESAMPLEFile = "ignoresample.txt"; + private static final String IGNORESAMPLENOSECRETFile = "ignoresamplenosecret.txt"; + + private String loadDir = "testdata" + File.separator; + + + @Override + protected void setUp() throws Exception { + String projectPath = JUnitUtilities.locate(loadDir); + if (projectPath == null) { + throw new Exception("Unable to locate " + loadDir); + } + File dir = new File(projectPath + File.separator + loadDir); + if (dir.exists()) { + loadDir = dir.toString() + File.separator; + } else { + System.err.println("could not find " + loadDir); + throw new Exception("Unable to locate " + loadDir); + } + super.setUp(); + } + + private int runTest(String testName, String inputFile, String outputFile, String cmdline, String expResult) throws Exception + { + int result = 0; + String [] args = new String[0]; + + if(!cmdline.isEmpty()) { + args = cmdline.split("\\s+"); + } + + LegacyGrader grader = new LegacyGrader(); + assertTrue("Expected no arguments to be valid ", grader.parseArguments(args)); + InputStream originalStdin = System.in; + PrintStream originalStdout = System.out; + + System.setIn(new FileInputStream(inputFile)); + System.setOut(new PrintStream(new FileOutputStream(outputFile))); + + result = grader.processResults(); + System.setOut(originalStdout); + System.setIn(originalStdin); + + // read answer if it worked + if(result == 0) { + String ans = null; + try (BufferedReader reader = new BufferedReader(new FileReader(outputFile))) { + ans = reader.readLine().trim(); + assertEquals(testName + ": " + expResult + " ", expResult, ans); + } catch(Exception e) { + System.err.println("Can not read " + outputFile); + result = -1; + } + } + return(result); + } + + /** + * Tests whether all accepted works + * + */ + public void testAllAC() throws Exception { + String inputTestDirectory = getDataDirectory(""); + String testDir = getOutputDataDirectory(); + String testDataFile = inputTestDirectory + AllACFile; + String testOutputFile = testDir + File.separator + "result.txt"; + + + removeDirectory(testDir); + ensureDirectory(testDir); + + assertEquals("Expected ALL AC with defaults to be 0 ", 0, + runTest(this.getName(), testDataFile, testOutputFile, "", "AC 1000.0")); + assertEquals("Expected ALL AC with SM avg to be 0 ", 0, + runTest(this.getName(), testDataFile, testOutputFile, "avg", "AC 90.9090909090909")); + assertEquals("Expected ALL AC with SM min to be 0 ", 0, + runTest(this.getName(), testDataFile, testOutputFile, "min", "AC 0.0")); + assertEquals("Expected ALL AC with SM max to be 0 ", 0, + runTest(this.getName(), testDataFile, testOutputFile, "max", "AC 200.25")); + + testDataFile = inputTestDirectory + ACTLEWARTEFile; + assertEquals("Expected ACTLEWARTE with defaults to be 0 ", 0, + runTest(this.getName(), testDataFile, testOutputFile, "", "RTE 0")); + assertEquals("Expected ACTLEWARTE with first_error to be 0 ", 0, + runTest(this.getName(), testDataFile, testOutputFile, "first_error", "TLE 0")); + assertEquals("Expected ACTLEWARTE with accept_if_any_accepted/sum to be 0 ", 0, + runTest(this.getName(), testDataFile, testOutputFile, "accept_if_any_accepted sum", "AC 654.0")); + assertEquals("Expected ACTLEWARTE with always_accept/avg to be 0 ", 0, + runTest(this.getName(), testDataFile, testOutputFile, "accept_if_any_accepted avg", "AC 59.45454545454545")); + + // Test ignore_sample + testDataFile = inputTestDirectory + IGNORESAMPLEFile; + assertEquals("Expected ignore_sample to be 0 ", 0, + runTest(this.getName(), testDataFile, testOutputFile, "ignore_sample", "AC 20.0")); + + // This file has exactly one line in it + testDataFile = inputTestDirectory + IGNORESAMPLENOSECRETFile; + assertEquals("Expected too few ignore_sample to be 8 ", LegacyGrader.GRADER_ERROR_IGNORE_SAMPLE, + runTest(this.getName(), testDataFile, testOutputFile, "ignore_sample", "NOT USED")); + + // This file has too many lines in it if ignore_sample is used; there should be only 2 results in the file. + testDataFile = inputTestDirectory + ACTLEWARTEFile; + assertEquals("Expected too many ignore_sample to be 8 ", LegacyGrader.GRADER_ERROR_IGNORE_SAMPLE, + runTest(this.getName(), testDataFile, testOutputFile, "ignore_sample", "NOT USED")); + + } +} diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump-reflected.ans new file mode 100644 index 000000000..23fa82a0f --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-b-b-.-. -.-.-.-. +.-.-W-.- .-.-.-.- +-b-b-b-. -.-.-.-. +.-.-.-.- .-W-.-.- +-.-b-b-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump-reflected.in new file mode 100644 index 000000000..d0f465701 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump-reflected.in @@ -0,0 +1,2 @@ +W 1 +15x24x31x22x15x6x13x22 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump.ans new file mode 100644 index 000000000..a561e501b --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-w-w-.- .-.-.-.- +-.-.-.-. -.-.-B-. +.-w-w-w- .-.-.-.- +-.-B-.-. -.-.-.-. +.-.-w-w- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump.desc b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump.desc new file mode 100644 index 000000000..596385d98 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump.desc @@ -0,0 +1 @@ +A jump that passes through its start point. diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump.in new file mode 100644 index 000000000..15b4d79da --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/01-loop-jump.in @@ -0,0 +1,2 @@ +B 1 +18x9x2x11x18x27x20x11 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump-reflected.ans new file mode 100644 index 000000000..6a8279c50 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-W-.- .-.-W-.- +-.-b-b-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-b-b-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump-reflected.in new file mode 100644 index 000000000..fcf80f6c1 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump-reflected.in @@ -0,0 +1,2 @@ +W 1 +15x24x31x22x15 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump.ans new file mode 100644 index 000000000..47f833233 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-w-w-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-w-w-.- .-.-.-.- +-.-B-.-. -.-B-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump.desc b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump.desc new file mode 100644 index 000000000..f92fe42ef --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump.desc @@ -0,0 +1 @@ +A jump that ends where it started. diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump.in new file mode 100644 index 000000000..0196c688f --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/02-loop-jump.in @@ -0,0 +1,2 @@ +B 1 +18x9x2x11x18 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color-reflected.ans new file mode 100644 index 000000000..03c95c7c1 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-b- .-.-.-.- +-.-.-.-. -.-.-w-b +.-.-w-w- .-w-w-.- +-.-w-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color-reflected.in new file mode 100644 index 000000000..fba14501c --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color-reflected.in @@ -0,0 +1,4 @@ +W 3 +16-11 +8-12 +18-14 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color.ans new file mode 100644 index 000000000..73f04213c --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-b-.- .-.-.-.- +-b-b-.-. -.-b-b-. +.-.-.-.- w-b-.-.- +-w-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color.desc b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color.desc new file mode 100644 index 000000000..557ae40df --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color.desc @@ -0,0 +1 @@ +An extra man must be inserted, and it can only be of one color without causing a contradiction. diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color.in new file mode 100644 index 000000000..66cb75ed5 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/03-force-fill-color.in @@ -0,0 +1,4 @@ +B 3 +17-22 +25-21 +15-19 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking-reflected.ans new file mode 100644 index 000000000..a75160d4f --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking-reflected.ans @@ -0,0 +1,8 @@ +-.-.-b-. -.-.-.-. +.-.-.-.- .-.-.-b- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-w -.-.-.-w +.-.-.-b- .-w-.-.- +-w-.-.-. -.-w-w-b +.-w-w-w- .-.-w-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking-reflected.in new file mode 100644 index 000000000..466a9aa56 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking-reflected.in @@ -0,0 +1,6 @@ +W 5 +32-27 +3-8 +30-26 +24-28 +25-22 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking.ans new file mode 100644 index 000000000..7eaa46c73 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking.ans @@ -0,0 +1,8 @@ +-b-b-b-. -.-b-.-. +.-.-.-b- w-b-b-.- +-w-.-.-. -.-.-b-. +w-.-.-.- w-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -w-.-.-. +.-w-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking.desc b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking.desc new file mode 100644 index 000000000..953be73cf --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking.desc @@ -0,0 +1 @@ +A square in row 0 needs to be filled to block a jump, but cannot be filled with a white king. diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking.in new file mode 100644 index 000000000..15e30e595 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/04-fill-noking.in @@ -0,0 +1,6 @@ +B 5 +1-6 +30-25 +3-7 +9-5 +8-11 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king-reflected.ans new file mode 100644 index 000000000..ce2972281 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-b-.-b- .-.-.-.- +-.-.-.-. -.-b-w-b +.-.-B-w- .-.-B-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king-reflected.in new file mode 100644 index 000000000..ff42aa19f --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king-reflected.in @@ -0,0 +1,4 @@ +B 3 +22-26 +32-27 +24-28 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king.ans new file mode 100644 index 000000000..88c84ceae --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king.ans @@ -0,0 +1,8 @@ +-b-W-.-. -.-W-.-. +.-.-.-.- w-b-w-.- +-w-.-w-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king.desc b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king.desc new file mode 100644 index 000000000..88a1fd617 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king.desc @@ -0,0 +1 @@ +A white king has to be added to block a jump. diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king.in new file mode 100644 index 000000000..9f65d8803 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/05-fill-king.in @@ -0,0 +1,4 @@ +W 3 +11-7 +1-6 +9-5 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1-reflected.ans new file mode 100644 index 000000000..b35b76e74 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-w-w-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-w-w-.-. -.-.-.-. +.-B-.-.- .-B-.-.- +-.-.-.-. -.-w-.-. +.-w-w-.- .-.-w-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1-reflected.in new file mode 100644 index 000000000..05d886e05 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1-reflected.in @@ -0,0 +1,3 @@ +W 2 +30-26 +22x15x6x13x22 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1.ans new file mode 100644 index 000000000..b22e1c5c9 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1.ans @@ -0,0 +1,8 @@ +-.-W-b-. -.-W-.-. +.-.-.-.- .-.-b-.- +-.-.-W-. -.-.-W-. +.-.-b-b- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-b-b- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1.in new file mode 100644 index 000000000..d8265833a --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/06-dk1.in @@ -0,0 +1,3 @@ +B 2 +3-7 +11x18x27x20x11 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2-reflected.ans new file mode 100644 index 000000000..ea3d4bfd9 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-b-.-b- .-.-.-.- +-.-.-.-. -b-w-b-. +.-w-B-.- .-.-B-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2-reflected.in new file mode 100644 index 000000000..274ca9ba1 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2-reflected.in @@ -0,0 +1,4 @@ +B 3 +24-27 +30-26 +22-25 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2.ans new file mode 100644 index 000000000..2259d55fa --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2.ans @@ -0,0 +1,8 @@ +-.-W-b-. -.-W-.-. +.-.-.-.- .-w-b-w- +-w-.-w-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2.in new file mode 100644 index 000000000..e28b10541 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/07-dk2.in @@ -0,0 +1,4 @@ +W 3 +9-6 +3-7 +11-8 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3-reflected.ans new file mode 100644 index 000000000..2c041fbce --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3-reflected.ans @@ -0,0 +1,8 @@ +-.-b-W-. -.-.-W-. +.-.-.-.- .-w-b-w- +-.-w-.-w -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3-reflected.in new file mode 100644 index 000000000..8fcb9dc68 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3-reflected.in @@ -0,0 +1,4 @@ +W 3 +12-8 +2-7 +10-6 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3.ans new file mode 100644 index 000000000..1c9ba1f18 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +b-.-b-.- .-.-.-.- +-.-.-.-. -b-w-b-. +.-B-w-.- .-B-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3.in new file mode 100644 index 000000000..696c15d84 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/08-dk3.in @@ -0,0 +1,4 @@ +B 3 +21-25 +31-26 +23-27 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4-reflected.ans new file mode 100644 index 000000000..65cc5c2d7 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4-reflected.ans @@ -0,0 +1,8 @@ +-.-b-.-. -.-.-.-. +.-.-.-.- w-.-.-.- +-.-w-.-. -.-.-.-. +.-w-.-.- .-.-.-.- +-.-w-W-. -.-w-.-B +.-.-.-B- .-.-W-.- +-.-.-.-w -.-.-.-w +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4-reflected.in new file mode 100644 index 000000000..50fe8868c --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4-reflected.in @@ -0,0 +1,6 @@ +W 5 +19-23 +24-20 +10-6 +2x9 +14x5 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4.ans new file mode 100644 index 000000000..1d8e97f4e --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4.ans @@ -0,0 +1,8 @@ +-W-.-.-. -W-.-.-. +w-w-.-.- w-w-.-.- +-W-.-.-. -.-B-.-. +.-B-w-.- W-.-w-.- +-.-.-b-. -.-.-.-. +.-.-b-.- .-.-.-.- +-.-.-.-. -.-.-.-b +.-.-w-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4.in new file mode 100644 index 000000000..04babd6ac --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/09-dk4.in @@ -0,0 +1,6 @@ +B 5 +14-10 +9-13 +23-27 +31x24 +19x28 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces-reflected.ans new file mode 100644 index 000000000..fd099b504 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces-reflected.ans @@ -0,0 +1,8 @@ +-.-b-b-B -.-B-B-B +b-b-B-B- .-W-b-B- +-B-w-B-B -b-.-B-B +b-b-b-B- b-b-B-B- +-b-B-b-B -b-B-b-B +b-B-b-B- .-B-b-B- +-b-B-b-B -b-B-b-B +B-B-B-B- B-B-B-B- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces-reflected.in new file mode 100644 index 000000000..70f39e0de --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces-reflected.in @@ -0,0 +1,61 @@ +W 60 +10x1 +9-6 +1x10 +2-6 +10x1 +5-9 +1-5 +7-2 +5-1 +3-7 +1-5 +8-3 +5-1 +4-8 +1-5 +8-4 +5-1 +12-8 +1-5 +16-12 +5-1 +20-16 +1-5 +24-20 +5-1 +28-24 +1-5 +32-28 +5-1 +27-32 +1-5 +23-27 +5-1 +19-23 +1-5 +15-19 +5-1 +11-15 +1-5 +15-11 +5-1 +18-15 +1-5 +22-18 +5-1 +26-22 +1-5 +31-26 +5-1 +26-31 +1-5 +30-26 +5-1 +25-30 +1-6 +29-25 +6-1 +25-29 +1-6 +21-25 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces.ans new file mode 100644 index 000000000..9aabfe8f2 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces.ans @@ -0,0 +1,8 @@ +-W-W-W-W -W-W-W-W +W-w-W-w- W-w-W-w- +-W-w-W-w -W-w-W-. +W-w-W-w- W-w-W-w- +-W-w-w-w -W-W-w-w +W-W-b-W- W-W-.-w- +-W-W-w-w -W-w-B-. +W-w-w-.- W-W-W-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces.in new file mode 100644 index 000000000..80b1b3fcd --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/10-max-pieces.in @@ -0,0 +1,61 @@ +B 60 +23x32 +24-27 +32x23 +31-27 +23x32 +28-24 +32-28 +26-31 +28-32 +30-26 +32-28 +25-30 +28-32 +29-25 +32-28 +25-29 +28-32 +21-25 +32-28 +17-21 +28-32 +13-17 +32-28 +9-13 +28-32 +5-9 +32-28 +1-5 +28-32 +6-1 +32-28 +10-6 +28-32 +14-10 +32-28 +18-14 +28-32 +22-18 +32-28 +18-22 +28-32 +15-18 +32-28 +11-15 +28-32 +7-11 +32-28 +2-7 +28-32 +7-2 +32-28 +3-7 +28-32 +8-3 +32-27 +4-8 +27-32 +8-4 +32-27 +12-8 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11-reflected.ans new file mode 100644 index 000000000..4730c8569 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11-reflected.ans @@ -0,0 +1,8 @@ +-.-W-W-W -.-.-.-W +.-.-b-b- .-.-.-W- +-.-b-.-. -.-.-.-. +.-w-.-.- .-.-b-.- +-.-.-.-. -.-.-.-. +.-w-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-B-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11-reflected.in new file mode 100644 index 000000000..7a8e71aac --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11-reflected.in @@ -0,0 +1,6 @@ +B 5 +10x17x26 +2x11 +8x15 +3-8 +26-31 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11.ans new file mode 100644 index 000000000..5abbe7a2d --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-W-.-. +.-.-.-.- .-.-.-.- +-.-.-b-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-b-. -.-w-.-. +.-.-w-.- .-.-.-.- +-w-w-.-. -B-.-.-. +B-B-B-.- B-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11.in new file mode 100644 index 000000000..79209512f --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/11.in @@ -0,0 +1,6 @@ +W 5 +23x16x7 +31x22 +25x18 +30-25 +7-2 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12-reflected.ans new file mode 100644 index 000000000..062610873 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +b-b-b-w- .-w-.-.- +-W-.-.-b -.-.-W-. +w-b-.-w- .-.-.-.- +-.-.-b-. -.-.-.-. +w-.-w-.- .-.-.-.- +-w-.-w-. -.-W-.-. +B-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12-reflected.in new file mode 100644 index 000000000..9e53dfef1 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12-reflected.in @@ -0,0 +1,33 @@ +B 32 +19x26 +9x2x11 +29x22 +21-17 +12x19 +17x10 +22-25 +11-15 +25-22 +15x24 +26-30 +27-23 +22-26 +8-4 +26x19x28 +10-6 +5-9 +6-2 +28-24 +13x6 +24-19 +4-8 +30-26 +8-12 +19-16 +12x19 +26-30 +2-7 +30-26 +7-11 +26-23 +19x26 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12.ans new file mode 100644 index 000000000..e4baa499f --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12.ans @@ -0,0 +1,8 @@ +-.-.-.-W -.-.-.-. +.-b-.-b- .-.-B-.- +-.-b-.-b -.-.-.-. +.-w-.-.- .-.-.-.- +-b-.-w-b -.-.-.-. +w-.-.-B- .-B-.-.- +-b-w-w-w -.-.-b-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12.in new file mode 100644 index 000000000..a3b688d45 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/12.in @@ -0,0 +1,33 @@ +W 32 +14x7 +24x31x22 +4x11 +12-16 +21x14 +16x23 +11-8 +22-18 +8-11 +18x9 +7-3 +6-10 +11-7 +25-29 +7x14x5 +23-27 +28-24 +27-31 +5-9 +20x27 +9-14 +29-25 +3-7 +25-21 +14-17 +21x14 +7-3 +31-26 +3-7 +26-22 +7-10 +14x7 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13-reflected.ans new file mode 100644 index 000000000..81a2214bc --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-B-.- +-w-b-.-. -B-.-.-. +.-W-.-.- .-.-.-.- +-.-.-.-B -.-.-.-. +.-.-B-.- .-.-.-.- +-w-w-.-b -.-.-.-. +.-.-.-w- .-.-.-B- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13-reflected.in new file mode 100644 index 000000000..90040737f --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13-reflected.in @@ -0,0 +1,26 @@ +B 25 +23x30x21 +14x7 +20-16 +7-11 +16x7 +32-27 +28-32 +9-5 +32x23 +5-1 +23-27 +1-6 +21-25 +6-1 +27-32 +1-6 +7-2 +6-1 +25-22 +1-5 +22-18 +5-9 +2-7 +9-14 +18x9 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13.ans new file mode 100644 index 000000000..16b129747 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13.ans @@ -0,0 +1,8 @@ +-b-.-.-. -W-.-.-. +w-.-b-b- .-.-.-.- +-.-W-.-. -.-.-.-. +W-.-.-.- .-.-.-.- +-.-.-B-. -.-.-.-. +.-.-w-b- .-.-.-W- +-.-.-.-. -.-W-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13.in new file mode 100644 index 000000000..b0455314a --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/13.in @@ -0,0 +1,26 @@ +W 25 +10x3x12 +19x26 +13-17 +26-22 +17x26 +1-6 +5-1 +24-28 +1x10 +28-32 +10-6 +32-27 +12-8 +27-32 +6-1 +32-27 +26-31 +27-32 +8-11 +32-28 +11-15 +28-24 +31-26 +24-19 +15x24 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14-reflected.ans new file mode 100644 index 000000000..5a57dd555 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-b-w- .-.-.-w- +-.-b-.-. -.-.-b-. +.-w-.-.- .-.-w-.- +-.-w-w-. -.-w-w-. +.-.-.-.- b-.-.-.- +-.-w-.-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14-reflected.in new file mode 100644 index 000000000..77eb0c9bb --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14-reflected.in @@ -0,0 +1,7 @@ +B 6 +10x17 +19-15 +7-11 +26-23 +17-21 +23-19 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14.ans new file mode 100644 index 000000000..10ca9c803 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-b-.- .-.-.-.- +-.-.-.-. -.-.-.-w +.-b-b-.- .-b-b-.- +-.-.-b-. -.-b-.-. +.-.-w-.- .-w-.-.- +-w-w-.-. -w-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14.in new file mode 100644 index 000000000..09ff9eea3 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/14.in @@ -0,0 +1,7 @@ +W 6 +23x16 +14-18 +26-22 +7-10 +16-12 +10-14 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15-reflected.ans new file mode 100644 index 000000000..dcff4ac52 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15-reflected.ans @@ -0,0 +1,8 @@ +-W-b-b-. -.-.-W-W +.-.-.-B- .-.-.-.- +-.-.-.-w -.-.-.-w +.-.-w-.- .-.-.-.- +-b-.-.-. -.-.-w-. +.-W-w-.- .-.-.-.- +-b-.-.-. -b-.-.-. +.-.-.-.- W-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15-reflected.in new file mode 100644 index 000000000..486ef2ac9 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15-reflected.in @@ -0,0 +1,13 @@ +W 12 +22x29 +2-6 +1x10 +3-7 +10x3 +8-4 +15-11 +4-8 +11x4 +17-21 +23-19 +21-25 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15.ans new file mode 100644 index 000000000..9904f6097 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-B +.-.-.-w- .-.-.-w- +-.-b-B-. -.-.-.-. +.-.-.-w- .-b-.-.- +-.-b-.-. -.-.-.-. +w-.-.-.- w-.-.-.- +-W-.-.-. -.-.-.-. +.-w-w-B- B-B-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15.in new file mode 100644 index 000000000..7fb22d1f0 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/15.in @@ -0,0 +1,13 @@ +B 12 +11x4 +31-27 +32x23 +30-26 +23x30 +25-29 +18-22 +29-25 +22x29 +16-12 +10-14 +12-8 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16-reflected.ans new file mode 100644 index 000000000..bd73334f0 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16-reflected.ans @@ -0,0 +1,8 @@ +-b-.-.-. -.-.-.-. +.-.-.-b- w-w-.-.- +-w-w-b-. -.-w-w-b +.-.-.-.- .-.-.-.- +-.-w-b-. -.-.-.-. +.-.-w-w- .-w-.-.- +-w-.-.-. -B-b-.-. +.-B-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16-reflected.in new file mode 100644 index 000000000..c5aaeae7c --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16-reflected.in @@ -0,0 +1,13 @@ +B 12 +19x26 +25-22 +8-12 +9-6 +1-5 +18-14 +11-16 +24-20 +5-9 +14x5 +30-25 +20x11 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16.ans new file mode 100644 index 000000000..c3d0965e5 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16.ans @@ -0,0 +1,8 @@ +-.-.-W-. -.-.-.-. +.-.-.-b- .-.-w-W- +-b-b-.-. -.-.-b-. +.-w-b-w- .-.-.-w- +-.-.-.-w -.-.-.-w +.-w-w-b- w-b-w-.- +-w-b-.-. -.-b-b-b +.-.-.-w- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16.in new file mode 100644 index 000000000..7b80a91dd --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/16.in @@ -0,0 +1,13 @@ +W 12 +14x7 +8-11 +25-21 +24-27 +32-28 +15-19 +22-17 +9-13 +28-24 +19x28 +3-8 +13x22 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17-reflected.ans new file mode 100644 index 000000000..7da94ac0c --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-b -.-.-W-. +.-.-b-.- w-.-.-.- +-b-w-b-w -.-.-.-w +.-w-.-W- w-.-.-W- +-.-.-.-b -.-.-.-w +.-w-b-.- .-.-w-.- +-w-.-.-. -.-.-.-. +.-B-w-w- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17-reflected.in new file mode 100644 index 000000000..251feb1f1 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17-reflected.in @@ -0,0 +1,16 @@ +W 15 +14x5 +30x21 +10x3 +23-27 +32x23 +21-17 +22x13 +20-24 +16x7 +24-27 +31x24 +4-8 +24-20 +8-11 +7x16 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17.ans new file mode 100644 index 000000000..3975e87ed --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17.ans @@ -0,0 +1,8 @@ +-b-b-W-. -.-.-.-. +.-.-.-b- .-.-.-.- +-.-w-b-. -.-b-.-. +w-b-.-.- b-b-.-.- +-B-.-b-. -B-.-.-b +w-w-b-w- w-.-.-.- +-.-w-.-. -.-.-.-b +w-.-.-.- .-B-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17.in new file mode 100644 index 000000000..128733b1f --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret1/17.in @@ -0,0 +1,16 @@ +B 15 +19x28 +3x12 +23x30 +10-6 +1x10 +12-16 +11x20 +13-9 +17x26 +9-6 +2x9 +29-25 +9-13 +25-22 +26x17 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump-reflected.ans new file mode 100644 index 000000000..23fa82a0f --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-b-b-.-. -.-.-.-. +.-.-W-.- .-.-.-.- +-b-b-b-. -.-.-.-. +.-.-.-.- .-W-.-.- +-.-b-b-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump-reflected.in new file mode 100644 index 000000000..d0f465701 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump-reflected.in @@ -0,0 +1,2 @@ +W 1 +15x24x31x22x15x6x13x22 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump.ans new file mode 100644 index 000000000..a561e501b --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-w-w-.- .-.-.-.- +-.-.-.-. -.-.-B-. +.-w-w-w- .-.-.-.- +-.-B-.-. -.-.-.-. +.-.-w-w- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump.in new file mode 100644 index 000000000..15b4d79da --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/01-loop-jump.in @@ -0,0 +1,2 @@ +B 1 +18x9x2x11x18x27x20x11 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/02-loop-jump-reflected.ans b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/02-loop-jump-reflected.ans new file mode 100644 index 000000000..6a8279c50 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/02-loop-jump-reflected.ans @@ -0,0 +1,8 @@ +-.-.-.-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-.-.-. -.-.-.-. +.-.-W-.- .-.-W-.- +-.-b-b-. -.-.-.-. +.-.-.-.- .-.-.-.- +-.-b-b-. -.-.-.-. +.-.-.-.- .-.-.-.- diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/02-loop-jump-reflected.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/02-loop-jump-reflected.in new file mode 100644 index 000000000..fcf80f6c1 --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/02-loop-jump-reflected.in @@ -0,0 +1,2 @@ +W 1 +15x24x31x22x15 diff --git a/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/02-loop-jump.in b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/02-loop-jump.in new file mode 100644 index 000000000..0196c688f --- /dev/null +++ b/testdata/ContestImportUtilitiesTest/testgetTestCaseFileNames/secret2/02-loop-jump.in @@ -0,0 +1,2 @@ +B 1 +18x9x2x11x18 diff --git a/testdata/LegacyGraderTest/acRTEWATLE.txt b/testdata/LegacyGraderTest/acRTEWATLE.txt new file mode 100644 index 000000000..7759b0a5e --- /dev/null +++ b/testdata/LegacyGraderTest/acRTEWATLE.txt @@ -0,0 +1,11 @@ +AC 100 +AC 50 +TLE 0 +AC 200 +AC 3.75 +AC 200.25 +WA 0 +AC 0 +AC 1 +AC 99 +RTE 0 diff --git a/testdata/LegacyGraderTest/allAC.txt b/testdata/LegacyGraderTest/allAC.txt new file mode 100644 index 000000000..6798cb2e8 --- /dev/null +++ b/testdata/LegacyGraderTest/allAC.txt @@ -0,0 +1,11 @@ +AC 100 +AC 50 +AC 150 +AC 200 +AC 3.75 +AC 200.25 +AC 96.0 +AC 0 +AC 1 +AC 99 +AC 100 diff --git a/testdata/LegacyGraderTest/ignoresample.txt b/testdata/LegacyGraderTest/ignoresample.txt new file mode 100644 index 000000000..a6562fe94 --- /dev/null +++ b/testdata/LegacyGraderTest/ignoresample.txt @@ -0,0 +1,2 @@ +AC 100.0 +AC 20.0 diff --git a/testdata/LegacyGraderTest/ignoresamplenosecret.txt b/testdata/LegacyGraderTest/ignoresamplenosecret.txt new file mode 100644 index 000000000..4d327b375 --- /dev/null +++ b/testdata/LegacyGraderTest/ignoresamplenosecret.txt @@ -0,0 +1 @@ +AC 100.0 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/1x1.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/1x1.ans new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/1x1.ans @@ -0,0 +1 @@ +1 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/1x1.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/1x1.in new file mode 100644 index 000000000..e8183f05f --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/1x1.in @@ -0,0 +1,3 @@ +1 +1 +1 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/double_stair.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/double_stair.ans new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/double_stair.ans @@ -0,0 +1 @@ +2 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/double_stair.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/double_stair.in new file mode 100644 index 000000000..b2cef7372 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/double_stair.in @@ -0,0 +1,6 @@ +4 +4 +1110 +1101 +1011 +0111 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/empty.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/empty.ans new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/empty.ans @@ -0,0 +1 @@ +2 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/empty.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/empty.in new file mode 100644 index 000000000..2f1465d15 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/empty.in @@ -0,0 +1,3 @@ +1 +1 +0 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/testdata.yaml b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/testdata.yaml new file mode 100644 index 000000000..5352c086f --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group1/testdata.yaml @@ -0,0 +1,5 @@ +on_reject: break +accept_score: 145 +input_validator_flags: "test arg group1" +output_validator_flags: "outputarg group1" +range: 0 25 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/4x5.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/4x5.ans new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/4x5.ans @@ -0,0 +1 @@ +1 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/4x5.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/4x5.in new file mode 100644 index 000000000..4d90f64f9 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/4x5.in @@ -0,0 +1,6 @@ +4 +5 +11111 +11111 +11111 +11111 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/5x5.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/5x5.ans new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/5x5.ans @@ -0,0 +1 @@ +2 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/5x5.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/5x5.in new file mode 100644 index 000000000..92d49c1f1 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/5x5.in @@ -0,0 +1,7 @@ +5 +5 +11111 +11111 +11111 +11111 +11111 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/L.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/L.ans new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/L.ans @@ -0,0 +1 @@ +1 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/L.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/L.in new file mode 100644 index 000000000..8e6ea459c --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/L.in @@ -0,0 +1,7 @@ +5 +5 +10000 +10000 +10000 +10000 +11111 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/checker.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/checker.ans new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/checker.ans @@ -0,0 +1 @@ +2 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/checker.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/checker.in new file mode 100644 index 000000000..3437f3278 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/checker.in @@ -0,0 +1,7 @@ +5 +5 +10101 +01010 +10101 +01010 +10101 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/testdata.yaml b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/testdata.yaml new file mode 100644 index 000000000..2a5826bd6 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group2/testdata.yaml @@ -0,0 +1,4 @@ +accept_score: 75. +input_validator_flags: "group2 ival1arg ival2arg" +output_validator_flags: "outputarg group2" +range: 0 100000 \ No newline at end of file diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x10.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x10.ans new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x10.ans @@ -0,0 +1 @@ +2 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x10.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x10.in new file mode 100644 index 000000000..c1d34c595 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x10.in @@ -0,0 +1,102 @@ +100 +100 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000100000000000000000000000000000001000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000100000000000000000000000000000001000000000000000000000000000000000000000000000000000 +0000000000000000100000000000000000000000000000001000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000100000000000000000000000000000001000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000100000000000000000000000000000001000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000100000000000000000000000000000001000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000100000000000000000000000000000001000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000100000000000000000000000000000001000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000100000000000000000000000000000001000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000100000000000000000000000000000001000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_diag.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_diag.ans new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_diag.ans @@ -0,0 +1 @@ +2 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_diag.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_diag.in new file mode 100644 index 000000000..1c5048eba --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_diag.in @@ -0,0 +1,10 @@ +8 +8 +00001100 +00001100 +10100000 +01000010 +00010001 +00010001 +01000010 +10100000 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_off.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_off.ans new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_off.ans @@ -0,0 +1 @@ +1 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_off.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_off.in new file mode 100644 index 000000000..df1c3f8a1 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x2_off.in @@ -0,0 +1,10 @@ +8 +8 +00101000 +00000101 +10010000 +01000010 +00000101 +00001000 +10010000 +01000010 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x6.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x6.ans new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x6.ans @@ -0,0 +1 @@ +2 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x6.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x6.in new file mode 100644 index 000000000..16869fc27 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x6.in @@ -0,0 +1,4 @@ +2 +6 +111111 +111111 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x7.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x7.ans new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x7.ans @@ -0,0 +1 @@ +1 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x7.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x7.in new file mode 100644 index 000000000..17fde4e80 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x7.in @@ -0,0 +1,4 @@ +2 +7 +1111111 +1111111 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x8.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x8.ans new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x8.ans @@ -0,0 +1 @@ +2 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x8.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x8.in new file mode 100644 index 000000000..931232137 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/2x8.in @@ -0,0 +1,4 @@ +2 +8 +11111111 +11111111 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/6x6.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/6x6.ans new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/6x6.ans @@ -0,0 +1 @@ +1 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/6x6.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/6x6.in new file mode 100644 index 000000000..aea5a3e8c --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/6x6.in @@ -0,0 +1,8 @@ +6 +6 +111001 +001110 +101111 +101001 +011001 +000011 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x5.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x5.ans new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x5.ans @@ -0,0 +1 @@ +2 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x5.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x5.in new file mode 100644 index 000000000..d6ac2e475 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x5.in @@ -0,0 +1,9 @@ +7 +5 +11011 +00001 +01101 +00110 +10110 +11000 +11001 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x7.ans b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x7.ans new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x7.ans @@ -0,0 +1 @@ +1 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x7.in b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x7.in new file mode 100644 index 000000000..a7a95fae3 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/group3/7x7.in @@ -0,0 +1,9 @@ +7 +7 +0100110 +0000000 +0010000 +0101011 +0101101 +1100010 +1001111 diff --git a/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/testdata.yaml b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/testdata.yaml new file mode 100644 index 000000000..815c9d1d3 --- /dev/null +++ b/testdata/TestDataGroupsTest/testTestDataGroup/data/secret/testdata.yaml @@ -0,0 +1,8 @@ +# This file has every key in it, except input_validator_flags +on_reject: continue +grading: custom +range: 0 100 +grader_flags: first_error accept_if_any_accepted +output_validator_flags: nothing here either +accept_score: 100.0 +reject_score: 1 \ No newline at end of file