From 6348a0807b887ef74f2c4b61b13625d3b729c5da Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Sun, 31 Mar 2024 02:03:51 -0400 Subject: [PATCH 01/14] docs: detailed workflow explanation --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ffcd5b6..f69dd2b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,11 @@ ## Quick Start -To register the sandbox executor, you need to run go-judge before running this program. +To register the sandbox executor, you need to run [go-judge](https://github.com/criyle/go-judge) before running this program. + +**Hint for `go-judge`:** `go build -o ./tmp/go-judge ./cmd/go-judge && ./tmp/go-judge -enable-grpc -enable-debug -enable-metrics` + +Then you can check the functions of `joj3` with the `make test`. The cases used here are in `/examples`. ```bash $ make test @@ -49,13 +53,12 @@ pre-commit installed at .git/hooks/pre-commit ``` ## Models -The program parses the TOML file to run multiple stages. -Each stage contains an executor and parser. +The program parses the configuration file to run multiple stages. It can create an issue on Gitea to report the result of each stage after all stages are done. -Executor takes a `Cmd` and returns an `ExecutorResult`. +Each stage contains an executor and parser. An executor just executes a command and returns the original result (stdout, stderr, output files). We can limit the time and memory used by each command in the executor. We run all kinds of commands in executors of different stages, including code formatting, static check, compilation, and execution. A parser takes the result and the configuration of the stage to parse the result and return the score and comment. e.g. If in the current stage, the executor runs a `clang-tidy` command, then we can use the clang-tidy parser in the configuration file to parse the stdout of the executor result and check whether some of the rules are followed. We can deduct the score and add some comments based on the result, and return the score and comment as the output of this stage. This stage ends here and the next stage starts. -Parser takes an `ExecutorResult` and its conf and returns a `ParserResult` and `bool` to indicate whether we should skip the rest stages. +In codes, an executor takes a `Cmd` and returns an `ExecutorResult`, while a parser takes an `ExecutorResult` and its conf and returns a `ParserResult` and `bool` to indicate whether we should skip the rest stages. ### `Cmd` From b82d6f48d90c2d62a35226672f870bdb1103b7d3 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Sun, 31 Mar 2024 17:03:08 -0400 Subject: [PATCH 02/14] chore: re-organize examples --- cmd/joj3/main_test.go | 8 ++++---- examples/{compile_error => compile/error}/conf.toml | 0 examples/{ => compile}/success/.gitignore | 0 examples/{ => compile}/success/a.cc | 0 examples/{ => compile}/success/cases/1.in | 0 examples/{ => compile}/success/cases/1.out | 0 examples/{ => compile}/success/cases/2.in | 0 examples/{ => compile}/success/cases/2.out | 0 examples/{ => compile}/success/conf.toml | 0 examples/{dummy_error => dummy/error}/conf.toml | 2 +- examples/dummy/{ => success}/conf.toml | 2 +- 11 files changed, 6 insertions(+), 6 deletions(-) rename examples/{compile_error => compile/error}/conf.toml (100%) rename examples/{ => compile}/success/.gitignore (100%) rename examples/{ => compile}/success/a.cc (100%) rename examples/{ => compile}/success/cases/1.in (100%) rename examples/{ => compile}/success/cases/1.out (100%) rename examples/{ => compile}/success/cases/2.in (100%) rename examples/{ => compile}/success/cases/2.out (100%) rename examples/{ => compile}/success/conf.toml (100%) rename examples/{dummy_error => dummy/error}/conf.toml (95%) rename examples/dummy/{ => success}/conf.toml (95%) diff --git a/cmd/joj3/main_test.go b/cmd/joj3/main_test.go index 73b387d..39db646 100644 --- a/cmd/joj3/main_test.go +++ b/cmd/joj3/main_test.go @@ -44,7 +44,7 @@ func TestMain(t *testing.T) { name string want []stage.StageResult }{ - {"success", []stage.StageResult{ + {"compile/success", []stage.StageResult{ {Name: "compile", Results: []stage.ParserResult{ {Score: 0, Comment: ""}, }}, @@ -53,17 +53,17 @@ func TestMain(t *testing.T) { {Score: 100, Comment: "executor status: run time: \\d+ ns, memory: \\d+ bytes"}, }}, }}, - {"compile_error", []stage.StageResult{ + {"compile/error", []stage.StageResult{ {Name: "compile", Results: []stage.ParserResult{ {Score: 0, Comment: "Unexpected executor status: Nonzero Exit Status\\."}, }}, }}, - {"dummy", []stage.StageResult{ + {"dummy/success", []stage.StageResult{ {Name: "dummy", Results: []stage.ParserResult{ {Score: 110, Comment: "dummy comment \\+ comment from toml conf"}, }}, }}, - {"dummy_error", []stage.StageResult{ + {"dummy/error", []stage.StageResult{ {Name: "dummy", Results: []stage.ParserResult{ {Score: 0, Comment: "Unexpected executor status: Nonzero Exit Status\\.\\s*Stderr: dummy negative score: -1"}, }}, diff --git a/examples/compile_error/conf.toml b/examples/compile/error/conf.toml similarity index 100% rename from examples/compile_error/conf.toml rename to examples/compile/error/conf.toml diff --git a/examples/success/.gitignore b/examples/compile/success/.gitignore similarity index 100% rename from examples/success/.gitignore rename to examples/compile/success/.gitignore diff --git a/examples/success/a.cc b/examples/compile/success/a.cc similarity index 100% rename from examples/success/a.cc rename to examples/compile/success/a.cc diff --git a/examples/success/cases/1.in b/examples/compile/success/cases/1.in similarity index 100% rename from examples/success/cases/1.in rename to examples/compile/success/cases/1.in diff --git a/examples/success/cases/1.out b/examples/compile/success/cases/1.out similarity index 100% rename from examples/success/cases/1.out rename to examples/compile/success/cases/1.out diff --git a/examples/success/cases/2.in b/examples/compile/success/cases/2.in similarity index 100% rename from examples/success/cases/2.in rename to examples/compile/success/cases/2.in diff --git a/examples/success/cases/2.out b/examples/compile/success/cases/2.out similarity index 100% rename from examples/success/cases/2.out rename to examples/compile/success/cases/2.out diff --git a/examples/success/conf.toml b/examples/compile/success/conf.toml similarity index 100% rename from examples/success/conf.toml rename to examples/compile/success/conf.toml diff --git a/examples/dummy_error/conf.toml b/examples/dummy/error/conf.toml similarity index 95% rename from examples/dummy_error/conf.toml rename to examples/dummy/error/conf.toml index c6b8fa2..7648609 100644 --- a/examples/dummy_error/conf.toml +++ b/examples/dummy/error/conf.toml @@ -11,7 +11,7 @@ memoryLimit = 104_857_600 procLimit = 50 copyInCwd = true [stages.executor.with.default.copyIn.dummy] -src = "./../../build/dummy" +src = "./../../../build/dummy" copyOut = ["stdout", "stderr"] [stages.executor.with.default.stdin] content = "" diff --git a/examples/dummy/conf.toml b/examples/dummy/success/conf.toml similarity index 95% rename from examples/dummy/conf.toml rename to examples/dummy/success/conf.toml index 80b2d72..7fef08c 100644 --- a/examples/dummy/conf.toml +++ b/examples/dummy/success/conf.toml @@ -11,7 +11,7 @@ memoryLimit = 104_857_600 procLimit = 50 copyInCwd = true [stages.executor.with.default.copyIn.dummy] -src = "./../../build/dummy" +src = "./../../../build/dummy" copyOut = ["stdout", "stderr"] [stages.executor.with.default.stdin] content = "" From 7b5b1f4addf0ef43f32daf0468a510963b141be2 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Sun, 31 Mar 2024 17:06:08 -0400 Subject: [PATCH 03/14] chore: move .out file in .gitignore --- .gitignore | 1 + examples/compile/success/.gitignore | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 examples/compile/success/.gitignore diff --git a/.gitignore b/.gitignore index 4e4ba07..60874eb 100644 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,4 @@ $RECYCLE.BIN/ # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) build/ +!examples/**/*.out diff --git a/examples/compile/success/.gitignore b/examples/compile/success/.gitignore deleted file mode 100644 index 6aa6b80..0000000 --- a/examples/compile/success/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!*.out From cb7cd5cc42e7c391175fdea06a5f2a5868b42650 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Tue, 2 Apr 2024 18:42:19 -0400 Subject: [PATCH 04/14] docs: update `make test` result --- README.md | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index f69dd2b..f7492f0 100644 --- a/README.md +++ b/README.md @@ -10,35 +10,36 @@ Then you can check the functions of `joj3` with the `make test`. The cases used ```bash $ make test -go test -v ./... -? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/dummy [no test files] +go test -coverprofile cover.out -v ./... + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/dummy coverage: 0.0% of statements ? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors [no test files] -? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/dummy [no test files] -? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy [no test files] ? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers [no test files] -? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/diff [no test files] -? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/sandbox [no test files] -? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/resultstatus [no test files] -? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage [no test files] -? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/dummy [no test files] ? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/healthcheck [no test files] + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/sandbox coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/dummy coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/diff coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/dummy coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/resultstatus coverage: 0.0% of statements === RUN TestMain -=== RUN TestMain/success - main_test.go:96: stageResults: [{Name:compile Results:[{Score:0 Comment:}]} {Name:run Results:[{Score:100 Comment:executor status: run time: 1910200 ns, memory: 13529088 bytes} {Score:100 Comment:executor status: run time: 1703000 ns, memory: 15536128 bytes}]}] -=== RUN TestMain/compile_error - main_test.go:96: stageResults: [{Name:compile Results:[{Score:0 Comment:Unexpected executor status: Nonzero Exit Status.}]}] -=== RUN TestMain/dummy - main_test.go:96: stageResults: [{Name:dummy Results:[{Score:110 Comment:dummy comment + comment from toml conf}]}] -=== RUN TestMain/dummy_error - main_test.go:96: stageResults: [{Name:dummy Results:[{Score:0 Comment:Unexpected executor status: Nonzero Exit Status. +=== RUN TestMain/compile/success + main_test.go:101: stageResults: [{Name:compile Results:[{Score:0 Comment:}]} {Name:run Results:[{Score:100 Comment:executor status: run time: 1867950 ns, memory: 10813440 bytes} {Score:100 Comment:executor status: run time: 1948947 ns, memory: 10813440 bytes}]}] +=== RUN TestMain/compile/error + main_test.go:101: stageResults: [{Name:compile Results:[{Score:0 Comment:Unexpected executor status: Nonzero Exit Status.}]}] +=== RUN TestMain/dummy/success + main_test.go:101: stageResults: [{Name:dummy Results:[{Score:110 Comment:dummy comment + comment from toml conf}]}] +=== RUN TestMain/dummy/error + main_test.go:101: stageResults: [{Name:dummy Results:[{Score:0 Comment:Unexpected executor status: Nonzero Exit Status. Stderr: dummy negative score: -1}]}] ---- PASS: TestMain (0.29s) - --- PASS: TestMain/success (0.27s) - --- PASS: TestMain/compile_error (0.01s) - --- PASS: TestMain/dummy (0.01s) - --- PASS: TestMain/dummy_error (0.01s) +--- PASS: TestMain (0.39s) + --- PASS: TestMain/compile/success (0.36s) + --- PASS: TestMain/compile/error (0.01s) + --- PASS: TestMain/dummy/success (0.02s) + --- PASS: TestMain/dummy/error (0.01s) PASS -ok focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/joj3 0.295s +coverage: 68.5% of statements +ok focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/joj3 0.403s coverage: 68.5% of statements ``` ### For developers From 8f3804543a8467a4b3e6a05aaf362a5bde476786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=B3=8A=E6=98=8E518370910136?= Date: Thu, 4 Apr 2024 01:04:20 +0800 Subject: [PATCH 05/14] docs: detailed startup guide --- README.md | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f7492f0..3f5d049 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,27 @@ ## Quick Start -To register the sandbox executor, you need to run [go-judge](https://github.com/criyle/go-judge) before running this program. +1. Make sure you are in a Unix-like OS (Linux, MacOS). For Windows, use [WSL 2](https://learn.microsoft.com/en-us/windows/wsl/install). -**Hint for `go-judge`:** `go build -o ./tmp/go-judge ./cmd/go-judge && ./tmp/go-judge -enable-grpc -enable-debug -enable-metrics` +2. Install [Go](https://go.dev/doc/install). Also make sure `make` and `git` are installed and all 3 programs are presented in `$PATH`. -Then you can check the functions of `joj3` with the `make test`. The cases used here are in `/examples`. +3. Enable cgroups v2 for your OS. Check [here](https://stackoverflow.com/a/73376219/13724598). So that you do not need root permission to run `go-judge`. +4. Clone [go-judge](https://github.com/criyle/go-judge). ```bash +$ git clone https://github.com/criyle/go-judge && cd go-judge +$ go build -o ./tmp/go-judge ./cmd/go-judge +``` + +5. Run `go-judge`. +```bash +$ # make sure you are in go-judge directory +$ ./tmp/go-judge -enable-grpc -enable-debug -enable-metrics +``` + +6. Check the functions of `joj3` with the `make test`, which should pass all the test cases. The cases used here are in `/examples`. +```bash +$ # make sure you are in JOJ3 directory $ make test go test -coverprofile cover.out -v ./... focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/dummy coverage: 0.0% of statements @@ -44,15 +58,16 @@ ok focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/joj3 0.403s coverage: 68.5% ### For developers -Install [`pre-commit`](https://pre-commit.com/), [`golangci-lint`](https://golangci-lint.run), [`goimports`](https://golang.org/x/tools/cmd/goimports), [`gofumpt`](https://github.com/mvdan/gofumpt). - -Then install the pre-commit hooks. It will run some checks before you commit. +1. Install [`pre-commit`](https://pre-commit.com/), [`golangci-lint`](https://golangci-lint.run), [`goimports`](https://golang.org/x/tools/cmd/goimports), [`gofumpt`](https://github.com/mvdan/gofumpt). +2. Install the pre-commit hooks. It will run some checks before you commit. ```bash $ pre-commit install pre-commit installed at .git/hooks/pre-commit ``` +3. You only need to run step 5 and 6 in quick start during development. + ## Models The program parses the configuration file to run multiple stages. It can create an issue on Gitea to report the result of each stage after all stages are done. From 0bf349c03286be2d22a889dea68256410b4b1fd4 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Wed, 3 Apr 2024 18:08:34 -0400 Subject: [PATCH 06/14] fix: json field case --- internal/stage/model.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/stage/model.go b/internal/stage/model.go index 848e4ec..1b052d8 100644 --- a/internal/stage/model.go +++ b/internal/stage/model.go @@ -159,11 +159,11 @@ type Stage struct { } type ParserResult struct { - Score int - Comment string + Score int `json:"score"` + Comment string `json:"comment"` } type StageResult struct { - Name string - Results []ParserResult + Name string `json:"name"` + Results []ParserResult `json:"results"` } From 51539c1e7c13d5af86704e9ce31c945fc1d378b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=B3=8A=E6=98=8E518370910136?= Date: Thu, 4 Apr 2024 07:29:00 +0800 Subject: [PATCH 07/14] feat: cpplint parser (#22) Co-authored-by: Boming Zhang Reviewed-on: https://focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pulls/22 --- .drone.yml | 1 + .gitmodules | 20 +++++ Makefile | 3 + README.md | 41 ++++++---- cmd/joj3/conf.go | 2 +- cmd/joj3/main.go | 11 +-- cmd/joj3/main_test.go | 112 +++++++++++++-------------- examples/compile/error | 1 + examples/compile/error/conf.toml | 61 --------------- examples/compile/success | 1 + examples/compile/success/a.cc | 6 -- examples/compile/success/cases/1.in | 1 - examples/compile/success/cases/1.out | 1 - examples/compile/success/cases/2.in | 1 - examples/compile/success/cases/2.out | 1 - examples/compile/success/conf.toml | 60 -------------- examples/cpplint/sillycode | 1 + examples/dummy/error | 1 + examples/dummy/error/conf.toml | 28 ------- examples/dummy/success | 1 + examples/dummy/success/conf.toml | 28 ------- internal/parsers/all.go | 1 + internal/parsers/cpplint/meta.go | 9 +++ internal/parsers/cpplint/parser.go | 53 +++++++++++++ 24 files changed, 173 insertions(+), 272 deletions(-) create mode 100644 .gitmodules create mode 160000 examples/compile/error delete mode 100644 examples/compile/error/conf.toml create mode 160000 examples/compile/success delete mode 100644 examples/compile/success/a.cc delete mode 100644 examples/compile/success/cases/1.in delete mode 100644 examples/compile/success/cases/1.out delete mode 100644 examples/compile/success/cases/2.in delete mode 100644 examples/compile/success/cases/2.out delete mode 100644 examples/compile/success/conf.toml create mode 160000 examples/cpplint/sillycode create mode 160000 examples/dummy/error delete mode 100644 examples/dummy/error/conf.toml create mode 160000 examples/dummy/success delete mode 100644 examples/dummy/success/conf.toml create mode 100644 internal/parsers/cpplint/meta.go create mode 100644 internal/parsers/cpplint/parser.go diff --git a/.drone.yml b/.drone.yml index 685e2bf..4358d7d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -25,6 +25,7 @@ steps: CONF_GITEATOKEN: from_secret: gitea-token commands: + - make prepare-test - make test - name: store commands: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2d32453 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,20 @@ +[submodule "examples/cpplint/sillycode"] + path = examples/cpplint/sillycode + url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git + branch = cpplint/sillycode +[submodule "examples/compile/success"] + path = examples/compile/success + url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git + branch = compile/success +[submodule "examples/compile/error"] + path = examples/compile/error + url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git + branch = compile/error +[submodule "examples/dummy/success"] + path = examples/dummy/success + url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git + branch = dummy/success +[submodule "examples/dummy/error"] + path = examples/dummy/error + url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git + branch = dummy/error diff --git a/Makefile b/Makefile index 3008575..5dace32 100644 --- a/Makefile +++ b/Makefile @@ -11,5 +11,8 @@ clean: rm -rf $(BUILD_DIR)/* rm -rf *.out +prepare-test: + git submodule update --init --remote + test: go test -coverprofile cover.out -v ./... diff --git a/README.md b/README.md index 3f5d049..ab9e051 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ 1. Make sure you are in a Unix-like OS (Linux, MacOS). For Windows, use [WSL 2](https://learn.microsoft.com/en-us/windows/wsl/install). -2. Install [Go](https://go.dev/doc/install). Also make sure `make` and `git` are installed and all 3 programs are presented in `$PATH`. +2. Install [Go](https://go.dev/doc/install). Also, make sure `make` and `git` are installed and all 3 programs are presented in `$PATH`. -3. Enable cgroups v2 for your OS. Check [here](https://stackoverflow.com/a/73376219/13724598). So that you do not need root permission to run `go-judge`. +3. Enable cgroup v2 for your OS. Check [here](https://stackoverflow.com/a/73376219/13724598). So that you do not need root permission to run `go-judge`. 4. Clone [go-judge](https://github.com/criyle/go-judge). ```bash @@ -20,40 +20,47 @@ $ # make sure you are in go-judge directory $ ./tmp/go-judge -enable-grpc -enable-debug -enable-metrics ``` -6. Check the functions of `joj3` with the `make test`, which should pass all the test cases. The cases used here are in `/examples`. +6. Pull submodules. It might be slow, so only run it when necessary. + ```bash $ # make sure you are in JOJ3 directory +$ make prepare-test +``` + +7. Check the functions of `joj3` with the `make test`, which should pass all the test cases. The cases used here are in `/examples`. + +Note: you may fail the test if the checking tools are not installed. e.g. For the test case `cpplint/sillycode`, you need to install `cpplint` in `/usr/bin` or `/usr/local/bin`. + +```bash $ make test go test -coverprofile cover.out -v ./... focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/dummy coverage: 0.0% of statements ? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors [no test files] ? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers [no test files] ? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/healthcheck [no test files] - focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/sandbox coverage: 0.0% of statements focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/dummy coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/cpplint coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/sandbox coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage coverage: 0.0% of statements focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/diff coverage: 0.0% of statements focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/dummy coverage: 0.0% of statements - focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage coverage: 0.0% of statements - focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy coverage: 0.0% of statements focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/resultstatus coverage: 0.0% of statements === RUN TestMain === RUN TestMain/compile/success - main_test.go:101: stageResults: [{Name:compile Results:[{Score:0 Comment:}]} {Name:run Results:[{Score:100 Comment:executor status: run time: 1867950 ns, memory: 10813440 bytes} {Score:100 Comment:executor status: run time: 1948947 ns, memory: 10813440 bytes}]}] === RUN TestMain/compile/error - main_test.go:101: stageResults: [{Name:compile Results:[{Score:0 Comment:Unexpected executor status: Nonzero Exit Status.}]}] === RUN TestMain/dummy/success - main_test.go:101: stageResults: [{Name:dummy Results:[{Score:110 Comment:dummy comment + comment from toml conf}]}] === RUN TestMain/dummy/error - main_test.go:101: stageResults: [{Name:dummy Results:[{Score:0 Comment:Unexpected executor status: Nonzero Exit Status. - Stderr: dummy negative score: -1}]}] ---- PASS: TestMain (0.39s) - --- PASS: TestMain/compile/success (0.36s) +=== RUN TestMain/cpplint/sillycode +--- PASS: TestMain (0.42s) + --- PASS: TestMain/compile/success (0.28s) --- PASS: TestMain/compile/error (0.01s) - --- PASS: TestMain/dummy/success (0.02s) + --- PASS: TestMain/dummy/success (0.01s) --- PASS: TestMain/dummy/error (0.01s) + --- PASS: TestMain/cpplint/sillycode (0.11s) PASS -coverage: 68.5% of statements -ok focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/joj3 0.403s coverage: 68.5% of statements +coverage: 72.4% of statements +ok focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/joj3 0.425s coverage: 72.4% of statements ``` ### For developers @@ -66,7 +73,7 @@ $ pre-commit install pre-commit installed at .git/hooks/pre-commit ``` -3. You only need to run step 5 and 6 in quick start during development. +3. You only need to run steps 5 and 7 in the quick start during development. If the test cases need to be updated, step 6 is also needed. ## Models diff --git a/cmd/joj3/conf.go b/cmd/joj3/conf.go index ec576ad..ef87bbf 100644 --- a/cmd/joj3/conf.go +++ b/cmd/joj3/conf.go @@ -89,7 +89,7 @@ func commitMsgToConf() (conf Conf, err error) { return } msg := commit.Message - slog.Info("commit msg to conf", "msg", msg) + slog.Debug("commit msg to conf", "msg", msg) // TODO: parse msg to conf name conf = parseConfFile("conf.toml") return diff --git a/cmd/joj3/main.go b/cmd/joj3/main.go index 154d8cf..4348b73 100644 --- a/cmd/joj3/main.go +++ b/cmd/joj3/main.go @@ -2,7 +2,6 @@ package main import ( "encoding/json" - "errors" "log/slog" "os" @@ -10,7 +9,6 @@ import ( _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers" "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" - "github.com/go-git/go-git/v5" "github.com/jinzhu/copier" ) @@ -75,13 +73,8 @@ func outputResult(conf Conf, results []stage.StageResult) error { func main() { conf, err := commitMsgToConf() if err != nil { - // FIXME: just for local testing purpose - if errors.Is(err, git.ErrRepositoryNotExists) { - conf = parseConfFile("conf.toml") - } else { - slog.Error("no conf found", "error", err) - os.Exit(1) - } + slog.Error("no conf found", "error", err) + os.Exit(1) } setupSlog(conf) stages := generateStages(conf) diff --git a/cmd/joj3/main_test.go b/cmd/joj3/main_test.go index 39db646..05e78f3 100644 --- a/cmd/joj3/main_test.go +++ b/cmd/joj3/main_test.go @@ -10,72 +10,70 @@ import ( "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" ) -func compareStageResults(t *testing.T, actual, want []stage.StageResult) { +func compareStageResults(t *testing.T, actual, expected []stage.StageResult, regex bool) { t.Helper() - if len(actual) != len(want) { - t.Fatalf("len(actual) = %d, want %d", len(actual), len(want)) + if len(actual) != len(expected) { + t.Fatalf("len(actual) = %d, expected %d", len(actual), len(expected)) } for i := range actual { - if actual[i].Name != want[i].Name { - t.Errorf("actual[%d].Name = %s, want = %s", i, actual[i].Name, - want[i].Name) + if actual[i].Name != expected[i].Name { + t.Errorf("actual[%d].Name = %s, expected = %s", i, actual[i].Name, + expected[i].Name) } - if len(actual[i].Results) != len(want[i].Results) { - t.Fatalf("len(actual[%d].Results) = %d, want = %d", i, - len(actual[i].Results), len(want[i].Results)) + if len(actual[i].Results) != len(expected[i].Results) { + t.Fatalf("len(actual[%d].Results) = %d, expected = %d", i, + len(actual[i].Results), len(expected[i].Results)) } for j := range actual[i].Results { - if actual[i].Results[j].Score != want[i].Results[j].Score { - t.Errorf("actual[%d].Results[%d].Score = %d, want = %d", i, j, - actual[i].Results[j].Score, want[i].Results[j].Score) + if actual[i].Results[j].Score != expected[i].Results[j].Score { + t.Errorf("actual[%d].Results[%d].Score = %d, expected = %d", i, j, + actual[i].Results[j].Score, expected[i].Results[j].Score) } - r := regexp.MustCompile(want[i].Results[j].Comment) - if !r.MatchString(actual[i].Results[j].Comment) { - t.Errorf("actual[%d].Results[%d].Comment = %s, want RegExp = %s", - i, j, actual[i].Results[j].Comment, - want[i].Results[j].Comment) + if regex { + r := regexp.MustCompile(expected[i].Results[j].Comment) + if !r.MatchString(actual[i].Results[j].Comment) { + t.Errorf("actual[%d].Results[%d].Comment = %s, expected RegExp = %s", + i, j, actual[i].Results[j].Comment, + expected[i].Results[j].Comment) + } + } else if actual[i].Results[j].Comment != expected[i].Results[j].Comment { + t.Errorf("actual[%d].Results[%d].Comment = %s, expected = %s", i, j, + actual[i].Results[j].Comment, expected[i].Results[j].Comment) } } } } +func readStageResults(t *testing.T, path string) []stage.StageResult { + t.Helper() + file, err := os.Open(path) + if err != nil { + t.Fatal(err) + } + defer file.Close() + var results []stage.StageResult + err = json.NewDecoder(file).Decode(&results) + if err != nil { + t.Fatal(err) + } + return results +} + func TestMain(t *testing.T) { - tests := []struct { - name string - want []stage.StageResult - }{ - {"compile/success", []stage.StageResult{ - {Name: "compile", Results: []stage.ParserResult{ - {Score: 0, Comment: ""}, - }}, - {Name: "run", Results: []stage.ParserResult{ - {Score: 100, Comment: "executor status: run time: \\d+ ns, memory: \\d+ bytes"}, - {Score: 100, Comment: "executor status: run time: \\d+ ns, memory: \\d+ bytes"}, - }}, - }}, - {"compile/error", []stage.StageResult{ - {Name: "compile", Results: []stage.ParserResult{ - {Score: 0, Comment: "Unexpected executor status: Nonzero Exit Status\\."}, - }}, - }}, - {"dummy/success", []stage.StageResult{ - {Name: "dummy", Results: []stage.ParserResult{ - {Score: 110, Comment: "dummy comment \\+ comment from toml conf"}, - }}, - }}, - {"dummy/error", []stage.StageResult{ - {Name: "dummy", Results: []stage.ParserResult{ - {Score: 0, Comment: "Unexpected executor status: Nonzero Exit Status\\.\\s*Stderr: dummy negative score: -1"}, - }}, - }}, + tests := []string{ + "compile/success", + "compile/error", + "dummy/success", + "dummy/error", + "cpplint/sillycode", } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { + t.Run(tt, func(t *testing.T) { origDir, err := os.Getwd() if err != nil { t.Fatal(err) } - err = os.Chdir(fmt.Sprintf("../../examples/%s", tt.name)) + err = os.Chdir(fmt.Sprintf("../../examples/%s", tt)) if err != nil { t.Fatal(err) } @@ -86,20 +84,18 @@ func TestMain(t *testing.T) { } }() os.Args = []string{"./joj3"} - main() outputFile := "joj3_result.json" - data, err := os.ReadFile(outputFile) - if err != nil { - t.Fatal(err) - } defer os.Remove(outputFile) - var stageResults []stage.StageResult - err = json.Unmarshal(data, &stageResults) - if err != nil { - t.Fatal(err) + main() + stageResults := readStageResults(t, outputFile) + regex := true + expectedFile := "expected_regex.json" + if _, err := os.Stat(expectedFile); os.IsNotExist(err) { + regex = false + expectedFile = "expected.json" } - t.Logf("stageResults: %+v", stageResults) - compareStageResults(t, stageResults, tt.want) + expectedStageResults := readStageResults(t, expectedFile) + compareStageResults(t, stageResults, expectedStageResults, regex) }) } } diff --git a/examples/compile/error b/examples/compile/error new file mode 160000 index 0000000..dc7be30 --- /dev/null +++ b/examples/compile/error @@ -0,0 +1 @@ +Subproject commit dc7be30db1fbfd83ecb78f214cff3f42732a1365 diff --git a/examples/compile/error/conf.toml b/examples/compile/error/conf.toml deleted file mode 100644 index a52ff88..0000000 --- a/examples/compile/error/conf.toml +++ /dev/null @@ -1,61 +0,0 @@ -skipGitea = true -logLevel = 0 -[[stages]] -name = "compile" -[stages.executor] -name = "sandbox" -[stages.executor.with.default] -args = ["g++", "b.cc", "-o", "a"] -env = ["PATH=/usr/bin:/bin"] -cpuLimit = 10_000_000_000 -memoryLimit = 104_857_600 -procLimit = 50 -copyInCwd = true -copyOut = ["stdout", "stderr"] -copyOutCached = ["a"] -[stages.executor.with.default.stdin] -content = "" -[stages.executor.with.default.stdout] -name = "stdout" -max = 4_096 -[stages.executor.with.default.stderr] -name = "stderr" -max = 4_096 -[stages.parser] -name = "result-status" -[stages.parser.with] -score = 100 -comment = "compile done" -[[stages]] -name = "run" -[stages.executor] -name = "sandbox" -[stages.executor.with.default] -args = ["./a"] -env = ["PATH=/usr/bin:/bin"] -cpuLimit = 1_000_000_000 -memoryLimit = 104_857_600 -procLimit = 50 -copyOut = ["stdout", "stderr"] -[stages.executor.with.default.stdout] -name = "stdout" -max = 4_096 -[stages.executor.with.default.stderr] -name = "stderr" -max = 4_096 -[stages.executor.with.default.copyInCached] -a = "a" -[[stages.executor.with.cases]] -[stages.executor.with.cases.stdin] -src = "./cases/1.in" -[[stages.executor.with.cases]] -[stages.executor.with.cases.stdin] -src = "./cases/2.in" -[stages.parser] -name = "diff" -[[stages.parser.with.cases]] -score = 100 -stdoutPath = "./cases/1.out" -[[stages.parser.with.cases]] -score = 100 -stdoutPath = "./cases/2.out" diff --git a/examples/compile/success b/examples/compile/success new file mode 160000 index 0000000..21f6df7 --- /dev/null +++ b/examples/compile/success @@ -0,0 +1 @@ +Subproject commit 21f6df70d483f34a99b50335e0a1fbe50d3a82dd diff --git a/examples/compile/success/a.cc b/examples/compile/success/a.cc deleted file mode 100644 index 2c7ca74..0000000 --- a/examples/compile/success/a.cc +++ /dev/null @@ -1,6 +0,0 @@ -#include -int main() { - int a, b; - std::cin >> a >> b; - std::cout << a + b << '\n'; -} diff --git a/examples/compile/success/cases/1.in b/examples/compile/success/cases/1.in deleted file mode 100644 index 2fb73a0..0000000 --- a/examples/compile/success/cases/1.in +++ /dev/null @@ -1 +0,0 @@ -1 1 diff --git a/examples/compile/success/cases/1.out b/examples/compile/success/cases/1.out deleted file mode 100644 index 0cfbf08..0000000 --- a/examples/compile/success/cases/1.out +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/examples/compile/success/cases/2.in b/examples/compile/success/cases/2.in deleted file mode 100644 index b5f1e36..0000000 --- a/examples/compile/success/cases/2.in +++ /dev/null @@ -1 +0,0 @@ -1024 2048 diff --git a/examples/compile/success/cases/2.out b/examples/compile/success/cases/2.out deleted file mode 100644 index 5fd86fd..0000000 --- a/examples/compile/success/cases/2.out +++ /dev/null @@ -1 +0,0 @@ -3072 diff --git a/examples/compile/success/conf.toml b/examples/compile/success/conf.toml deleted file mode 100644 index e91739a..0000000 --- a/examples/compile/success/conf.toml +++ /dev/null @@ -1,60 +0,0 @@ -skipGitea = true -[[stages]] -name = "compile" -[stages.executor] -name = "sandbox" -[stages.executor.with.default] -args = ["g++", "a.cc", "-o", "a"] -env = ["PATH=/usr/bin:/bin"] -cpuLimit = 10_000_000_000 -memoryLimit = 104_857_600 -procLimit = 50 -copyInCwd = true -copyOut = ["stdout", "stderr"] -copyOutCached = ["a"] -[stages.executor.with.default.stdin] -content = "" -[stages.executor.with.default.stdout] -name = "stdout" -max = 4_096 -[stages.executor.with.default.stderr] -name = "stderr" -max = 4_096 -[stages.parser] -name = "result-status" -[stages.parser.with] -score = 100 -comment = "compile done" -[[stages]] -name = "run" -[stages.executor] -name = "sandbox" -[stages.executor.with.default] -args = ["./a"] -env = ["PATH=/usr/bin:/bin"] -cpuLimit = 1_000_000_000 -memoryLimit = 104_857_600 -procLimit = 50 -copyOut = ["stdout", "stderr"] -[stages.executor.with.default.stdout] -name = "stdout" -max = 4_096 -[stages.executor.with.default.stderr] -name = "stderr" -max = 4_096 -[stages.executor.with.default.copyInCached] -a = "a" -[[stages.executor.with.cases]] -[stages.executor.with.cases.stdin] -src = "./cases/1.in" -[[stages.executor.with.cases]] -[stages.executor.with.cases.stdin] -src = "./cases/2.in" -[stages.parser] -name = "diff" -[[stages.parser.with.cases]] -score = 100 -stdoutPath = "./cases/1.out" -[[stages.parser.with.cases]] -score = 100 -stdoutPath = "./cases/2.out" diff --git a/examples/cpplint/sillycode b/examples/cpplint/sillycode new file mode 160000 index 0000000..e650457 --- /dev/null +++ b/examples/cpplint/sillycode @@ -0,0 +1 @@ +Subproject commit e6504575379ef49c1495dc635f7aba36f57dddc8 diff --git a/examples/dummy/error b/examples/dummy/error new file mode 160000 index 0000000..3ebebbc --- /dev/null +++ b/examples/dummy/error @@ -0,0 +1 @@ +Subproject commit 3ebebbc913534fb4c4598d3ed313d1bb3ff29020 diff --git a/examples/dummy/error/conf.toml b/examples/dummy/error/conf.toml deleted file mode 100644 index 7648609..0000000 --- a/examples/dummy/error/conf.toml +++ /dev/null @@ -1,28 +0,0 @@ -skipGitea = true -[[stages]] -name = "dummy" -[stages.executor] -name = "sandbox" -[stages.executor.with.default] -args = ["./dummy", "--score", "-1"] -env = ["PATH=/usr/bin:/bin"] -cpuLimit = 10_000_000_000 -memoryLimit = 104_857_600 -procLimit = 50 -copyInCwd = true -[stages.executor.with.default.copyIn.dummy] -src = "./../../../build/dummy" -copyOut = ["stdout", "stderr"] -[stages.executor.with.default.stdin] -content = "" -[stages.executor.with.default.stdout] -name = "stdout" -max = 4_096 -[stages.executor.with.default.stderr] -name = "stderr" -max = 4_096 -[stages.parser] -name = "dummy" -[stages.parser.with] -score = 10 -comment = " + comment from toml conf" diff --git a/examples/dummy/success b/examples/dummy/success new file mode 160000 index 0000000..4bca6f0 --- /dev/null +++ b/examples/dummy/success @@ -0,0 +1 @@ +Subproject commit 4bca6f0e4b5e263532b8410afecca615e90524e9 diff --git a/examples/dummy/success/conf.toml b/examples/dummy/success/conf.toml deleted file mode 100644 index 7fef08c..0000000 --- a/examples/dummy/success/conf.toml +++ /dev/null @@ -1,28 +0,0 @@ -skipGitea = true -[[stages]] -name = "dummy" -[stages.executor] -name = "sandbox" -[stages.executor.with.default] -args = ["./dummy", "--score", "100"] -env = ["PATH=/usr/bin:/bin"] -cpuLimit = 10_000_000_000 -memoryLimit = 104_857_600 -procLimit = 50 -copyInCwd = true -[stages.executor.with.default.copyIn.dummy] -src = "./../../../build/dummy" -copyOut = ["stdout", "stderr"] -[stages.executor.with.default.stdin] -content = "" -[stages.executor.with.default.stdout] -name = "stdout" -max = 4_096 -[stages.executor.with.default.stderr] -name = "stderr" -max = 4_096 -[stages.parser] -name = "dummy" -[stages.parser.with] -score = 10 -comment = " + comment from toml conf" diff --git a/internal/parsers/all.go b/internal/parsers/all.go index ecc658c..d305bd6 100644 --- a/internal/parsers/all.go +++ b/internal/parsers/all.go @@ -1,6 +1,7 @@ package parsers import ( + _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/cpplint" _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/diff" _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy" _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/resultstatus" diff --git a/internal/parsers/cpplint/meta.go b/internal/parsers/cpplint/meta.go new file mode 100644 index 0000000..33b5188 --- /dev/null +++ b/internal/parsers/cpplint/meta.go @@ -0,0 +1,9 @@ +package cpplint + +import "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" + +var name = "cpplint" + +func init() { + stage.RegisterParser(name, &Cpplint{}) +} diff --git a/internal/parsers/cpplint/parser.go b/internal/parsers/cpplint/parser.go new file mode 100644 index 0000000..55a7f60 --- /dev/null +++ b/internal/parsers/cpplint/parser.go @@ -0,0 +1,53 @@ +package cpplint + +import ( + "fmt" + "regexp" + "strconv" + + "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" +) + +type Conf struct { + Score int +} + +type Cpplint struct{} + +func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult { + stderr := executorResult.Files["stderr"] + pattern := `(.+):(\d+): (.+) \[(.+)\] \[(\d)]\n` + re := regexp.MustCompile(pattern) + matches := re.FindAllStringSubmatch(stderr, -1) + score := 0 + comment := "" + for _, match := range matches { + fileName := match[1] + lineNum, _ := strconv.Atoi(match[2]) + message := match[3] + category := match[4] + confidence, _ := strconv.Atoi(match[5]) + score -= confidence + // TODO: add more detailed comment, just re-assemble for now + comment += fmt.Sprintf("%s:%d: %s [%s] [%d]\n", + fileName, lineNum, message, category, confidence) + } + return stage.ParserResult{ + Score: score, + Comment: comment, + } +} + +func (*Cpplint) Run(results []stage.ExecutorResult, confAny any) ( + []stage.ParserResult, bool, error, +) { + conf, err := stage.DecodeConf[Conf](confAny) + if err != nil { + return nil, true, err + } + var res []stage.ParserResult + for _, result := range results { + res = append(res, Parse(result, *conf)) + } + return res, false, nil +} From c53082283a5a97f33d820ae128bf8858645182ff Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Wed, 3 Apr 2024 19:33:36 -0400 Subject: [PATCH 08/14] docs: run `make` before `make test` --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ab9e051..3997d51 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,13 @@ $ # make sure you are in JOJ3 directory $ make prepare-test ``` -7. Check the functions of `joj3` with the `make test`, which should pass all the test cases. The cases used here are in `/examples`. +7. Build binaries in `/cmd`. + +```bash +$ make +``` + +8. Check the functions of `joj3` with the `make test`, which should pass all the test cases. The cases used here are in `/examples`. Note: you may fail the test if the checking tools are not installed. e.g. For the test case `cpplint/sillycode`, you need to install `cpplint` in `/usr/bin` or `/usr/local/bin`. @@ -73,7 +79,7 @@ $ pre-commit install pre-commit installed at .git/hooks/pre-commit ``` -3. You only need to run steps 5 and 7 in the quick start during development. If the test cases need to be updated, step 6 is also needed. +3. You only need to run steps 5, 7, and 8 in the quick start during development. If the test cases need to be updated, step 6 is also needed. ## Models From 15b8b34476f3c386fa1ffdeee8c694bd5f39578e Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Thu, 11 Apr 2024 13:19:38 -0400 Subject: [PATCH 09/14] feat: auto detect tests in exampls --- cmd/joj3/main_test.go | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/cmd/joj3/main_test.go b/cmd/joj3/main_test.go index 05e78f3..2169197 100644 --- a/cmd/joj3/main_test.go +++ b/cmd/joj3/main_test.go @@ -4,7 +4,9 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "regexp" + "strings" "testing" "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" @@ -60,12 +62,29 @@ func readStageResults(t *testing.T, path string) []stage.StageResult { } func TestMain(t *testing.T) { - tests := []string{ - "compile/success", - "compile/error", - "dummy/success", - "dummy/error", - "cpplint/sillycode", + var tests []string + root := "../../examples/" + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + if path == root { + return nil + } + path0 := filepath.Join(path, "expected_regex.json") + path1 := filepath.Join(path, "expected.json") + _, err0 := os.Stat(path0) + _, err1 := os.Stat(path1) + if err0 != nil && err1 != nil { + return nil + } + tests = append(tests, strings.TrimPrefix(path, root)) + } + return nil + }) + if err != nil { + t.Fatal(err) } for _, tt := range tests { t.Run(tt, func(t *testing.T) { @@ -73,7 +92,7 @@ func TestMain(t *testing.T) { if err != nil { t.Fatal(err) } - err = os.Chdir(fmt.Sprintf("../../examples/%s", tt)) + err = os.Chdir(fmt.Sprintf("%s%s", root, tt)) if err != nil { t.Fatal(err) } From b9957a9965b99e2de96108480dddb11704d48a12 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Fri, 26 Apr 2024 17:18:35 -0400 Subject: [PATCH 10/14] ci: show git status --- .drone.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.drone.yml b/.drone.yml index 4358d7d..b566a29 100644 --- a/.drone.yml +++ b/.drone.yml @@ -17,6 +17,8 @@ steps: - env - go version - go env + - git status -v + - git log -1 - name: build commands: - make From 2854fd7c915aa1f0bde6c57d87bb1a4efad97110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=B3=8A=E6=98=8E518370910136?= Date: Sun, 28 Apr 2024 03:52:07 +0800 Subject: [PATCH 11/14] feat: keyword parser (#23) (#24) Reviewed-on: https://focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pulls/24 --- .gitmodules | 4 ++ examples/cpplint/sillycode | 2 +- examples/keyword/sillycode | 1 + internal/parsers/all.go | 1 + internal/parsers/keyword/meta.go | 9 ++++ internal/parsers/keyword/parser.go | 67 ++++++++++++++++++++++++++++++ 6 files changed, 83 insertions(+), 1 deletion(-) create mode 160000 examples/keyword/sillycode create mode 100644 internal/parsers/keyword/meta.go create mode 100644 internal/parsers/keyword/parser.go diff --git a/.gitmodules b/.gitmodules index 2d32453..5691987 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,3 +18,7 @@ path = examples/dummy/error url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git branch = dummy/error +[submodule "examples/keyword/sillycode"] + path = examples/keyword/sillycode + url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git + branch = keyword/sillycode diff --git a/examples/cpplint/sillycode b/examples/cpplint/sillycode index e650457..a700156 160000 --- a/examples/cpplint/sillycode +++ b/examples/cpplint/sillycode @@ -1 +1 @@ -Subproject commit e6504575379ef49c1495dc635f7aba36f57dddc8 +Subproject commit a7001564a22f9807119efb7b8f4cf6f74d4c12fc diff --git a/examples/keyword/sillycode b/examples/keyword/sillycode new file mode 160000 index 0000000..09cfded --- /dev/null +++ b/examples/keyword/sillycode @@ -0,0 +1 @@ +Subproject commit 09cfdedda32061a03b4335e9a0f162c7891301c9 diff --git a/internal/parsers/all.go b/internal/parsers/all.go index d305bd6..c687743 100644 --- a/internal/parsers/all.go +++ b/internal/parsers/all.go @@ -4,6 +4,7 @@ import ( _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/cpplint" _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/diff" _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy" + _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/keyword" _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/resultstatus" ) diff --git a/internal/parsers/keyword/meta.go b/internal/parsers/keyword/meta.go new file mode 100644 index 0000000..fd2dccd --- /dev/null +++ b/internal/parsers/keyword/meta.go @@ -0,0 +1,9 @@ +package keyword + +import "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" + +var name = "keyword" + +func init() { + stage.RegisterParser(name, &Keyword{}) +} diff --git a/internal/parsers/keyword/parser.go b/internal/parsers/keyword/parser.go new file mode 100644 index 0000000..ccf0c0f --- /dev/null +++ b/internal/parsers/keyword/parser.go @@ -0,0 +1,67 @@ +package keyword + +import ( + "fmt" + "strings" + + "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" +) + +type Match struct { + Keyword string + Score int +} + +type Conf struct { + FullScore int + MinScore int + Files []string + EndOnMatch bool + Matches []Match +} + +type Keyword struct{} + +func Parse(executorResult stage.ExecutorResult, conf Conf) ( + stage.ParserResult, bool, +) { + score := conf.FullScore + comment := "" + matched := false + for _, file := range conf.Files { + content := executorResult.Files[file] + for _, match := range conf.Matches { + count := strings.Count(content, match.Keyword) + if count > 0 { + matched = true + score -= count * match.Score + comment += fmt.Sprintf( + "Matched keyword %d time(s): %s\n", + count, match.Keyword) + } + } + } + return stage.ParserResult{ + Score: max(score, conf.MinScore), + Comment: comment, + }, matched +} + +func (*Keyword) Run(results []stage.ExecutorResult, confAny any) ( + []stage.ParserResult, bool, error, +) { + conf, err := stage.DecodeConf[Conf](confAny) + if err != nil { + return nil, true, err + } + var res []stage.ParserResult + end := false + for _, result := range results { + tmp, matched := Parse(result, *conf) + if matched && conf.EndOnMatch { + end = true + } + res = append(res, tmp) + } + return res, end, nil +} From 5aefc46ba8b79c4e2a111b2aca37e736a30dbd04 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Sat, 27 Apr 2024 16:41:45 -0400 Subject: [PATCH 12/14] chore: update submodule --- examples/keyword/sillycode | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/keyword/sillycode b/examples/keyword/sillycode index 09cfded..f43b0b3 160000 --- a/examples/keyword/sillycode +++ b/examples/keyword/sillycode @@ -1 +1 @@ -Subproject commit 09cfdedda32061a03b4335e9a0f162c7891301c9 +Subproject commit f43b0b3a7b873ee935b19e4e5f26a8ceda7d3d61 From 39a720afa2c771c483388421a340b6f004d78b93 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Thu, 2 May 2024 19:07:33 -0400 Subject: [PATCH 13/14] chore: rename examples --- .gitmodules | 6 +++--- examples/keyword/{ => cpplint}/sillycode | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename examples/keyword/{ => cpplint}/sillycode (100%) diff --git a/.gitmodules b/.gitmodules index 5691987..0582607 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,7 +18,7 @@ path = examples/dummy/error url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git branch = dummy/error -[submodule "examples/keyword/sillycode"] - path = examples/keyword/sillycode +[submodule "examples/keyword/cpplint/sillycode"] + path = examples/keyword/cpplint/sillycode url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git - branch = keyword/sillycode + branch = keyword/cpplint/sillycode diff --git a/examples/keyword/sillycode b/examples/keyword/cpplint/sillycode similarity index 100% rename from examples/keyword/sillycode rename to examples/keyword/cpplint/sillycode From 16a59a7fc2ce9d4b9d3796461bb7e0aa9c7236e2 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Thu, 2 May 2024 20:30:57 -0400 Subject: [PATCH 14/14] chore: add examples/keyword/clang-tidy --- .gitmodules | 4 ++++ README.md | 31 ++++++++++++++++----------- examples/keyword/clang-tidy/sillycode | 1 + 3 files changed, 23 insertions(+), 13 deletions(-) create mode 160000 examples/keyword/clang-tidy/sillycode diff --git a/.gitmodules b/.gitmodules index 0582607..13637d8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,7 @@ path = examples/keyword/cpplint/sillycode url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git branch = keyword/cpplint/sillycode +[submodule "examples/keyword/clang-tidy/sillycode"] + path = examples/keyword/clang-tidy/sillycode + url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git + branch = keyword/clang-tidy/sillycode diff --git a/README.md b/README.md index 3997d51..5a27e7a 100644 --- a/README.md +++ b/README.md @@ -46,27 +46,32 @@ go test -coverprofile cover.out -v ./... ? focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/healthcheck [no test files] focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/dummy coverage: 0.0% of statements focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/cpplint coverage: 0.0% of statements - focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/sandbox coverage: 0.0% of statements - focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy coverage: 0.0% of statements - focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage coverage: 0.0% of statements focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/diff coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage coverage: 0.0% of statements focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/dummy coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/keyword coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy coverage: 0.0% of statements + focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/sandbox coverage: 0.0% of statements focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/resultstatus coverage: 0.0% of statements === RUN TestMain -=== RUN TestMain/compile/success === RUN TestMain/compile/error -=== RUN TestMain/dummy/success -=== RUN TestMain/dummy/error +=== RUN TestMain/compile/success === RUN TestMain/cpplint/sillycode ---- PASS: TestMain (0.42s) - --- PASS: TestMain/compile/success (0.28s) - --- PASS: TestMain/compile/error (0.01s) - --- PASS: TestMain/dummy/success (0.01s) +=== RUN TestMain/dummy/error +=== RUN TestMain/dummy/success +=== RUN TestMain/keyword/clang-tidy/sillycode +=== RUN TestMain/keyword/cpplint/sillycode +--- PASS: TestMain (2.28s) + --- PASS: TestMain/compile/error (0.03s) + --- PASS: TestMain/compile/success (0.42s) + --- PASS: TestMain/cpplint/sillycode (0.14s) --- PASS: TestMain/dummy/error (0.01s) - --- PASS: TestMain/cpplint/sillycode (0.11s) + --- PASS: TestMain/dummy/success (0.01s) + --- PASS: TestMain/keyword/clang-tidy/sillycode (1.57s) + --- PASS: TestMain/keyword/cpplint/sillycode (0.11s) PASS -coverage: 72.4% of statements -ok focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/joj3 0.425s coverage: 72.4% of statements +coverage: 74.0% of statements +ok focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/cmd/joj3 2.290s coverage: 74.0% of statements ``` ### For developers diff --git a/examples/keyword/clang-tidy/sillycode b/examples/keyword/clang-tidy/sillycode new file mode 160000 index 0000000..51160e0 --- /dev/null +++ b/examples/keyword/clang-tidy/sillycode @@ -0,0 +1 @@ +Subproject commit 51160e0a0cb01159fa264435865185083d756b06