Skip to content
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

[2.1]: Add tag system and Crystal 1.13 #85

Merged
merged 5 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM crystallang/crystal:1.12.1-alpine
FROM crystallang/crystal:1.13.1-alpine

# install packages required to run the tests
RUN apk add --no-cache bash jq coreutils
Expand Down
2 changes: 1 addition & 1 deletion bin/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ echo "${slug}: testing..."

# Run the tests for the provided implementation file and redirect stdout and
# stderr to capture it
crystal spec "${modified_spec_file}" --junit_output="${output_dir}" --no-color &> "${capture_file}"
crystal spec "${modified_spec_file}" --junit_output="${output_dir}" --tag "~optional" --no-color &> "${capture_file}"

./bin/test_runner "${spec_file}" "${capture_file}" "${junit_file}" "${results_file}"

Expand Down
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 2.1

- Move to Crystal 1.13.x
- Changed task_id system to make it more secure but also more feature rich
- Add support for optional tags

# 2.0.2

- Move to Crystal 1.12.1
Expand Down
42 changes: 34 additions & 8 deletions src/test_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ require "compiler/crystal/syntax"
class TestVisitor < Crystal::Visitor
getter result = [] of NamedTuple(snippet: String, name: String, task_id: Int32?)

# The task_id is used to give the test cases a task_id.
@task_id : Int32? = nil

# The initilization of the visitor.
# The source_code is used to get the test snippets.
# The level is used to give the test cases a task_id.
# The breadcrumbs are used to get the name of the test cases.
def initialize(source_code : Array(String))
@level = 0
def initialize(@source_code : Array(String))
@breadcrumbs = [] of String
@source_code = source_code
end

# This method is used to visit the nodes of the AST, specifically the Call nodes.
Expand Down Expand Up @@ -45,7 +45,15 @@ class TestVisitor < Crystal::Visitor
end

private def handle_visit_describe_call(node : Crystal::Call)
@level += 1 if @breadcrumbs.size == 1
if tags = node.named_args
if tags.any? { |tag| tag.value.as(Crystal::StringLiteral).value.to_s == "optional" }
return
end
@task_id = extract_task_id?(tags[0].value.as(Crystal::StringLiteral).value)
else
@task_id = nil
end

case arg = node.args[0]
when Crystal::StringLiteral
@breadcrumbs << arg.value
Expand All @@ -56,11 +64,21 @@ class TestVisitor < Crystal::Visitor
end

private def handle_end_visit_describe_call(node : Crystal::Call)
if tags = node.named_args
if tags.any? { |tag| tag.value.as(Crystal::StringLiteral).value.to_s == "optional" }
return
end
end
@breadcrumbs.pop
true
end

private def handle_visit_it_call(node : Crystal::Call)
if tags = node.named_args
if tags.any? { |tag| tag.value.as(Crystal::StringLiteral).value.to_s == "optional" }
return
end
end
label = node.args[0].not_nil!.as(Crystal::StringLiteral).value
current_test_name_prefix = @breadcrumbs.join(" ")
name = "#{current_test_name_prefix} #{label}"
Expand All @@ -73,8 +91,16 @@ class TestVisitor < Crystal::Visitor
end_line_index = end_location.line_number - 1

snippet = @source_code[start_line_index..end_line_index]
.map { |line| line[start_column_index..-1]? || "" }
.join("\n")
@result << {snippet: snippet, name: name, task_id: @level != 0 ? @level : nil}
.map { |line| line[start_column_index..-1]? || "" }
.join("\n")
@result << {snippet: snippet, name: name, task_id: @task_id}
end

private def extract_task_id?(text)
if text.starts_with?("task_id=")
text[8..-1].to_i?
else
nil
end
end
end
4 changes: 2 additions & 2 deletions tests/example-success-with-sub-group/spec/mini_bob_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ require "spec"
require "../src/*"

describe "Bob" do
describe "hey" do
describe "hey", tags: "task_id=1" do
it "responds to stating something" do
Bob.hey("Tom-ay-to, tom-aaaah-to.").should eq "Whatever. Tom-ay-to, tom-aaaah-to."
end
Expand All @@ -12,7 +12,7 @@ describe "Bob" do
end
end

describe "bye" do
describe "bye", tags: "task_id=2" do
it "says bye when calling bye" do
Bob.bye("").should eq "Bye."
end
Expand Down
12 changes: 12 additions & 0 deletions tests/example-with-optional-tests/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"files": {
"solution": [
"src/mini_bob.cr"
],
"test": [
"spec/mini_bob_spec.cr"
]
},
"blurb": "Create a sentence of the form \"One for X, one for me.\".",
"source_url": "https://github.com/exercism/problem-specifications/issues/757"
}
12 changes: 12 additions & 0 deletions tests/example-with-optional-tests/expected_results.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 3,
"status": "pass",
"tests": [
{
"name": "Bob hey responds to stating something",
"status": "pass",
"test_code": "Bob.hey(\"Tom-ay-to, tom-aaaah-to.\").should eq \"Whatever. Tom-ay-to, tom-aaaah-to.\"",
"output": "1\n1\n5\n5\n6\n6\n5\n5\n10\n10\n2\n2"
}
]
}
20 changes: 20 additions & 0 deletions tests/example-with-optional-tests/spec/mini_bob_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require "spec"
require "../src/*"

describe "Bob" do
describe "hey" do
it "responds to stating something" do
Bob.hey("Tom-ay-to, tom-aaaah-to.").should eq "Whatever. Tom-ay-to, tom-aaaah-to."
end

it "doesnt add extra part when not given", tags: "optional" do
Bob.hey("").should eq "Whatever. "
end
end

describe "bye", tags: "optional" do
it "says bye when calling bye" do
Bob.bye("").should eq "Bye."
end
end
end
18 changes: 18 additions & 0 deletions tests/example-with-optional-tests/src/mini_bob.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Bob
def self.hey(string : String)
numbers = [[1, 5, 6], [5, 10], [2]]
numbers.each do |number|
number.each do |num|
p num
puts num
end
end
"Whatever. #{string}"
end

def self.bye(string : String)
p "Bye."
puts 5
"Bye."
end
end
4 changes: 2 additions & 2 deletions tests/example-with-output/spec/mini_bob_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ require "spec"
require "../src/*"

describe "Bob" do
describe "hey" do
describe "hey", tags: "task_id=1" do
it "responds to stating something" do
Bob.hey("Tom-ay-to, tom-aaaah-to.").should eq "Whatever. Tom-ay-to, tom-aaaah-to."
end
Expand All @@ -12,7 +12,7 @@ describe "Bob" do
end
end

describe "bye" do
describe "bye", tags: "task_id=2" do
it "says bye when calling bye" do
Bob.bye("").should eq "Bye."
end
Expand Down