Skip to content
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
7 changes: 7 additions & 0 deletions bin/generate-custom-set
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env ruby

require_relative '../lib/helper'
require 'generator'
require 'custom_set_cases'

Generator.new('custom-set', CustomSetCases).generate
1 change: 1 addition & 0 deletions exercises/custom-set/.version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
321 changes: 253 additions & 68 deletions exercises/custom-set/custom_set_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,114 +3,299 @@
require 'minitest/autorun'
require_relative 'custom_set'

# Test data version:
# cdfb4a2
class CustomSetTest < Minitest::Test
def test_equal
assert_equal CustomSet.new([1, 3]), CustomSet.new([3, 1])
refute_equal CustomSet.new([1, 3]), CustomSet.new([3, 1, 5])
refute_equal CustomSet.new([1, 3, 5]), CustomSet.new([3, 1])
refute_equal CustomSet.new([1, 3]), CustomSet.new([2, 1])
def test_sets_with_no_elements_are_empty
# skip
set = CustomSet.new []
assert_empty set
end

def test_no_duplicates
assert_equal CustomSet.new([1, 1]), CustomSet.new([1])
def test_sets_with_elements_are_not_empty
skip
set = CustomSet.new [1]
refute_empty set
end
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.

Shouldn't the empty tests use the empty? method? As written, these are really testing equality and not emptiness.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The test assert_empty? requires that the object responds to empty? and there is no spec for that currently.

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 believe this should be that very spec. The metadata for these exercises is:

   "empty": {
     "description": "Returns true if the set contains no elements",
     "cases": [
        "..."
     ]
   },

This is not the place to rely on equality. Equality has its own section later on.


def test_nothing_is_contained_in_an_empty_set
skip
set = CustomSet.new []
element = 1
refute set.member? element
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

member? should be contains?

member is not referenced in the custom-set.json but contains is.

Copy link
Copy Markdown
Member

@Cohen-Carlisle Cohen-Carlisle Jun 27, 2016

Choose a reason for hiding this comment

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

I would actually make an argument for include? as its the most common duck typed method for this kind of thing in Ruby. I don't think we have to stick to the description exactly, especially when ruby has a clear pattern for this kind of query.

Edit: And include? is the Enumerable standard. http://ruby-doc.org/core-2.3.1/Enumerable.html#method-i-include-3F

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The names in section don't necessarily have to match the methods, they are section headers for common-data. But I changed it in the example code anyway.

end

def test_when_the_element_is_in_the_set
skip
set = CustomSet.new [1, 2, 3]
element = 1
assert set.member? element
end

def test_delete
def test_when_the_element_is_not_in_the_set
skip
assert_equal CustomSet.new([1, 3]), CustomSet.new([3, 2, 1]).delete(2)
assert_equal CustomSet.new([1, 2, 3]), CustomSet.new([3, 2, 1]).delete(4)
assert_equal CustomSet.new([1, 2, 3]), CustomSet.new([3, 2, 1]).delete(2.0)
assert_equal CustomSet.new([1, 3]), CustomSet.new([3, 2.0, 1]).delete(2.0)
set = CustomSet.new [1, 2, 3]
element = 4
refute set.member? element
end

def test_difference
def test_empty_set_is_a_subset_of_another_empty_set
skip
assert_equal CustomSet.new([1, 3]),
CustomSet.new([1, 2, 3]).difference(CustomSet.new([2, 4]))
set1 = CustomSet.new []
set2 = CustomSet.new []
assert set1.subset? set2
end

assert_equal CustomSet.new([1, 2.0, 3]),
CustomSet.new([1, 2.0, 3]).difference(CustomSet.new([2, 4]))
def test_empty_set_is_a_subset_of_non_empty_set
skip
set1 = CustomSet.new []
set2 = CustomSet.new [1]
assert set1.subset? set2
end
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.

