Skip to content

Commit

Permalink
Merge pull request #282 from ppeble/test_generation
Browse files Browse the repository at this point in the history
Rewrite test generation to use new test format in definitions
  • Loading branch information
ppeble committed Aug 1, 2017
2 parents 7d0715d + bff2ca0 commit 306f5b5
Show file tree
Hide file tree
Showing 98 changed files with 5,637 additions and 3,353 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
@@ -1,3 +1,3 @@
[submodule "definitions"]
path = definitions
url = https://github.com/holidays/definitions
url = https://github.com/holidays/definitions
1 change: 0 additions & 1 deletion .travis.yml
Expand Up @@ -18,7 +18,6 @@ notifications:
holidaysgem@gmail.com

rvm:
- 2.1.0
- 2.2.0
- 2.3.0
- 2.4.0
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,16 @@
# Ruby Holidays Gem CHANGELOG

## 6.0.0

* Remove support for ruby 2.1.0 since it is [no longer officially supported](https://www.ruby-lang.org/en/news/2017/04/01/support-of-ruby-2-1-has-ended/). This is the cause of the major
version bump.
* Update to [v2.0.0 definitions](https://github.com/holidays/definitions/releases/tag/v2.0.0). This changes the format
of definition tests and requires the other changes.
* Rewrite test generation logic to consume new YAML format.

To be crystal clear: this version should not behave differently in terms of holiday results than v5.7.0 of the gem. Any
differences are a bug that should be addressed.

## 5.7.0

* Update to [v1.7.1 definitions](https://github.com/holidays/definitions/releases/tag/v1.7.1). Please see the
Expand Down
2 changes: 1 addition & 1 deletion definitions
Submodule definitions updated 71 files
+1 −0 .gitignore
+0 −1 .travis.yml
+43 −16 SYNTAX.md
+103 −20 ar.yaml
+48 −13 at.yaml
+460 −147 au.yaml
+86 −27 be_fr.yaml
+74 −15 be_nl.yaml
+77 −18 bg.yaml
+104 −20 br.yaml
+461 −169 ca.yaml
+86 −27 ch.yaml
+152 −28 cl.yaml
+47 −12 cr.yaml
+80 −16 cz.yaml
+183 −75 de.yaml
+98 −20 dk.yaml
+41 −11 ecb_target.yaml
+61 −21 ee.yaml
+92 −18 el.yaml
+338 −61 es.yaml
+301 −57 federal_reserve.yaml
+52 −13 fedex.yaml
+146 −28 fi.yaml
+84 −26 fr.yaml
+260 −52 gb.yaml
+86 −29 ge.yaml
+145 −27 hk.yaml
+97 −19 hr.yaml
+84 −17 hu.yaml
+119 −26 ie.yaml
+124 −23 is.yaml
+74 −15 it.yaml
+320 −67 jp.yaml
+79 −20 kr.yaml
+70 −23 li.yaml
+4 −0 lib/validation/run.rb
+66 −1 lib/validation/test_validator.rb
+132 −31 lt.yaml
+56 −13 lu.yaml
+56 −12 ma.yaml
+71 −20 mt_en.yaml
+71 −20 mt_mt.yaml
+56 −12 mx.yaml
+37 −9 my.yaml
+56 −13 nerc.yaml
+68 −14 nl.yaml
+98 −19 no.yaml
+56 −12 north_america_informal.yaml
+60 −17 nyse.yaml
+141 −27 nz.yaml
+110 −21 pe.yaml
+48 −12 ph.yaml
+598 −134 pl.yaml
+110 −27 pt.yaml
+120 −24 ro.yaml
+61 −27 rs_cyrl.yaml
+61 −27 rs_la.yaml
+52 −17 ru.yaml
+147 −29 se.yaml
+43 −10 sg.yaml
+97 −24 si.yaml
+92 −18 sk.yaml
+156 −7 spec/validation/test_validator_spec.rb
+42 −11 tn.yaml
+79 −33 tr.yaml
+53 −13 ups.yaml
+584 −248 us.yaml
+61 −17 ve.yaml
+29 −11 vi.yaml
+74 −15 za.yaml
2 changes: 1 addition & 1 deletion lib/generated_definitions/north_america.rb
Expand Up @@ -158,7 +158,7 @@ def self.custom_methods

"christmas_eve_holiday(date)" => Proc.new { |date|
beginning_of_month = Date.civil(date.year, date.month, 1)
(date.saturday? || date.sunday?) ? date.downto(beginning_of_month).find {|date| date if date.wday == 5} : date
(date.saturday? || date.sunday?) ? date.downto(beginning_of_month).find {|d| d if d.wday == 5} : date
},

"rosh_hashanah(year)" => Proc.new { |year|
Expand Down
1 change: 1 addition & 0 deletions lib/generated_definitions/tr.rb
Expand Up @@ -23,6 +23,7 @@ def self.holidays_by_month
4 => [{:mday => 23, :name => "Ulusal Egemenlik ve Çocuk Bayramı", :regions => [:tr]}],
5 => [{:mday => 1, :name => "Emek ve Dayanışma Günü", :regions => [:tr]},
{:mday => 19, :name => "Atatürk'ü Anma Gençlik ve Spor Bayramı", :regions => [:tr]}],
7 => [{:mday => 15, :year_ranges => [{:after => 2016}],:name => "Demokrasi ve Milli Birlik Günü", :regions => [:tr]}],
8 => [{:mday => 30, :name => "Zafer Bayramı", :regions => [:tr]}],
10 => [{:mday => 29, :name => "Cumhuriyet Bayramı", :regions => [:tr]}]
}
Expand Down
2 changes: 1 addition & 1 deletion lib/generated_definitions/us.rb
Expand Up @@ -92,7 +92,7 @@ def self.custom_methods
{
"christmas_eve_holiday(date)" => Proc.new { |date|
beginning_of_month = Date.civil(date.year, date.month, 1)
(date.saturday? || date.sunday?) ? date.downto(beginning_of_month).find {|date| date if date.wday == 5} : date
(date.saturday? || date.sunday?) ? date.downto(beginning_of_month).find {|d| d if d.wday == 5} : date
},

"rosh_hashanah(year)" => Proc.new { |year|
Expand Down
41 changes: 5 additions & 36 deletions lib/holidays/definition/context/generator.rb
Expand Up @@ -11,10 +11,12 @@ module Holidays
module Definition
module Context
class Generator
def initialize(custom_method_parser, custom_method_source_decorator, custom_methods_repository)
def initialize(custom_method_parser, custom_method_source_decorator, custom_methods_repository, test_parser, test_source_generator)
@custom_method_parser = custom_method_parser
@custom_method_source_decorator = custom_method_source_decorator
@custom_methods_repository = custom_methods_repository
@test_parser = test_parser
@test_source_generator = test_source_generator
end

def parse_definition_files(files)
Expand Down Expand Up @@ -47,7 +49,7 @@ def parse_definition_files(files)
# on each other so we need a solution.
all_custom_methods.merge!(custom_methods)

all_tests << parse_test_definitions(definition_file['tests'])
all_tests += @test_parser.call(definition_file['tests'])
end

all_regions.flatten!.uniq!
Expand All @@ -65,7 +67,7 @@ def generate_definition_source(module_name, files, regions, rules_by_month, cust
end

module_src = generate_module_src(module_name, files, regions, month_strings, custom_method_string)
test_src = generate_test_src(module_name, files, tests)
test_src = @test_source_generator.call(module_name, files, tests)

return module_src, test_src || ''
end
Expand Down Expand Up @@ -135,16 +137,6 @@ def clean_year_ranges(year_ranges)
end
end

def parse_test_definitions(tests)
test_strings = []

if tests
test_strings << tests
end

test_strings
end

#FIXME This should really be split out and tested with its own unit tests.
def generate_month_definition_strings(rules_by_month, parsed_custom_methods)
month_strings = []
Expand Down Expand Up @@ -258,29 +250,6 @@ def self.custom_methods

return module_src
end

def generate_test_src(module_name, files, tests)
unless tests.empty?
test_src = ""

test_src =<<-EndOfTests
# encoding: utf-8
require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
# This file is generated by the Ruby Holiday gem.
#
# Definitions loaded: #{files.join(', ')}
class #{module_name.to_s.capitalize}DefinitionTests < Test::Unit::TestCase # :nodoc:
def test_#{module_name.to_s.downcase}
#{tests.join("\n\n")}
end
end
EndOfTests
end

return test_src
end
end
end
end
Expand Down
37 changes: 37 additions & 0 deletions lib/holidays/definition/decorator/test.rb
@@ -0,0 +1,37 @@
module Holidays
module Definition
module Decorator
class Test
def call(t)
src = ""

t.dates.each do |d|
date = "Date.civil(#{d.year}, #{d.month}, #{d.day})"

holiday_call = "Holidays.on(#{date}, #{t.regions}"

if t.options
holiday_call += ", #{decorate_options(t.options)}"
end

if t.holiday?
src += "assert_equal \"#{t.name}\", (#{holiday_call})[0] || {})[:name]\n"
else
src += "assert_nil (#{holiday_call})[0] || {})[:name]\n"
end
end

src
end

private

def decorate_options(options)
options.map do |o|
o.to_sym
end
end
end
end
end
end
11 changes: 11 additions & 0 deletions lib/holidays/definition/entity/test.rb
@@ -0,0 +1,11 @@
module Holidays
module Definition
module Entity
Test = Struct.new(:dates, :regions, :options, :name, :holiday?) do
def initialize(fields = {})
super(*fields.values_at(*members))
end
end
end
end
end
51 changes: 51 additions & 0 deletions lib/holidays/definition/generator/test.rb
@@ -0,0 +1,51 @@
require 'holidays/errors'

module Holidays
module Definition
module Generator
class Test
def initialize(decorator)
@decorator = decorator
end

def call(module_name, file_names, tests)
validate!(module_name, file_names, tests)

test_src =<<-EndOfTests
# encoding: utf-8
require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
# This file is generated by the Ruby Holiday gem.
#
# Definitions loaded: #{file_names.join(', ')}
class #{module_name.to_s.capitalize}DefinitionTests < Test::Unit::TestCase # :nodoc:
def test_#{module_name.to_s.downcase}#{decorate(tests)}
end
end
EndOfTests

test_src
end

private

def validate!(module_name, file_names, tests)
raise ArgumentError.new("module_name cannot be missing") if module_name.nil? || module_name.empty?
raise ArgumentError.new("file_names for '#{module_name}' cannot be missing") if file_names.nil? || file_names.empty?
raise ArgumentError.new("tests for '#{module_name}' cannot be missing") if tests.nil?
end

def decorate(tests)
out = ""

tests.each do |t|
out << "\n " + @decorator.call(t)
end

out
end
end
end
end
end
86 changes: 86 additions & 0 deletions lib/holidays/definition/parser/test.rb
@@ -0,0 +1,86 @@
require 'holidays/definition/entity/test'

module Holidays
module Definition
module Parser
class Test
def initialize(validator)
@validator = validator
end

def call(tests)
return [] if tests.nil?

validate!(tests)

tests.map do |t|
given = t["given"]
expect = t["expect"]

Entity::Test.new(
dates: parse_dates(given["date"]),
regions: parse_regions(given["regions"]),
options: parse_options(given["options"]),
name: expect["name"],
holiday?: is_holiday?(expect["holiday"]),
)
end
end

private

def validate!(tests)
raise ArgumentError unless tests.all? do |t|
dates = t["given"]["date"]
unless dates.is_a?(Array)
dates = [ dates ]
end

@validator.valid?(
{
:dates => dates,
:regions => t["given"]["regions"],
:options => t["given"]["options"],
:name => t["expect"]["name"],
:holiday => t["expect"]["holiday"],
}
)
end
end

def parse_dates(dates)
unless dates.is_a?(Array)
dates = [ dates ]
end

dates.map do |d|
DateTime.parse(d)
end
end

def parse_regions(regions)
regions.map do |r|
r.to_sym
end
end

def parse_options(options)
if options
if options.is_a?(Array)
options.map do |o|
o.to_sym
end
else
[ options.to_sym ]
end
end
end

# If flag is not present then default to 'true'
def is_holiday?(flag)
flag.nil? ? true : !!flag
end
end
end
end
end
71 changes: 71 additions & 0 deletions lib/holidays/definition/validator/test.rb
@@ -0,0 +1,71 @@
module Holidays
module Definition
module Validator
class Test
def valid?(t)
valid_dates?(t[:dates]) &&
valid_regions?(t[:regions]) &&
valid_name?(t[:name]) &&
valid_holiday?(t[:holiday]) &&
valid_options?(t[:options]) &&
required_fields?(t)
end

private

def valid_dates?(dates)
return false unless dates

dates.all? do |d|
begin
DateTime.parse(d)
true
rescue TypeError, ArgumentError
false
end
end
end

def valid_regions?(regions)
return false unless regions

regions.all? do |r|
r.is_a?(String)
end
end

# Can be missing
def valid_name?(n)
return true unless n
n.is_a?(String)
end

# Can be missing
def valid_holiday?(h)
return true unless h
h.is_a?(TrueClass)
end

# Okay to be missing and can be either string or array of strings
def valid_options?(options)
return true unless options

if options.is_a?(Array)
options.all? do |o|
o.is_a?(String)
end
elsif options.is_a?(String)
true
else
false
end
end

def required_fields?(t)
return false if t[:name].nil? && t[:holiday].nil?
true
end
end
end
end
end

0 comments on commit 306f5b5

Please sign in to comment.