-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Julia #126
Julia #126
Conversation
Thanks for your contribution, @rystidia! Can I also ask to take one step further and Julia testing runs in Docker? This way:
It should be relatively straightforward to create a Julia image in kaitai_struct_docker_images, following the pattern of existing images. |
I created this PR (which was merged). It worked for me when running like |
Nope, sorry, please disregard my previous comment — I've just overlooked that you've already created it. Now it's merged and this enables to test tests locally. |
@@ -0,0 +1,10 @@ | |||
#!/bin/bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only run-*
script with the #!/bin/bash
shebang - all the other run-*
scripts use mostly #!/bin/sh -ef
. I don't see any reason to use #!/bin/bash
here, given that the script apparently doesn't shouldn't have to use any bashisms. Using #!/bin/sh -ef
instead will make it more portable, for example it will then work on Alpine Linux that doesn't have bash
by default.
Edit: oh, source
is actually a bashism, as ShellCheck points out:
Line 4:
source ./config
^-- SC3046 (warning): In POSIX sh, 'source' in place of '.' is undefined.
Well, that's another good reason to use .
instead of source
, as I suggest below.
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double newline here:
#!/bin/bash | ||
|
||
|
||
source ./config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use
source ./config | |
. ./config |
everywhere.
# Define the command to run the Julia script with redirected output | ||
command="JULIA_LOAD_PATH=\"$JULIA_LOAD_PATH:$JULIA_RUNTIME_DIR:compiled/julia:spec/julia/extra\" julia \"spec/julia/runtests.jl\"" | ||
|
||
# Run the command | ||
eval $command |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any reason to use eval
here. It makes everything ugly by having to escape the double quotes as \"
, and given that the $command
in eval $command
is unquoted, it is subject to all the globbing and word splitting as described in SC2086, which in this case means that JULIA_LOAD_PATH
and JULIA_RUNTIME_DIR
must not contain any spaces or glob characters, otherwise the script might break.
Why not just:
# Define the command to run the Julia script with redirected output | |
command="JULIA_LOAD_PATH=\"$JULIA_LOAD_PATH:$JULIA_RUNTIME_DIR:compiled/julia:spec/julia/extra\" julia \"spec/julia/runtests.jl\"" | |
# Run the command | |
eval $command | |
JULIA_LOAD_PATH="$JULIA_LOAD_PATH:$JULIA_RUNTIME_DIR:compiled/julia:spec/julia/extra" julia spec/julia/runtests.jl |
Also, I don't see the value of trivial comments such as Define the command to run the Julia script with redirected output
(and I don't think this one actually makes sense - what exactly does "with redirected output" refer to? - I don't see any |
, >
or >>
after the command, so it doesn't look like the output is redirected anywhere).
@@ -71,6 +74,15 @@ def each_test | |||
|
|||
tr = TestResult.new(name, :skipped, 0, nil) | |||
yield tr | |||
elsif ts.attribute('errors') && ts.attribute('errors').value.to_f != 0 && ts.attribute('name').value =~ /^\/(.*?) test$/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parsing the errors
attribute as a float (.to_f
) doesn't make sense. It should be parsed as an integer, preferably using the strictest way possible, which would be Integer(..., 10)
(see https://stackoverflow.com/a/49282/12940655).
Alternatively, we might not need to parse it at all, but instead can compare it directly to the string representation like ts.attribute('errors').value != '0'
(this is even stricter, but all we really need, since there shouldn't be any other representations of zero anyway).
function run_tests() | ||
#nested testset is needed to continue even after some test errors | ||
@testset "" begin | ||
for f in filter(s -> contains(s, r".jl$") && !contains(s, "runtests.jl"), readdir(@__DIR__; join=true)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
This:
contains(s, r".jl$")
... is an unnecessary use of a regex, which actually comes with a subtle bug - the dot
.
doesn't mean "literal dot" in this context, but "arbitrary character":julia> print(contains("hjl", r".jl$")) true
This demonstrates that it is generally best to use the "least powerful" tool for the job - in this case
endswith
:julia> print(endswith("hjl", ".jl")) false julia> print(endswith("h.jl", ".jl")) true
Simpler tools tend to have less surprises.
-
To me, this is sloppy:
!contains(s, "runtests.jl")
This tests whether the string
"runtests.jl"
is contained anywhere in the (absolute) file path, which is not really what we want (there's no reason to matchhelloruntests.jl
or directorieshelloruntests.jl/...
, etc.). What we actually want is to take the basename of that path and test it for equality with the string"runtests.jl"
.
override def header(): Unit = { | ||
importList.add(s"import ${className}Module") | ||
out.puts | ||
out.puts(s"@testset ${'"'}$className test${'"'} begin") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me, this way of inserting the literal double quote is unnecessarily complicated - this is how we usually do it:
out.puts(s"@testset ${'"'}$className test${'"'} begin") | |
out.puts(s"""@testset "$className test" begin""") |
See for example:
Line 33 in 44ffc60
out.puts(s"""local r = $className:from_file("src/${spec.data}")""") |
override def noAsserts() = | ||
out.puts("") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any particular reason to override noAsserts
just to insert a blank line? All other languages except Python don't override it and just use the default (empty) implementation:
Line 32 in 44ffc60
def noAsserts(): Unit = {} |
Python is the only language which overrides it, but that actually serves a purpose (Python syntactically requires at least one statement in each block, and pass
is idiomatically used if there's nothing to do):
Lines 77 to 78 in 44ffc60
override def noAsserts() = | |
out.puts("pass") |
override def simpleEquality(check: TestEquals): Unit = { | ||
val actStr = translateAct(check.actual) | ||
val expStr = translateExp(check.expected) | ||
out.puts(s"@test $actStr == $expStr") | ||
} | ||
|
||
override def floatEquality(check: TestEquals): Unit = { | ||
val actStr = translateAct(check.actual) | ||
val expStr = translator.translate(check.expected) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This inconsistency doesn't look intentional - either both should use translateExp
, or both should use translator.translate
.
def translateExp(x: Ast.expr) = | ||
translator.translate(x).replace("this._root", className) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like seeing this pattern at all since:
- it uses a textual replacement... (which is inherently wrong and should be avoided as much as possible - textual replacement can be only ever appropriate in free-form text, not in anything that has certain syntax and semantics that must be respected, like the code expression of some programming language. The existing
.replace("this." + Main.INIT_OBJ_NAME, "r")
is also fundamentally flawed, but at least it is very unlikely to cause any actual problems, becauseval INIT_OBJ_NAME = "q1w2e3"
is sufficiently "random" that it probably won't clash with anything unintended.) - with something that doesn't even make sense at first sight (in general you can hardly "replace"
_root
, which is the reference to the root instance of the top-level class, with the class name itself. That is like apples and oranges).
This shouldn't be necessary, and I'm afraid it indicates some issues with the compiler code generation...
@generalmimon Thanks for your detailed review. As a reaction to your suggested changes, I opened !127 |
Add tests for compiled tests, running scripts, and KST translator support for Julia
This PR addresses the issue kaitai-io/kaitai_struct#1109