Skip to content
Open
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
9 changes: 4 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ jobs:
runs-on: ubuntu-24.04
strategy:
matrix:
elixir: [1.19.2]
otp: [28.1]
elixir: [1.20.1]
otp: [29.0]

steps:
- name: Checkout code
Expand Down Expand Up @@ -76,8 +76,6 @@ jobs:
strategy:
matrix:
include:
- elixir: '1.15.0'
otp: '26.0'
- elixir: '1.16.0'
otp: '26.2'
- elixir: '1.17.0'
Expand All @@ -86,6 +84,8 @@ jobs:
otp: '27.2'
- elixir: '1.19.2'
otp: '28.1'
- elixir: '1.20.1'
otp: '29.0'

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
Expand Down Expand Up @@ -121,4 +121,3 @@ jobs:

- name: Run Checks
run: bin/ci.sh

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

## Setup

The exercises currently target Elixir versions from 1.15 to 1.19 and Erlang/OTP versions from 25 to 27. Detailed installation instructions can be found at
The exercises currently target Elixir versions from 1.16 to 1.20 and Erlang/OTP versions from 26 to 29. Detailed installation instructions can be found at
[https://elixir-lang.org/install.html](https://elixir-lang.org/install.html). We recommend using the [asdf version manager](https://github.com/asdf-vm/asdf) to manage multiple Elixir versions.

## Testing
Expand Down
20 changes: 8 additions & 12 deletions exercises/concept/city-office/test/form_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ defmodule FormTest do
end
end

defmacrop assert_type({module_name, type_name}, expected_type_definition) do
defmacrop assert_type({module_name, type_name}, expected_type_definitions) do
quote do
{:ok, types} = Code.Typespec.fetch_types(unquote(module_name))

Expand All @@ -94,15 +94,11 @@ defmodule FormTest do

actual_type_definition = Macro.to_string(type_definition)

if is_list(unquote(expected_type_definition)) do
if actual_type_definition in unquote(expected_type_definition) do
assert true
else
# we know this will fail at this point, but we're using it to provide a nice failure message
assert actual_type_definition == hd(unquote(expected_type_definition))
end
if actual_type_definition in unquote(expected_type_definitions) do
assert true
else
assert actual_type_definition == unquote(expected_type_definition)
# we know this will fail at this point, but we're using it to provide a nice failure message
assert actual_type_definition == hd(unquote(expected_type_definitions))
end
end
end
Expand Down Expand Up @@ -231,10 +227,10 @@ defmodule FormTest do

@tag task_id: 5
test "has a custom 'address_tuple' type with named arguments" do
expected_type_definition =
"{street :: String.t(), postal_code :: String.t(), city :: String.t()}"
expected_type_definitions =
["{street :: String.t(), postal_code :: String.t(), city :: String.t()}"]

assert_type({Form, :address_tuple}, expected_type_definition)
assert_type({Form, :address_tuple}, expected_type_definitions)
end

@tag task_id: 5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,22 +88,22 @@ defmodule KitchenCalculatorTest do
describe "convert from x to y:" do
@tag task_id: 4
test "teaspoon to tablespoon" do
assert KitchenCalculator.convert({:teaspoon, 15}, :tablespoon) == {:tablespoon, 5}
assert KitchenCalculator.convert({:teaspoon, 15}, :tablespoon) == {:tablespoon, 5.0}
end

@tag task_id: 4
test "cups to fluid ounces" do
assert KitchenCalculator.convert({:cup, 4}, :fluid_ounce) == {:fluid_ounce, 32}
assert KitchenCalculator.convert({:cup, 4}, :fluid_ounce) == {:fluid_ounce, 32.0}
end

@tag task_id: 4
test "fluid ounces to teaspoons" do
assert KitchenCalculator.convert({:fluid_ounce, 4}, :teaspoon) == {:teaspoon, 24}
assert KitchenCalculator.convert({:fluid_ounce, 4}, :teaspoon) == {:teaspoon, 24.0}
end

@tag task_id: 4
test "tablespoons to cups" do
assert KitchenCalculator.convert({:tablespoon, 320}, :cup) == {:cup, 20}
assert KitchenCalculator.convert({:tablespoon, 320}, :cup) == {:cup, 20.0}
end
end
end
4 changes: 2 additions & 2 deletions exercises/concept/paint-by-number/.meta/exemplar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ defmodule PaintByNumber do

case picture do
<<>> -> nil
<<pixel_color_index::size(palette_bit_size), _::bitstring>> -> pixel_color_index
<<pixel_color_index::size(^palette_bit_size), _::bitstring>> -> pixel_color_index
end
end

Expand All @@ -39,7 +39,7 @@ defmodule PaintByNumber do

case picture do
<<>> -> <<>>
<<_::size(palette_bit_size), rest::bitstring>> -> rest
<<_::size(^palette_bit_size), rest::bitstring>> -> rest
end
end

Expand Down
23 changes: 12 additions & 11 deletions exercises/concept/top-secret/test/top_secret_test.exs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
defmodule TopSecretTest do
use ExUnit.Case

# Maintainer notes:
# - in Elixir 1.17, `:...` got [] as an argument list, before that it was nil
# - in Elixir 1.20, `:__block__` got the line number in the metadata, before that it was empty

describe "to_ast/1" do
@tag task_id: 1
test "handles an empty string" do
string = ""
ast = {:__block__, [], []}

assert TopSecret.to_ast(string) == ast
assert {:__block__, _line_1_or_empty_list, []} = TopSecret.to_ast(string)
end

@tag task_id: 1
Expand All @@ -17,14 +20,12 @@ defmodule TopSecretTest do
y = x - 2
"""

ast =
{:__block__, [],
[
{:=, [line: 1], [{:x, [line: 1], nil}, 7]},
{:=, [line: 2], [{:y, [line: 2], nil}, {:-, [line: 2], [{:x, [line: 2], nil}, 2]}]}
]}

assert TopSecret.to_ast(string) == ast
assert {:__block__, _line_1_or_empty_list,
[
{:=, [line: 1], [{:x, [line: 1], nil}, 7]},
{:=, [line: 2],
[{:y, [line: 2], nil}, {:-, [line: 2], [{:x, [line: 2], nil}, 2]}]}
]} = TopSecret.to_ast(string)
end

@tag task_id: 1
Expand All @@ -45,7 +46,7 @@ defmodule TopSecretTest do
[
do: {
:__block__,
[],
_line_1_or_empty_list,
[
{:@, [line: 2],
[
Expand Down
4 changes: 2 additions & 2 deletions exercises/practice/beer-song/test/beer_song_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ defmodule BeerSongTest do
describe "lyrics" do
@tag :pending
test "first two verses" do
assert BeerSong.lyrics(99..98) == """
assert BeerSong.lyrics(99..98//-1) == """
99 bottles of beer on the wall, 99 bottles of beer.
Take one down and pass it around, 98 bottles of beer on the wall.

Expand All @@ -57,7 +57,7 @@ defmodule BeerSongTest do

@tag :pending
test "last three verses" do
assert BeerSong.lyrics(2..0) == """
assert BeerSong.lyrics(2..0//-1) == """
2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,4 @@ defmodule CollatzConjectureTest do
test "negative value is an error " do
assert_raise FunctionClauseError, fn -> CollatzConjecture.calc(-15) end
end

@tag :pending
test "string as input value is an error " do
assert_raise FunctionClauseError, fn -> CollatzConjecture.calc("fubar") end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ defmodule Transmission do
@spec get_parity(bitstring()) :: <<_::1>>
defp get_parity(data) do
bit_count = bit_size(data)
<<value::size(bit_count)>> = data
<<value::size(^bit_count)>> = data

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very happy that 1.20 is finding those bugs 💯


parity_int =
0..(bit_count - 1)
Expand Down Expand Up @@ -71,7 +71,7 @@ defmodule Transmission do

defp finalize(data, acc) when bit_size(data) == 7 do
bits_count = 8 - rem(bit_size(acc), 8)
<<head::size(bits_count), _::bitstring>> = data
<<head::size(^bits_count), _::bitstring>> = data
<<acc::bitstring, head::size(bits_count)>>
end
end
4 changes: 2 additions & 2 deletions exercises/practice/kindergarten-garden/.meta/example.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ defmodule Garden do
letters = "#{first_row_letters}#{second_row_letters}"
map = add_to_map(map, current_name, letters)

first_row = String.slice(first_row, 2..-1//-1)
second_row = String.slice(second_row, 2..-1//-1)
first_row = String.slice(first_row, 2..-1//1)
second_row = String.slice(second_row, 2..-1//1)
populate_map(first_row, second_row, map, student_names)
end

Expand Down
2 changes: 1 addition & 1 deletion exercises/practice/list-ops/test/list_ops_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ defmodule ListOpsTest do
@tag :pending
@tag :slow
test "huge list" do
assert L.reverse(Enum.to_list(1..1_000_000)) == Enum.to_list(1_000_000..1)
assert L.reverse(Enum.to_list(1..1_000_000)) == Enum.to_list(1_000_000..1//-1)
end
end

Expand Down
2 changes: 1 addition & 1 deletion exercises/practice/pascals-triangle/.meta/example.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule PascalsTriangle do
defp each_cons(list) do
list
|> Enum.flat_map(&[&1, &1])
|> Enum.slice(1..-2//-1)
|> Enum.slice(1..-2//1)
|> Enum.chunk_every(2)
end

Expand Down
2 changes: 1 addition & 1 deletion exercises/practice/pig-latin/.meta/example.ex
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ defmodule PigLatin do
end

defp move_prefix_to_the_end(string, prefix) do
String.slice(string, String.length(prefix)..-1) <> prefix
String.slice(string, String.length(prefix)..-1//1) <> prefix
end
end
8 changes: 5 additions & 3 deletions exercises/practice/rail-fence-cipher/.meta/example.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,27 @@ defmodule RailFenceCipher do
Decode a given rail fence ciphertext to the corresponding plaintext
"""
@spec decode(String.t(), pos_integer) :: String.t()
def decode("", _), do: ""
def decode(str, 1), do: str

def decode(str, rails) do
0..(String.length(str) - 1)
0..(String.length(str) - 1)//1
|> Enum.to_list()
|> fill_fence(rails)
|> extract_original(str)
end

defp extract_original(fence, str) do
0..(String.length(str) - 1)
0..(String.length(str) - 1)//1
|> Enum.map(fn i ->
String.at(str, Enum.find_index(fence, &(&1 == i)))
end)
|> Enum.join()
end

defp fill_fence(list_chars, rails) do
rail_zigzag_indexes = Enum.concat(Enum.to_list(0..(rails - 1)), Enum.to_list((rails - 2)..1))
rail_zigzag_indexes =
Enum.concat(Enum.to_list(0..(rails - 1)//1), Enum.to_list((rails - 2)..1//-1))

create_empty_fence(rails, length(list_chars))
|> set_chars_to_fence(list_chars, rail_zigzag_indexes)
Expand Down
21 changes: 0 additions & 21 deletions exercises/practice/two-fer/test/two_fer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,4 @@ defmodule TwoFerTest do
test "another name given" do
assert TwoFer.two_fer("Bob") == "One for Bob, one for me."
end

@tag :pending
test "when the argument is a number" do
assert_raise FunctionClauseError, fn ->
TwoFer.two_fer(10)
end
end

@tag :pending
test "when the argument is an atom" do
assert_raise FunctionClauseError, fn ->
TwoFer.two_fer(:bob)
end
end

@tag :pending
test "when the argument is a charlist" do
assert_raise FunctionClauseError, fn ->
refute TwoFer.two_fer(~c"Jon Snow")
end
end
end
Loading