def test_invalid_allocation_invalid_unit( self ): """ Verifies parsing fails if the allocation's duration has invalid units. """ allocation = allocations_module.Allocations( None, configuration=TestAllocationInvalidAllocations.strict_config ) h_string = "invalid (incorrect units): 1 h" hr_string = "invalid (incorrect units): 2 hr" hrs_string = "invalid (incorrect units): 2 hrs" seconds_string = "invalid (incorrect units): 3600 seconds" # "h" or "H" instead of hours. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has wrong units - expected \"hours\" but received \"h\"".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + h_string ) with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has wrong units - expected \"hours\" but received \"H\"".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + h_string.upper() ) # "hr" or "HR" instead of hours. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has wrong units - expected \"hours\" but received \"hr\"".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + hr_string ) with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has wrong units - expected \"hours\" but received \"HR\"".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + hr_string.upper() ) # "hrs" or "HRS" instead of hours. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has wrong units - expected \"hours\" but received \"hrs\"".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + hrs_string ) with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has wrong units - expected \"hours\" but received \"HRS\"".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + hrs_string.upper() ) # "seconds" or "SECONDS" instead of hours. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has wrong units - expected \"hours\" but received \"seconds\"".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + seconds_string ) with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has wrong units - expected \"hours\" but received \"SECONDS\"".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + seconds_string.upper() )
def test_valid_allocation_categories( self ): """ Verifies allocations can have nested sub-categories. """ allocation = allocations_module.Allocations( None, configuration=TestAllocationValidAllocations.strict_config ) # number of nesting levels to test. this should be significantly larger # than any sane person would use. nesting_depth = 20 # walk through nesting levels and construct an allocation that has # nesting_level-many sub-categories. subcategory_prefix = "sub" subcategory_string = "" for nesting_index in range( nesting_depth ): test_string = "category{:s}{:s}: {:d} hour{:s}\n".format( subcategory_string, ")" * nesting_index, nesting_index + 1, "" if nesting_index == 0 else "s" ) allocation.parse( self.VALID_DATE_STRING + test_string ) # nest another subcategory for the next iteration. subcategory_string = "{:s} ({:s}category".format( subcategory_string, subcategory_prefix ) subcategory_prefix += "sub"
def test_valid_allocation_duration( self ): """ Verifies allocations have valid duration. Tests integral and fractional hours. """ integral_duration_string = "category: 1 hour\n" fractional_duration_1_string = "category: 0.5 hours\n" fractional_duration_2_string = "category: 0.25 hours\n" fractional_duration_3_string = "category: 1.5 hours\n" fractional_duration_4_string = "category: 1.0 hours\n" fractional_duration_5_string = "category: 1. hour\n" fractional_duration_6_string = "category: 10. hours\n" fractional_duration_7_string = "category: 10.0 hours\n" fractional_duration_8_string = "category: 10.5 hours\n" allocation = allocations_module.Allocations( None, configuration=TestAllocationValidAllocations.strict_config ) allocation.parse( self.VALID_DATE_STRING + integral_duration_string ) allocation.parse( self.VALID_DATE_STRING + fractional_duration_1_string ) allocation.parse( self.VALID_DATE_STRING + fractional_duration_2_string ) allocation.parse( self.VALID_DATE_STRING + fractional_duration_3_string ) allocation.parse( self.VALID_DATE_STRING + fractional_duration_4_string ) allocation.parse( self.VALID_DATE_STRING + fractional_duration_5_string ) allocation.parse( self.VALID_DATE_STRING + fractional_duration_6_string ) allocation.parse( self.VALID_DATE_STRING + fractional_duration_7_string ) allocation.parse( self.VALID_DATE_STRING + fractional_duration_8_string )
def test_invalid_date_month_day( self ): """ Verifies that date lines do not accept invalid <month>/<day> combinations. """ allocation = allocations_module.Allocations( None, configuration=TestAllocationInvalidAllocations.strict_config ) # segment our months by the number of days. February is handled # separately below. months_with_30_days = [4, 6, 9, 11] months_with_31_days = [1, 3, 5, 7, 8, 10, 12] # range of invalid months and days to test. invalid months are tested # with all combinations of their valid days and valid months are tested # with invalid days tailored to the month in question. invalid_standard_months = [0] + list( range( 13, 100 ) ) invalid_standard_days = [0] + list( range( 32, 100 ) ) valid_standard_days = range( 1, 32 ) valid_months = range( 1, 13 ) # verify we don't accept invalid months (0, 12+) with all of the valid # "standard" days (1-31). for month_number in invalid_standard_months: for day_number in valid_standard_days: date_string = "Monday {:d}/{:d}".format( month_number, day_number ) with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Month is invalid ({:d})".format( allocations_module.STRING_INPUT_LABEL, month_number ) ) ): allocation.parse( date_string ) # verify we don't accept invalid days (0, 31ish+) for valid months # (1-12), handling each month's duration properly. for month_number in valid_months: if month_number in months_with_30_days: invalid_days = [31] + invalid_standard_days elif month_number in months_with_31_days: invalid_days = invalid_standard_days elif month_number == 2: invalid_days = [30, 31] + invalid_standard_days for day_number in invalid_days: date_string = "Monday {:d}/{:d}".format( month_number, day_number ) with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Date is invalid ({:d}/{:d})".format( allocations_module.STRING_INPUT_LABEL, month_number, day_number ) ) ): allocation.parse( date_string )
def test_valid_allocation_multiline( self ): """ Verifies that multi-line allocations are parsed properly. """ example_1_string = ("category1 (subcategoryA): 1 hour\n" + "category2: 3 hours\n" "category1 (subcategoryA): .75 hours\n" + "category2 (subcategoryB): 1.0 hour\n") allocation = allocations_module.Allocations( None, configuration=TestAllocationValidAllocations.strict_config ) allocation.parse( self.VALID_DATE_STRING + example_1_string )
def test_invalid_allocation_form( self ): """ Verifies parsing fails if the allocation is not of the form: <categories>: <duration> """ allocation = allocations_module.Allocations( None, configuration=TestAllocationInvalidAllocations.strict_config ) # XXX: these need to be tested way more thoroughly # category_only_string = "invalid:\n" # multiple_separators_string = "invalid: 0.5: hours\n" no_separator_string = "invalid 0.5 hours\n" duration_only_string = ": 0.5 hours\n" # separator_only_string = ":\n" # category only, no duration. # with self.assertRaisesRegex( ValueError, # re.escape( "{:s}:{:d} - Allocation is not well formed (\"{:s}\")".format( # allocations_module.STRING_INPUT_LABEL, # self.FIRST_INVALID_LINE_NUMBER, # category_only_string.strip() ) ) ): # allocation.parse( self.VALID_DATE_STRING + category_only_string ) # multiple separators. # with self.assertRaisesRegex( ValueError, # re.escape( "{:s}:{:d} - Allocation is not well formed (\"{:s}\")".format( # allocations_module.STRING_INPUT_LABEL, # self.FIRST_INVALID_LINE_NUMBER, # multiple_separators_string.strip() ) ) ): # allocation.parse( self.VALID_DATE_STRING + multiple_separators_string ) # no separator string between category and duration. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation is not well formed (\"{:s}\")".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER, no_separator_string.strip() ) ) ): allocation.parse( self.VALID_DATE_STRING + no_separator_string ) # duration only, no category. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation is not well formed (\"{:s}\")".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER, duration_only_string.strip() ) ) ): allocation.parse( self.VALID_DATE_STRING + duration_only_string )
def test_invalid_allocation_missing_unit( self ): """ Verifies parsing fails if the allocation's duration does not have its units specified. """ allocation = allocations_module.Allocations( None, configuration=TestAllocationInvalidAllocations.strict_config ) input_string = "invalid (no unit): .5\n" with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation is missing units (\"{:s}\")".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER, input_string.strip() ) ) ): allocation.parse( self.VALID_DATE_STRING + input_string )
def test_valid_allocation_form( self ): """ Verifies parsing of allocations with the forms: <category>: <duration> <category> (<subcategory>): <duration> <category> (<subcategory> (<subsubcategory>)): <duration> """ single_category_string = "category: 0.5 hours\n" subcategory_string = "category (sub-category): 1 hour\n" subsubcategory_string = "category (sub-category (sub-category)): 2 hours\n" allocation = allocations_module.Allocations( None, configuration=TestAllocationValidAllocations.strict_config ) allocation.parse( self.VALID_DATE_STRING + single_category_string ) allocation.parse( self.VALID_DATE_STRING + subcategory_string ) allocation.parse( self.VALID_DATE_STRING + subsubcategory_string )
def test_valid_allocation_ignored_lines( self ): """ Verifies allocations with non-recognized lines do not trigger errors. """ empty_line_string = "\n" commented_invalid_allocation_string = "# category () ( (): XYZ hours\n" divider_string = "############ divider #############\n" duration_notes_1_string = "07:00-08:00\n" duration_notes_2_string = "07:00-08:00 (1 hour)\n" duration_notes_3_string = "09:15-19:00 20:15-23:00 (12.5 hours)\n" duration_notes_4_string = "07:00-12:00 13:00-\n" allocation = allocations_module.Allocations( None, configuration=TestAllocationValidAllocations.strict_config ) allocation.parse( self.VALID_DATE_STRING + empty_line_string ) allocation.parse( self.VALID_DATE_STRING + commented_invalid_allocation_string ) allocation.parse( self.VALID_DATE_STRING + divider_string ) allocation.parse( self.VALID_DATE_STRING + duration_notes_1_string ) allocation.parse( self.VALID_DATE_STRING + duration_notes_2_string ) allocation.parse( self.VALID_DATE_STRING + duration_notes_3_string ) allocation.parse( self.VALID_DATE_STRING + duration_notes_4_string )
def test_invalid_allocation_invalid_categories( self ): """ Verifies parsing fails if the allocation's categories are invalid. """ allocation = allocations_module.Allocations( None, configuration=TestAllocationInvalidAllocations.strict_config ) # empty categories. empty_category_1_string = ": 1 hour" empty_category_2_string = " : 1 hour" empty_category_3_string = "(empty category): 1 hour" empty_category_4_string = " (empty category): 1 hour" # empty sub-categories. empty_subcategory_1_string = "invalid (): 1 hour" empty_subcategory_2_string = "invalid ( ): 1 hour" empty_subcategory_3_string = "(): 1 hour" empty_subcategory_4_string = " (): 1 hour" # empty sub-sub-categories. empty_subsubcategory_1_string = "invalid (subcategory ()): 1 hour" empty_subsubcategory_2_string = "invalid (subcategory ( )): 1 hour" # unbalanced sub-categories. unbalanced_subcategory_1_string = "invalid (: 1 hour" unbalanced_subcategory_2_string = "invalid ): 1 hour" unbalanced_subcategory_3_string = "invalid )unbalanced): 1 hour" unbalanced_subcategory_4_string = "invalid (unbalanced (a): 1 hour" # sub-categories that aren't nested. unnested_subcategory_1_string = "invalid (correct) (unnested): 1 hour" unnested_subcategory_2_string = "invalid (sub-category (correct) (unnested)): 1 hour" # empty category. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation is not well formed".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + empty_category_1_string ) # empty category, after normalization. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation is not well formed".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + empty_category_2_string ) # empty category, with sub-categories. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has an empty category".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + empty_category_3_string ) # empty category, with sub-categories. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has an empty category".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + empty_category_4_string ) # category with an empty sub-category. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has an empty sub-category (nesting level 1)".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + empty_subcategory_1_string ) # category with an empty sub-category (after whitespace normalization). with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has an empty sub-category (nesting level 1)".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + empty_subcategory_2_string ) # empty category with an empty sub-category. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has an empty category".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + empty_subcategory_3_string ) # empty category (after whitespace normalization) with an empty sub-category. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has an empty category".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + empty_subcategory_4_string ) # category and sub-category with an empty sub-sub-category. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has an empty sub-category (nesting level 2)".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + empty_subsubcategory_1_string ) # category and sub-category with an empty sub-sub-category (after # whitespace normalization). with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has an empty sub-category (nesting level 2)".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + empty_subsubcategory_2_string ) # unbalanced sub-category parentheses (open-only). with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has an unmatched open parenthesis".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + unbalanced_subcategory_1_string ) # unbalanced sub-category parentheses (close-only). with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has a closing parenthesis without an open".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + unbalanced_subcategory_2_string ) # unbalanced sub-category parentheses (typo, close when an open was # needed). with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has a closing parenthesis without an open".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + unbalanced_subcategory_3_string ) # unbalanced sub-category parentheses (missing close parenthesis for # sub-category after sub-sub-category). with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has an unmatched open parenthesis".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + unbalanced_subcategory_4_string ) # two sub-categories for the category. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has multiple sub-categories".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + unnested_subcategory_1_string ) # two sub-categories for the category. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has multiple sub-categories".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + unnested_subcategory_1_string ) # two sub-sub-categories for the sub-category. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has multiple sub-categories".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + unnested_subcategory_2_string )
def test_invalid_allocation_invalid_duration( self ): """ Verifies parsing fails if the allocation's duration is invalid. """ allocation = allocations_module.Allocations( None, configuration=TestAllocationInvalidAllocations.strict_config ) negative_integer_string = "invalid (negative duration): -1 hours" negative_float_string = "invalid (negative duration): -1.0 hours" zero_integer_string = "invalid (zero duration): 0 hours" zero_float_1_string = "invalid (zero duration): 0.0 hours" zero_float_2_string = "invalid (zero duration): 0.00000 hours" zero_float_3_string = "invalid (zero duration): .0 hours" zero_float_4_string = "invalid (zero duration): -.0 hours" invalid_time_1_string = "invalid (zero duration): 1e0 hours" invalid_time_2_string = "invalid (zero duration): 1a2b3c hours" invalid_time_3_string = "invalid (zero duration): XXX hours" # negative integer duration. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has invalid duration".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + negative_integer_string ) # negative floating point duration. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has invalid duration".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + negative_float_string ) # zero integer duration. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has invalid duration".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + zero_integer_string ) # zero floating point duration. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has invalid duration".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + zero_float_1_string ) # zero floating point duration (with extra zeros). with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has invalid duration".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + zero_float_2_string ) # zero floating point duration (no leading zero). with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has invalid duration".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + zero_float_3_string ) # zero floating point duration (no leading zero, with a negative sign). with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has invalid duration".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + zero_float_4_string ) # scientific notation duration. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has invalid duration".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + invalid_time_1_string ) # hexadecimal notation duration. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has invalid duration".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + invalid_time_2_string ) # non-numeric duration. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:{:d} - Allocation has invalid duration".format( allocations_module.STRING_INPUT_LABEL, self.FIRST_INVALID_LINE_NUMBER ) ) ): allocation.parse( self.VALID_DATE_STRING + invalid_time_3_string )
def test_invalid_date_form( self ): """ Verifies that date lines with invalid formatting are not accepted. Non-capitalized and abbreviated weekdays are verified as rejected, as well as <month>/<date> combinations that are either missing a component or have extraneous whitespace in them. """ allocation = allocations_module.Allocations( None, configuration=TestAllocationInvalidAllocations.strict_config ) valid_weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] invalid_weekdays = ["Sun", "Mon", "Tues", "Weds", "Thurs", "Fri", "Sat"] valid_month_day_string = "1/1\n" invalid_date_1_string = "Monday 1 /1\n" invalid_date_2_string = "Monday 1/ 1\n" invalid_date_3_string = "Monday /1\n" invalid_date_4_string = "Monday 1/\n" # verify that non-capitalized weekdays with a valid date are invalid. for weekday in valid_weekdays: lowercase_date_string = "{:s} {:s}\n".format( weekday.lower(), valid_month_day_string ) uppercase_date_string = "{:s} {:s}\n".format( weekday.upper(), valid_month_day_string ) # lowercase weekday. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Invalid weekday in date ({:s})".format( allocations_module.STRING_INPUT_LABEL, weekday.lower() ) ) ): allocation.parse( lowercase_date_string ) # uppercase weekday. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Invalid weekday in date ({:s})".format( allocations_module.STRING_INPUT_LABEL, weekday.upper() ) ) ): allocation.parse( uppercase_date_string ) # verify that abbreviated weekdays (capitalized, lowercase, and # uppercase) are invalid. for weekday in invalid_weekdays: date_string = "{:s} {:s}\n".format( weekday, valid_month_day_string ) lowercase_date_string = "{:s} {:s}\n".format( weekday.lower(), valid_month_day_string ) uppercase_date_string = "{:s} {:s}\n".format( weekday.upper(), valid_month_day_string ) # abbreviated weekday. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Invalid weekday in date ({:s})".format( allocations_module.STRING_INPUT_LABEL, weekday ) ) ): allocation.parse( date_string ) # lowercase abbreviated weekday. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Invalid weekday in date ({:s})".format( allocations_module.STRING_INPUT_LABEL, weekday.lower() ) ) ): allocation.parse( lowercase_date_string ) # uppercase abbreviated weekday. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Invalid weekday in date ({:s})".format( allocations_module.STRING_INPUT_LABEL, weekday.upper() ) ) ): allocation.parse( uppercase_date_string ) # verify invalid <month>/<date> forms aren't accepted. with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Date is not well formed".format( allocations_module.STRING_INPUT_LABEL ) ) ): allocation.parse( invalid_date_1_string ) with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Date is not well formed".format( allocations_module.STRING_INPUT_LABEL ) ) ): allocation.parse( invalid_date_2_string ) with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Date is not well formed".format( allocations_module.STRING_INPUT_LABEL ) ) ): allocation.parse( invalid_date_3_string ) with self.assertRaisesRegex( ValueError, re.escape( "{:s}:1 - Date is not well formed".format( allocations_module.STRING_INPUT_LABEL ) ) ): allocation.parse( invalid_date_4_string )
def test_valid_date_month_day( self ): """ Verifies the form and content of date lines. Iterates through each combination of <weekday> and <month>/<date> and verifies that both un- and zero-padded combinations of <month>/<date> are accepted. """ weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] months_with_30_days = [4, 6, 9, 11] months_with_31_days = [1, 3, 5, 7, 8, 10, 12] allocation = allocations_module.Allocations( None, configuration=TestAllocationValidAllocations.strict_config ) # iterate through all the possibilities of weekday and month/date. for weekday in weekdays: # handle February since there is only one month with 28 or 29 days. for day_number in range( 1, 30 ): date_string = "{:s} 2/{:d}\n".format( weekday, day_number ) padded_date_1_string = "{:s} 2/{:02d}\n".format( weekday, day_number ) padded_date_2_string = "{:s} 02/{:02d}\n".format( weekday, day_number ) allocation.parse( date_string ) allocation.parse( padded_date_1_string ) allocation.parse( padded_date_2_string ) # handle the months with 30 days in them. for month_number in months_with_30_days: for day_number in range( 1, 31 ): date_string = "{:s} {:d}/{:d}\n".format( weekday, month_number, day_number ) padded_date_1_string = "{:s} {:d}/{:02d}\n".format( weekday, month_number, day_number ) padded_date_2_string = "{:s} {:02d}/{:02d}\n".format( weekday, month_number, day_number ) allocation.parse( date_string ) allocation.parse( padded_date_1_string ) allocation.parse( padded_date_2_string ) # handle the months with 31 days in them. for month_number in months_with_31_days: for day_number in range( 1, 32 ): date_string = "{:s} {:d}/{:d}\n".format( weekday, month_number, day_number ) padded_date_1_string = "{:s} {:d}/{:02d}\n".format( weekday, month_number, day_number ) padded_date_2_string = "{:s} {:02d}/{:02d}\n".format( weekday, month_number, day_number ) allocation.parse( date_string ) allocation.parse( padded_date_1_string ) allocation.parse( padded_date_2_string )