Skip to content
Browse files

Tests written for additional date formats

  • Loading branch information...
1 parent eb800b7 commit 1c1f8ebc17d917a50b13cd00ae2f68b48433c68e Cameron Dutro committed Nov 11, 2012
View
2 lib/twitter_cldr/formatters/calendars/datetime_formatter.rb
@@ -69,7 +69,7 @@ def era(date, pattern, length)
if result = choices[date.year < 0 ? 0 : 1]
result
else
- era(date, pattern, length - 1)
+ era(date, pattern[0..-2], length - 1)
end
end
View
3 lib/twitter_cldr/tokenizers/base.rb
@@ -3,9 +3,6 @@
# Copyright 2012 Twitter, Inc
# http://www.apache.org/licenses/LICENSE-2.0
-require 'pry'
-require 'pry-nav'
-
module TwitterCldr
module Tokenizers
class Base
View
13 lib/twitter_cldr/tokenizers/calendars/additional_date_format_selector.rb
@@ -13,7 +13,7 @@ def initialize(pattern_hash)
end
def find_closest(goal_pattern)
- if goal_pattern.strip.empty?
+ if !goal_pattern || goal_pattern.strip.empty?
nil
else
rank(goal_pattern).min do |(p1, score1), (p2, score2)|
@@ -50,7 +50,11 @@ def score(entities, goal_entities)
def position_score(entities, goal_entities)
goal_entities.each_with_index.inject(0) do |sum, (goal_entity, index)|
- sum + ((entities.index(goal_entity) || 0) - index).abs
+ if found = entities.index(goal_entity)
+ sum + (found - index).abs
+ else
+ sum
+ end
end
end
@@ -63,9 +67,10 @@ def exist_score(entities, goal_entities)
def count_score(entities, goal_entities)
goal_entities.inject(0) do |sum, goal_entity|
if found_entity = entities.select { |entity| entity[0] == goal_entity[0] }.first
- sum += (found_entity.size - goal_entity.size).abs
+ sum + (found_entity.size - goal_entity.size).abs
+ else
+ sum
end
- sum
end
end
View
6 lib/twitter_cldr/tokenizers/calendars/datetime_tokenizer.rb
@@ -61,7 +61,7 @@ def additional_format_selector
protected
def merge_token_type_regexes(first, second)
- TwitterCldr::Utils.deep_merge(first, second) do |left, right|
+ TwitterCldr::Utils.deep_merge_hash(first, second) do |left, right|
if right.is_a?(Regexp) && left.is_a?(Regexp)
Regexp.union(left, right)
else
@@ -101,8 +101,8 @@ def mirror_resource(options)
def init_placeholders
@placeholders = [
- { :name => :time, :object => TwitterCldr::Tokenizers::TimeTokenizer.new(:locale => @locale, :calendar_type => @calendar_type) },
- { :name => :date, :object => TwitterCldr::Tokenizers::DateTokenizer.new(:locale => @locale, :calendar_type => @calendar_type) }
+ { :name => :time, :object => TwitterCldr::Tokenizers::TimeTokenizer.new(:locale => @locale, :calendar_type => @calendar_type) },
+ { :name => :date, :object => TwitterCldr::Tokenizers::DateTokenizer.new(:locale => @locale, :calendar_type => @calendar_type) }
]
end
View
20 lib/twitter_cldr/utils.rb
@@ -34,15 +34,19 @@ def deep_merge!(first, second)
first
end
- def deep_merge(first, second, &block)
- if first.is_a?(Hash) && second.is_a?(Hash)
- second.inject({}) { |ret, (key, val)| ret[key] = deep_merge(first[key], val, &block); ret }
- elsif first.is_a?(Array) && second.is_a?(Array)
- second.each_with_index.map { |elem, index| deep_merge(first[index], elem, &block) }
- else
- return yield first, second if block_given?
- second.dup
+ def deep_merge_hash(first, second, &block)
+ target = first.dup
+
+ second.keys.each do |key|
+ if second[key].is_a?(Hash) && first[key].is_a?(Hash)
+ target[key] = deep_merge_hash(target[key], second[key], &block)
+ next
+ end
+
+ target[key] = block_given? ? yield(first[key], second[key]) : second[key]
end
+
+ target
end
def compute_cache_key(*pieces)
View
2 spec/bidi/bidi_spec.rb
@@ -20,7 +20,7 @@ def expand_bitset_str(bitset)
end
describe Bidi do
- it "should pass the derived tests in classpath_bidi_test.txt" do
+ it "should pass the derived tests in classpath_bidi_test.txt", :slow => true do
expected_level_data = []
expected_reorder_data = []
num_failed = 0
View
48 spec/formatters/calendars/datetime_formatter_spec.rb
@@ -416,5 +416,53 @@
@formatter.send(:era, Date.new(2012, 1, 1), 'GGGG', 4).should == "Anno Domini"
@formatter.send(:era, Date.new(-1, 1, 1), 'GGGG', 4).should == "Before Christ"
end
+
+ it "should fall back if the calendar doesn't contain the appropriate era data" do
+ stub(@formatter.tokenizer).calendar do
+ {
+ :eras => {
+ :abbr => {
+ 0 => "abbr0",
+ 1 => "abbr1"
+ },
+ :name => {
+ 0 => "name0"
+ }
+ }
+ }
+ end
+
+ date = Date.new(2012, 1, 1)
+ mock.proxy(@formatter).era(date, "GGGG", 4) # first attempts to find full name era
+ mock.proxy(@formatter).era(date, "GGG", 3) # falls back to abbreviated era
+ @formatter.send(:era, date, 'GGGG', 4).should == "abbr1"
+ end
+ end
+
+ describe "#month_stand_alone" do
+ it "pattern L" do
+ @formatter.send(:month_stand_alone, Date.new(2010, 1, 1), 'L', 1).should == "1"
+ @formatter.send(:month_stand_alone, Date.new(2010, 10, 1), 'L', 1).should == "10"
+ end
+
+ it "pattern LL" do
+ @formatter.send(:month_stand_alone, Date.new(2010, 1, 1), 'LL', 2).should == "01"
+ @formatter.send(:month_stand_alone, Date.new(2010, 10, 1), 'LL', 2).should == "10"
+ end
+
+ it "pattern LLL" do
+ @formatter.send(:month_stand_alone, Date.new(2010, 1, 1), 'LLL', 3).should == "Jan"
+ @formatter.send(:month_stand_alone, Date.new(2010, 10, 1), 'LLL', 3).should == "Okt"
+ end
+
+ it "pattern LLLL" do
+ @formatter.send(:month_stand_alone, Date.new(2010, 1, 1), 'LLLL', 4).should == "Januar"
+ @formatter.send(:month_stand_alone, Date.new(2010, 10, 1), 'LLLL', 4).should == "Oktober"
+ end
+
+ it "pattern LLLLL" do
+ @formatter.send(:month_stand_alone, Date.new(2010, 1, 1), 'LLLLL', 5).should == "J"
+ @formatter.send(:month_stand_alone, Date.new(2010, 10, 1), 'LLLLL', 5).should == "O"
+ end
end
end
View
22 spec/localized/localized_datetime_spec.rb
@@ -32,7 +32,7 @@
it "should stringify with buddhist calendar" do
# Ensure that buddhist calendar data is present in th locale.
TwitterCldr.get_locale_resource(:th, :calendars)[:th][:calendars][:buddhist].should_not(
- be_nil, 'buddhist calendar is missing for :th locale (check resources/locales/th/calendars.yml)'
+ be_nil, 'buddhist calendar is missing for :th locale (check resources/locales/th/calendars.yml)'
)
#date_time.localize(:th, :calendar_type => :buddhist).to_full_s # It doesn't support era
@@ -72,7 +72,7 @@
it "don't raise errors for any locale" do
TwitterCldr.supported_locales.each do |locale|
(TwitterCldr::Tokenizers::DateTimeTokenizer::VALID_TYPES - [:additional]).each do |type|
- lambda { DateTime.now.localize(locale).send(:"to_#{type}_s") }.should_not raise_error
+ lambda { date_time.localize(locale).send(:"to_#{type}_s") }.should_not raise_error
end
end
end
@@ -81,11 +81,25 @@
TwitterCldr.supported_locales.each do |locale|
fmt = TwitterCldr::Formatters::DateTimeFormatter.new(:locale => locale)
fmt.additional_format_selector.patterns.each do |pattern|
- lambda { fmt.format(DateTime.now, :type => :additional, :format => pattern.to_s) }.should_not raise_error
- lambda { DateTime.now.localize(:locale).to_s(:format => pattern.to_s) }.should_not raise_error
+ lambda { fmt.format(date_time, :type => :additional, :format => pattern.to_s) }.should_not raise_error
+ lambda { date_time.localize(locale).to_s(:format => pattern.to_s) }.should_not raise_error
end
end
end
end
+ describe "#to_s" do
+ it "uses the default format if no :format is given" do
+ loc_date = date_time.localize
+ mock.proxy(loc_date).to_default_s
+ loc_date.to_s.should == "Sep 20, 1987, 10:05:00 p.m."
+ end
+
+ it "uses the given format instead of the default when specified" do
+ loc_date = date_time.localize
+ mock.proxy(loc_date).to_default_s.never
+ date_time.localize.to_s(:format => "MMMd").should == "Sep 20"
+ end
+ end
+
end
View
34 spec/tokenizers/base_spec.rb
@@ -216,4 +216,38 @@ def init_resources; end
tokens[4].value.should == "c"; tokens[4].type.should == :c
end
end
+
+ describe "#token_type_regexes_for" do
+ before(:each) do
+ @base.instance_variable_set(:'@token_type_regexes', {
+ :blarg => "blarg",
+ :else => "otherwise"
+ })
+ end
+
+ it "returns the correct regexes by name" do
+ @base.send(:token_type_regexes_for, :blarg).should == "blarg"
+ end
+
+ it "defaults to the regexes stored at :else if the name given can't be found" do
+ @base.send(:token_type_regexes_for, :foo).should == "otherwise"
+ end
+ end
+
+ describe "#token_splitter_regex_for" do
+ before(:each) do
+ @base.instance_variable_set(:'@token_splitter_regexes', {
+ :blarg => "blarg",
+ :else => "otherwise"
+ })
+ end
+
+ it "returns the correct regexes by name" do
+ @base.send(:token_splitter_regex_for, :blarg).should == "blarg"
+ end
+
+ it "defaults to the regexes stored at :else if the name given can't be found" do
+ @base.send(:token_splitter_regex_for, :foo).should == "otherwise"
+ end
+ end
end
View
131 spec/tokenizers/calendars/additional_date_format_selector_spec.rb
@@ -0,0 +1,131 @@
+# encoding: UTF-8
+
+# Copyright 2012 Twitter, Inc
+# http://www.apache.org/licenses/LICENSE-2.0
+
+require 'spec_helper'
+
+include TwitterCldr::Tokenizers
+
+describe AdditionalDateFormatSelector do
+ let(:selector) do
+ AdditionalDateFormatSelector.new(
+ TwitterCldr.get_locale_resource(:en, :calendars)[:en][:calendars][:gregorian][:additional_formats]
+ )
+ end
+
+ describe "#position_score" do
+ it "calculates the score based on relative offset from actual position" do
+ goal_entities = selector.send(:separate, "MMMyyd")
+ entities = selector.send(:separate, "d")
+ selector.send(:position_score, entities, goal_entities).should == 2
+ end
+
+ it "calculates a zero score if all entites are in the same positions" do
+ goal_entities = selector.send(:separate, "MMMyyd")
+ entities = selector.send(:separate, "MMMyyd")
+ selector.send(:position_score, entities, goal_entities).should == 0
+ end
+ end
+
+ describe "#exist_score" do
+ it "calculates a higher score if an entity doesn't exist" do
+ goal_entities = selector.send(:separate, "MMMd")
+ entities = selector.send(:separate, "d")
+ selector.send(:exist_score, entities, goal_entities).should == 1
+ end
+
+ it "calculates a zero score if all entities exist" do
+ goal_entities = selector.send(:separate, "MMMyyd")
+ entities = selector.send(:separate, "dMMMyy")
+ selector.send(:exist_score, entities, goal_entities).should == 0
+ end
+ end
+
+ describe "#count_score" do
+ it "calculates the score based on the difference in the length of each matching entity" do
+ goal_entities = selector.send(:separate, "MMMyyd")
+ entities = selector.send(:separate, "ddMMy")
+ selector.send(:count_score, entities, goal_entities).should == 3
+ end
+
+ it "calculates a zero score if all entities are the same length" do
+ goal_entities = selector.send(:separate, "MMMyyd")
+ entities = selector.send(:separate, "d")
+ selector.send(:count_score, entities, goal_entities).should == 0
+ end
+ end
+
+ describe "#score" do
+ it "calculates a cumulative score from position and count" do
+ goal_entities = selector.send(:separate, "MMMyydGG")
+ entities = selector.send(:separate, "ddMMGGyy")
+ selector.send(:exist_score, entities, goal_entities).should == 0
+ selector.send(:count_score, entities, goal_entities).should == 2
+ selector.send(:position_score, entities, goal_entities).should == 3
+ selector.send(:score, entities, goal_entities).should == 5
+ end
+
+ it "calculates a cumulative score from position, count, and existence (existence weighted by 2)" do
+ goal_entities = selector.send(:separate, "MMMyydGG")
+ entities = selector.send(:separate, "ddMMyy")
+ selector.send(:exist_score, entities, goal_entities).should == 1
+ selector.send(:count_score, entities, goal_entities).should == 2
+ selector.send(:position_score, entities, goal_entities).should == 1
+ # (exist_score * 2) + count_score + position_score
+ selector.send(:score, entities, goal_entities).should == 5
+ end
+ end
+
+ describe "#rank" do
+ it "returns a score for each available format" do
+ ranked_formats = selector.send(:rank, "MMMd")
+ ranked_formats["MMMd"].should == 0
+ ranked_formats["yMEd"].should == 4
+ ranked_formats["y"].should == 4
+ ranked_formats["EHms"].should == 4
+ ranked_formats["MMM"].should == 2
+ end
+ end
+
+ describe "#find_closest" do
+ it "returns an exact match if it exists" do
+ selector.find_closest("MMMd").should == "MMMd"
+ selector.find_closest("Hms").should == "Hms"
+ selector.find_closest("yQQQQ").should == "yQQQQ"
+ end
+
+ it "returns the next closest match (lowest score) if an exact match can't be found" do
+ selector.find_closest("MMMMd").should == "MMMd"
+ selector.find_closest("mHs").should == "Hms"
+ selector.find_closest("Ehd").should == "Ehm"
+ end
+
+ it "returns nil if an empty pattern is given" do
+ selector.find_closest(nil).should be_nil
+ selector.find_closest("").should be_nil
+ selector.find_closest(" ").should be_nil
+ end
+ end
+
+ describe "#separate" do
+ it "divides a string into entities by runs of equal characters" do
+ selector.send(:separate, "ddMMyy").should == ["dd", "MM", "yy"]
+ selector.send(:separate, "ddMMyyMM").should == ["dd", "MM", "yy", "MM"]
+ selector.send(:separate, "mmMM").should == ["mm", "MM"]
+ end
+ end
+
+ describe "#patterns" do
+ it "returns a list of all available patterns" do
+ patterns = selector.patterns
+ patterns.should be_a(Array)
+ patterns.should include(:MMMd)
+ patterns.should include(:yQQQ)
+ patterns.should include(:yQQQQ)
+ patterns.should include(:EHms)
+ patterns.should include(:d)
+ end
+ end
+
+end
View
33 spec/tokenizers/calendars/datetime_tokenizer_spec.rb
@@ -8,14 +8,16 @@
include TwitterCldr::Tokenizers
describe DateTimeTokenizer do
+ let(:tokenizer) { DateTimeTokenizer.new }
+
describe "#initialize" do
it "chooses gregorian as the calendar type if none is specified" do
DateTimeTokenizer.new.calendar_type.should == :gregorian
DateTimeTokenizer.new(:calendar_type => :julian).calendar_type.should == :julian
end
it "initializes individual date and time placeholder tokenizers" do
- placeholders = DateTimeTokenizer.new.placeholders
+ placeholders = tokenizer.placeholders
placeholders[0][:name].should == :time
placeholders[0][:object].should be_a(TimeTokenizer)
placeholders[1][:name].should == :date
@@ -25,7 +27,6 @@
describe "#tokens" do
it "should choose the default date time path if no other type is specified" do
- tokenizer = DateTimeTokenizer.new
mock.proxy(tokenizer.paths)[:default]
tokenizer.tokens
end
@@ -71,7 +72,6 @@
:e => 101
}
- tokenizer = DateTimeTokenizer.new
tokenizer.send(:mirror_resource, :from => from, :to => to)
to[:a].should == 1
@@ -88,4 +88,31 @@
from[:e][:f].should == 4
end
end
+
+ describe "#merge_token_type_regexes" do
+ it "merges the token type regex hash recursively, uniting regexes" do
+ first = {
+ :type1 => { :regex => /a/, :content => /aa/, :priority => 1 },
+ :type2 => { :regex => /b/, :priority => 2 },
+ }
+
+ second = {
+ :type1 => { :regex => /c/, :content => /cc/, :priority => 1 },
+ :type2 => { :regex => /d/, :priority => 2 },
+ }
+
+ tokenizer.send(:merge_token_type_regexes, first, second).should == {
+ :type1 => { :regex => Regexp.union(/a/, /c/), :content => Regexp.union(/aa/, /cc/), :priority => 1 },
+ :type2 => { :regex => Regexp.union(/b/, /d/), :priority => 2 }
+ }
+ end
+ end
+
+ describe "#pattern_for" do
+ it "returns the closest matching pattern if this tokenizer has been set up to handle additional date formats" do
+ tokenizer.instance_variable_set(:'@type', :additional)
+ tokenizer.instance_variable_set(:'@format', "MMMd")
+ tokenizer.send(:pattern_for, { :MMMd => "found me!" }).should == "found me!"
+ end
+ end
end
View
5 spec/utils/yaml/yaml_spec.rb
@@ -30,7 +30,10 @@
require 'yaml'
# Psych doesn't handle Unicode characters properly, have to use Syck instead.
-YAML::ENGINE.yamler = 'syck' if YAML.const_defined?(:ENGINE)
+if YAML.const_defined?(:ENGINE)
+ require 'syck'
+ YAML::ENGINE.yamler = 'syck'
+end
# There's an incompatibility in how ruby handles struct dumps
# between versions that's beyond our scope.
View
8 spec/utils_spec.rb
@@ -28,22 +28,26 @@
end
end
- describe "#deep_merge!" do
+ describe "#deep_merge! and #deep_merge_hash" do
it "combines two non-nested hashes with different keys" do
first = { :foo => "bar" }
+ TwitterCldr::Utils.deep_merge_hash(first, { :bar => "baz" }).should == { :foo => "bar", :bar => "baz" }
TwitterCldr::Utils.deep_merge!(first, { :bar => "baz" }).should == { :foo => "bar", :bar => "baz" }
end
it "combines two non-nested hashes with the same keys" do
first = { :foo => "bar" }
+ TwitterCldr::Utils.deep_merge_hash(first, { :foo => "baz" }).should == { :foo => "baz" }
TwitterCldr::Utils.deep_merge!(first, { :foo => "baz" }).should == { :foo => "baz" }
end
it "combines two nested hashes" do
first = { :foo => "bar", :second => { :bar => "baz", :twitter => "rocks" } }
second = { :foo => "baz", :third => { :whassup => "cool" }, :second => { :twitter => "rules" } }
+ result = { :foo => "baz", :second => { :bar => "baz", :twitter => "rules" }, :third => { :whassup => "cool" } }
+ TwitterCldr::Utils.deep_merge_hash(first, second).should == result
TwitterCldr::Utils.deep_merge!(first, second)
- first.should == { :foo => "baz", :second => { :bar => "baz", :twitter => "rules" }, :third => { :whassup => "cool" } }
+ first.should == result
end
it "replaces arrays with simple types" do

0 comments on commit 1c1f8eb

Please sign in to comment.
Something went wrong with that request. Please try again.