These subset tests seem backwards to me. I read set2.subset? set1 as Is set2 at subset of set1?
Looking at Ruby's Set class, I see it also handles it this way.


def test_disjoint?
def test_non_empty_set_is_not_a_subset_of_empty_set
skip
assert CustomSet.new([1, 2]).disjoint?(CustomSet.new([3, 4]))
refute CustomSet.new([1, 2]).disjoint?(CustomSet.new([2, 3]))
assert CustomSet.new([1.0, 2.0]).disjoint?(CustomSet.new([2, 3]))
assert CustomSet.new.disjoint?(CustomSet.new)
set1 = CustomSet.new [1]
set2 = CustomSet.new []
refute set1.subset? set2
end

def test_empty
def test_set_is_a_subset_of_set_with_exact_same_elements
skip
assert_equal CustomSet.new, CustomSet.new([1, 2]).empty
assert_equal CustomSet.new, CustomSet.new.empty
set1 = CustomSet.new [1, 2, 3]
set2 = CustomSet.new [1, 2, 3]
assert set1.subset? set2
end

def test_set_is_a_subset_of_larger_set_with_same_elements
skip
set1 = CustomSet.new [1, 2, 3]
set2 = CustomSet.new [4, 1, 2, 3]
assert set1.subset? set2
end

def test_set_is_not_a_subset_of_set_that_does_not_contain_its_elements
skip
set1 = CustomSet.new [1, 2, 3]
set2 = CustomSet.new [4, 1, 3]
refute set1.subset? set2
end

def test_the_empty_set_is_disjoint_with_itself
skip
set1 = CustomSet.new []
set2 = CustomSet.new []
assert set1.disjoint? set2
end

def test_empty_set_is_disjoint_with_non_empty_set
skip
set1 = CustomSet.new []
set2 = CustomSet.new [1]
assert set1.disjoint? set2
end

def test_non_empty_set_is_disjoint_with_empty_set
skip
set1 = CustomSet.new [1]
set2 = CustomSet.new []
assert set1.disjoint? set2
end

def test_sets_are_not_disjoint_if_they_share_an_element
skip
set1 = CustomSet.new [1, 2]
set2 = CustomSet.new [2, 3]
refute set1.disjoint? set2
end

def test_sets_are_disjoint_if_they_share_no_elements
skip
set1 = CustomSet.new [1, 2]
set2 = CustomSet.new [3, 4]
assert set1.disjoint? set2
end

def test_empty_sets_are_equal
skip
set1 = CustomSet.new []
set2 = CustomSet.new []
assert_equal set1, set2
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.

This is the right place to test equality. Note that the first test (about emptiness) is actually more complex than this as its a duplicate except it requires no args to new to be treated the same as an empty array to new.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This comment is probably more directed to the x-common repository for that .json file?

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.

The problem isn't here, it was really a follow up comment to:

Shouldn't the empty tests use the empty? method? As written, these are really testing equality and not emptiness.

So then I followed up with:

This is the right place to test equality. Note that the first test (about emptiness) is actually more complex than this as its a duplicate except it requires no args to new to be treated the same as an empty array to new.

end

def test_empty_set_is_not_equal_to_non_empty_set
skip
set1 = CustomSet.new []
set2 = CustomSet.new [1, 2, 3]
refute_equal set1, set2
end

def test_non_empty_set_is_not_equal_to_empty_set
skip
set1 = CustomSet.new [1, 2, 3]
set2 = CustomSet.new []
refute_equal set1, set2
end

def test_sets_with_the_same_elements_are_equal
skip
set1 = CustomSet.new [1, 2]
set2 = CustomSet.new [2, 1]
assert_equal set1, set2
end

def test_sets_with_different_elements_are_not_equal
skip
set1 = CustomSet.new [1, 2, 3]
set2 = CustomSet.new [1, 2, 4]
refute_equal set1, set2
end

def test_add_to_empty_set
skip
set = CustomSet.new []
expected = CustomSet.new [3]
assert_equal expected, set.add(3)
end

def test_add_to_non_empty_set
skip
set = CustomSet.new [1, 2, 4]
expected = CustomSet.new [1, 2, 3, 4]
assert_equal expected, set.add(3)
end
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.

Something about this seems a little awkward to me. As I read each line I don't understand why its important. Perhaps something like?

set1 = CustomSet.new [1, 2, 3, 4]
set2 = CustomSet.new [1, 2, 4]
assert_equal set1, set2.put(3)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Also from the point of view of the common 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.

Not necessarily, you are still consuming the data. For instance, I was thinking the above code (revised slightly) would correspond to the generator code:

  def add
    "set = CustomSet.new #{set}
    expected = CustomSet.new #{expected}
    #{assert_or_refute}_equal expected, set.put(#{element})"
  end

So actually, I think the thing I didn't like was declaring element separately.


def test_adding_an_existing_element_does_not_change_the_set
skip
set = CustomSet.new [1, 2, 3]
expected = CustomSet.new [1, 2, 3]
assert_equal expected, set.add(3)
end

def test_intersection_of_two_empty_sets_is_an_empty_set
skip
set1 = CustomSet.new []
set2 = CustomSet.new []
expected = CustomSet.new []
assert_equal expected, set2.intersection(set1)
end

def test_intersection_of_an_empty_set_and_non_empty_set_is_an_empty_set
skip
set1 = CustomSet.new []
set2 = CustomSet.new [3, 2, 5]
expected = CustomSet.new []
assert_equal expected, set2.intersection(set1)
end

def test_intersection_of_a_non_empty_set_and_an_empty_set_is_an_empty_set
skip
set1 = CustomSet.new [1, 2, 3, 4]
set2 = CustomSet.new []
expected = CustomSet.new []
assert_equal expected, set2.intersection(set1)
end

def test_intersection_of_two_sets_with_no_shared_elements_is_an_empty_set
skip
set1 = CustomSet.new [1, 2, 3]
set2 = CustomSet.new [4, 5, 6]
expected = CustomSet.new []
assert_equal expected, set2.intersection(set1)
end

# rubocop:disable Metrics/LineLength
def test_intersection
def test_intersection_of_two_sets_with_shared_elements_is_a_set_of_the_shared_elements
skip
assert_equal CustomSet.new([:a, :c]),
CustomSet.new([:a, :b, :c]).intersection(CustomSet.new([:a, :c, :d]))
set1 = CustomSet.new [1, 2, 3, 4]
set2 = CustomSet.new [3, 2, 5]
expected = CustomSet.new [2, 3]
assert_equal expected, set2.intersection(set1)
end

assert_equal CustomSet.new([3]),
CustomSet.new([1, 2, 3]).intersection(CustomSet.new([1.0, 2.0, 3]))
def test_difference_of_two_empty_sets_is_an_empty_set
skip
set1 = CustomSet.new []
set2 = CustomSet.new []
expected = CustomSet.new []
assert_equal expected, set1.difference(set2)
end

def test_member?
def test_difference_of_empty_set_and_non_empty_set_is_an_empty_set
skip
assert CustomSet.new([1, 2, 3]).member?(2)
assert CustomSet.new(1..3).member?(2)
refute CustomSet.new(1..3).member?(2.0)
refute CustomSet.new(1..3).member?(4)
set1 = CustomSet.new []
set2 = CustomSet.new [3, 2, 5]
expected = CustomSet.new []
assert_equal expected, set1.difference(set2)
end

def test_put
def test_difference_of_a_non_empty_set_and_an_empty_set_is_the_non_empty_set
skip
assert_equal CustomSet.new([1, 2, 3, 4]),
CustomSet.new([1, 2, 4]).put(3)
set1 = CustomSet.new [1, 2, 3, 4]
set2 = CustomSet.new []
expected = CustomSet.new [1, 2, 3, 4]
assert_equal expected, set1.difference(set2)
end

assert_equal CustomSet.new([1, 2, 3]),
CustomSet.new([1, 2, 3]).put(3)
def test_difference_of_two_non_empty_sets_is_a_set_of_elements_that_are_only_in_the_first_set
skip
set1 = CustomSet.new [3, 2, 1]
set2 = CustomSet.new [2, 4]
expected = CustomSet.new [1, 3]
assert_equal expected, set1.difference(set2)
end

assert_equal CustomSet.new([1, 2, 3, 3.0]),
CustomSet.new([1, 2, 3]).put(3.0)
def test_union_of_empty_sets_is_an_empty_set
skip
set1 = CustomSet.new []
set2 = CustomSet.new []
expected = CustomSet.new []
assert_equal expected, set1.union(set2)
end

def test_size
def test_union_of_an_empty_set_and_non_empty_set_is_the_non_empty_set
skip
assert_equal 0, CustomSet.new.size
assert_equal 3, CustomSet.new([1, 2, 3]).size
assert_equal 3, CustomSet.new([1, 2, 3, 2]).size
set1 = CustomSet.new []
set2 = CustomSet.new [2]
expected = CustomSet.new [2]
assert_equal expected, set1.union(set2)
end

def test_subset?
def test_union_of_a_non_empty_set_and_empty_set_is_the_non_empty_set
skip
assert CustomSet.new([1, 2, 3]).subset?(CustomSet.new([1, 2, 3]))
assert CustomSet.new([4, 1, 2, 3]).subset?(CustomSet.new([1, 2, 3]))
refute CustomSet.new([4, 1, 3]).subset?(CustomSet.new([1, 2, 3]))
refute CustomSet.new([1, 2, 3, 4]).subset?(CustomSet.new([1, 2, 3.0]))
assert CustomSet.new([4, 1, 3]).subset?(CustomSet.new)
assert CustomSet.new.subset?(CustomSet.new)
set1 = CustomSet.new [1, 3]
set2 = CustomSet.new []
expected = CustomSet.new [1, 3]
assert_equal expected, set1.union(set2)
end

def test_to_a
def test_union_of_non_empty_sets_contains_all_unique_elements
skip
assert_equal [], CustomSet.new.to_a.sort
assert_equal [1, 2, 3], CustomSet.new([3, 1, 2]).to_a.sort
assert_equal [1, 2, 3], CustomSet.new([3, 1, 2, 1]).to_a.sort
set1 = CustomSet.new [1, 3]
set2 = CustomSet.new [2, 3]
expected = CustomSet.new [3, 2, 1]
assert_equal expected, set1.union(set2)
end

def test_union # rubocop:disable Metrics/MethodLength
# Problems in exercism evolve over time, as we find better ways to ask
# questions.
# The version number refers to the version of the problem you solved,
# not your solution.
#
# Define a constant named VERSION inside of the top level BookKeeping
# module, which may be placed near the end of your file.
#
# In your file, it will look like this:
#
# module BookKeeping
# VERSION = 1 # Where the version number matches the one in the test.
# end
#
# If you are curious, read more about constants on RubyDoc:
# http://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/constants.html

def test_bookkeeping
skip
assert_equal CustomSet.new([3, 2, 1]),
CustomSet.new([1, 3]).union(CustomSet.new([2]))
assert_equal CustomSet.new([3.0, 3, 2, 1]),
CustomSet.new([1, 3]).union(CustomSet.new([2, 3.0]))
assert_equal CustomSet.new([3, 1]),
CustomSet.new([1, 3]).union(CustomSet.new)
assert_equal CustomSet.new([2]),
CustomSet.new([2]).union(CustomSet.new)
assert_equal CustomSet.new([]),
CustomSet.new.union(CustomSet.new)
assert_equal 1, BookKeeping::VERSION
end
end
Loading