diff --git a/apprise/plugins/NotifyBase.py b/apprise/plugins/NotifyBase.py index 5e480c20..6f6d0c41 100644 --- a/apprise/plugins/NotifyBase.py +++ b/apprise/plugins/NotifyBase.py @@ -199,6 +199,54 @@ class NotifyBase(URLBase): }, }) + # + # Overflow Defaults / Configuration applicable to SPLIT mode only + # + + # Display Count [X/X] + # ^^^^^^ + # \\\\\\ + # 6 characters (space + count) + # Display Count [XX/XX] + # ^^^^^^^^ + # \\\\\\\\ + # 8 characters (space + count) + # Display Count [XXX/XXX] + # ^^^^^^^^^^ + # \\\\\\\\\\ + # 10 characters (space + count) + # Display Count [XXXX/XXXX] + # ^^^^^^^^^^^^ + # \\\\\\\\\\\\ + # 12 characters (space + count) + # + # Given the above + some buffer we come up with the following: + # If this value is exceeded, display counts automatically shut off + overflow_max_display_count_width = 12 + + # The number of characters to reserver for whitespace buffering + # This is detected automatically, but you can enforce a value if + # you desire: + overflow_buffer = 0 + + # the min accepted length of a title to allow for a counter display + overflow_display_count_threshold = 130 + + # Whether or not when over-flow occurs, if the title should be repeated + # each time the message is split up + # - None: Detect + # - True: Always display title once + # - False: Display the title for each occurance + overflow_display_title_once = None + + # If this is set to to True: + # The title_maxlen should be considered as a subset of the body_maxlen + # Hence: len(title) + len(body) should never be greater then body_maxlen + # + # If set to False, then there is no corrorlation between title_maxlen + # restrictions and that of body_maxlen + overflow_amalgamate_title = False + def __init__(self, **kwargs): """ Initialize some general configuration that will keep things consistent @@ -454,7 +502,6 @@ class NotifyBase(URLBase): overflow = self.overflow_mode if self.title_maxlen <= 0 and len(title) > 0: - if self.notify_format == NotifyFormat.HTML: # Content is appended to body as html body = '<{open_tag}>{title}' \ @@ -490,17 +537,37 @@ class NotifyBase(URLBase): response.append({'body': body, 'title': title}) return response - elif len(title) > self.title_maxlen: - # Truncate our Title - title = title[:self.title_maxlen] + # a value of '2' allows for the \r\n that is applied when + # amalgamating the title + overflow_buffer = max(2, self.overflow_buffer) \ + if (self.title_maxlen == 0 and len(title)) \ + else self.overflow_buffer - if self.body_maxlen > self.title_maxlen - 2: - # Combine title length into body if defined (2 for \r\n) - body_maxlen = self.body_maxlen \ - if not title else self.body_maxlen - len(title) - 2 + # + # If we reach here in our code, then we're using TRUNCATE, or SPLIT + # actions which require some math to handle the data + # + + # Handle situations where our body and title are amalamated into one + # calculation + title_maxlen = self.title_maxlen \ + if not self.overflow_amalgamate_title \ + else min(len(title) + self.overflow_max_display_count_width, + self.title_maxlen, self.body_maxlen) + + if len(title) > title_maxlen: + # Truncate our Title + title = title[:title_maxlen].rstrip() + + if self.overflow_amalgamate_title and ( + self.body_maxlen - overflow_buffer) >= title_maxlen: + body_maxlen = (self.body_maxlen if not title else ( + self.body_maxlen - title_maxlen)) - overflow_buffer else: # status quo - body_maxlen = self.body_maxlen + body_maxlen = self.body_maxlen \ + if not self.overflow_amalgamate_title else \ + (self.body_maxlen - overflow_buffer) if body_maxlen > 0 and len(body) <= body_maxlen: response.append({'body': body, 'title': title}) @@ -509,43 +576,109 @@ class NotifyBase(URLBase): if overflow == OverflowMode.TRUNCATE: # Truncate our body and return response.append({ - 'body': body[:body_maxlen], + 'body': body[:body_maxlen].lstrip('\r\n\x0b\x0c').rstrip(), 'title': title, }) # For truncate mode, we're done now return response - # Display Count [XX/XX] - # ^^^^^^^^ - # \\\\\\\\ - # 8 characters (space + count) - display_count_width = 8 + if self.overflow_display_title_once is None: + # Detect if we only display our title once or not: + overflow_display_title_once = \ + True if self.overflow_amalgamate_title and \ + body_maxlen < self.overflow_display_count_threshold \ + else False + else: + # Take on defined value - # the min accepted length of a title to allow for a counter display - display_count_threshold = 130 - - show_counter = title and len(body) > body_maxlen \ - and self.title_maxlen > \ - (display_count_threshold + display_count_width) - - count = 0 - if show_counter: - count = int(len(body) / body_maxlen) \ - + (1 if len(body) % body_maxlen else 0) - - if len(title) > self.title_maxlen - display_count_width: - # Truncate our title further - title = title[:self.title_maxlen - display_count_width] + overflow_display_title_once = self.overflow_display_title_once # If we reach here, then we are in SPLIT mode. # For here, we want to split the message as many times as we have to # in order to fit it within the designated limits. - response = [{ - 'body': body[i: i + body_maxlen], - 'title': title + ( - '' if not count else - ' [{:02}/{:02}]'.format(idx, count))} for idx, i in - enumerate(range(0, len(body), body_maxlen), start=1)] + if not overflow_display_title_once and not ( + # edge case that can occur when overflow_display_title_once is + # forced off, but no body exists + self.overflow_amalgamate_title and body_maxlen <= 0): + + show_counter = title and len(body) > body_maxlen and \ + ((self.overflow_amalgamate_title and + body_maxlen >= self.overflow_display_count_threshold) or + (not self.overflow_amalgamate_title and + title_maxlen > self.overflow_display_count_threshold)) and ( + title_maxlen > (self.overflow_max_display_count_width + + overflow_buffer) and + self.title_maxlen >= self.overflow_display_count_threshold) + + count = 0 + template = '' + if show_counter: + # introduce padding + body_maxlen -= overflow_buffer + + count = int(len(body) / body_maxlen) \ + + (1 if len(body) % body_maxlen else 0) + + # Detect padding and prepare template + digits = len(str(count)) + template = ' [{:0%d}/{:0%d}]' % (digits, digits) + + # Update our counter + overflow_display_count_width = 4 + (digits * 2) + if overflow_display_count_width <= \ + self.overflow_max_display_count_width: + if len(title) > \ + title_maxlen - overflow_display_count_width: + # Truncate our title further + title = title[:title_maxlen - + overflow_display_count_width] + + else: # Way to many messages to display + show_counter = False + + response = [{ + 'body': body[i: i + body_maxlen] + .lstrip('\r\n\x0b\x0c').rstrip(), + 'title': title + ( + '' if not show_counter else + template.format(idx, count))} for idx, i in + enumerate(range(0, len(body), body_maxlen), start=1)] + + else: # Display title once and move on + response = [] + try: + i = range(0, len(body), body_maxlen)[0] + + response.append({ + 'body': body[i: i + body_maxlen] + .lstrip('\r\n\x0b\x0c').rstrip(), + 'title': title, + }) + + except (ValueError, IndexError): + # IndexError: + # - This happens if there simply was no body to display + + # ValueError: + # - This happens when body_maxlen < 0 (due to title being + # so large) + + # No worries; send title along + response.append({ + 'body': '', + 'title': title, + }) + + # Ensure our start is set properly + body_maxlen = 0 + + # Now re-calculate based on the increased length + for i in range(body_maxlen, len(body), self.body_maxlen): + response.append({ + 'body': body[i: i + self.body_maxlen] + .lstrip('\r\n\x0b\x0c').rstrip(), + 'title': '', + }) return response diff --git a/apprise/plugins/NotifyDiscord.py b/apprise/plugins/NotifyDiscord.py index 839980a4..c6af9b9d 100644 --- a/apprise/plugins/NotifyDiscord.py +++ b/apprise/plugins/NotifyDiscord.py @@ -105,6 +105,10 @@ class NotifyDiscord(NotifyBase): # The maximum allowable characters allowed in the body per message body_maxlen = 2000 + # The 2000 characters above defined by the body_maxlen include that of the + # title. Setting this to True ensures overflow options behave properly + overflow_amalgamate_title = True + # Discord has a limit of the number of fields you can include in an # embeds message. This value allows the discord message to safely # break into multiple messages to handle these cases. diff --git a/test/test_plugin_discord.py b/test/test_plugin_discord.py index e68af18f..fb75833f 100644 --- a/test/test_plugin_discord.py +++ b/test/test_plugin_discord.py @@ -652,7 +652,7 @@ def test_plugin_discord_overflow(mock_post): # Ensure we never exceed 2000 characters for entry in results: assert len(entry['title']) <= instance.title_maxlen - assert len(entry['title']) + len(entry['body']) < instance.body_maxlen + assert len(entry['title']) + len(entry['body']) <= instance.body_maxlen @mock.patch('requests.post') diff --git a/test/test_rest_plugins.py b/test/test_rest_plugins.py index ad9385ff..f45d8f04 100644 --- a/test/test_rest_plugins.py +++ b/test/test_rest_plugins.py @@ -45,9 +45,9 @@ import logging logging.disable(logging.CRITICAL) -def test_notify_overflow_truncate(): +def test_notify_overflow_truncate_with_amalgamation(): """ - API: Overflow Truncate Functionality Testing + API: Overflow With Amalgamation Truncate Functionality Testing """ # @@ -80,6 +80,9 @@ def test_notify_overflow_truncate(): # Test title max length title_maxlen = 10 + # With amalgamation + overflow_amalgamate_title = True + def __init__(self, *args, **kwargs): super().__init__(**kwargs) @@ -98,12 +101,15 @@ def test_notify_overflow_truncate(): # Verify that we break the title to a max length of our title_max # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title, overflow=None) + chunks = obj._apply_overflow(body="", title="", overflow=None) + chunks = obj._apply_overflow(body=body, title="", overflow=None) chunks = obj._apply_overflow(body=body, title=title, overflow=None) chunks = obj._apply_overflow( body=body, title=title, overflow=OverflowMode.SPLIT) assert len(chunks) == 1 - assert body.rstrip() == chunks[0].get('body') - assert title[0:TestNotification.title_maxlen] == chunks[0].get('title') + assert body.lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') + assert title[0:obj.title_maxlen] == chunks[0].get('title') # # Next Test: Line Count Control @@ -117,6 +123,9 @@ def test_notify_overflow_truncate(): # Maximum number of lines body_max_line_count = 5 + # With amalgamation + overflow_amalgamate_title = True + def __init__(self, *args, **kwargs): super().__init__(**kwargs) @@ -130,11 +139,13 @@ def test_notify_overflow_truncate(): # Verify that we break the title to a max length of our title_max # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) assert len(chunks) == 1 - assert len(chunks[0].get('body').split('\n')) == \ - TestNotification.body_max_line_count - assert title[0:TestNotification.title_maxlen] == chunks[0].get('title') + assert len(chunks[0].get('body').split('\n')) == obj.body_max_line_count + assert title[0:obj.title_maxlen] == chunks[0].get('title') # # Next Test: Truncated body @@ -148,6 +159,9 @@ def test_notify_overflow_truncate(): # Enforce a body length of just 10 body_maxlen = 10 + # With amalgamation + overflow_amalgamate_title = True + def __init__(self, *args, **kwargs): super().__init__(**kwargs) @@ -161,11 +175,50 @@ def test_notify_overflow_truncate(): # Verify that we break the title to a max length of our title_max # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) assert len(chunks) == 1 - # -2 because \r\n are factored into calculation (safe whitespace) - assert body[0:TestNotification.body_maxlen] == chunks[0].get('body') - assert title == chunks[0].get('title') + # Body is lost as the title prevails over everything + assert chunks[0].get('body') == '' + # Title is not longer then the maximum size the body can be due to + # amalgamationflag: + assert title[:obj.body_maxlen] == chunks[0].get('title') + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length of just 10 + body_maxlen = 10 + + # With amalgamation + overflow_amalgamate_title = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.TRUNCATE) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + + assert chunks[0].get('body') == '' + # body_maxlen prevails due to it being smaller and amalgamation flag set + assert title[0:obj.body_maxlen] == chunks[0].get('title') # # Next Test: Append title to body + Truncated body @@ -179,6 +232,9 @@ def test_notify_overflow_truncate(): # Enforce a body length of just 100 body_maxlen = 100 + # With amalgamation + overflow_amalgamate_title = True + def __init__(self, *args, **kwargs): super().__init__(**kwargs) @@ -194,14 +250,23 @@ def test_notify_overflow_truncate(): # and that the body remains untouched obj.notify_format = NotifyFormat.HTML + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) assert len(chunks) == 1 obj.notify_format = NotifyFormat.MARKDOWN + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) assert len(chunks) == 1 obj.notify_format = NotifyFormat.TEXT + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) assert len(chunks) == 1 @@ -211,13 +276,247 @@ def test_notify_overflow_truncate(): # The thing is, since the title is so large, all of the body was lost # and a good chunk of the title was too. The message sent will just be a # small portion of the title - assert len(chunks[0].get('body')) == TestNotification.body_maxlen - assert title[0:TestNotification.body_maxlen] == chunks[0].get('body') + assert len(chunks[0].get('body')) == obj.body_maxlen + assert title[0:obj.body_maxlen] == chunks[0].get('body') -def test_notify_overflow_split(): +def test_notify_overflow_truncate_no_amalgamation(): """ - API: Overflow Split Functionality Testing + API: Overflow No Amalgamation Truncate Functionality Testing + + """ + # + # A little preparation + # + + # Number of characters per line + row = 24 + + # Some variables we use to control the data we work with + body_len = 1024 + title_len = 1024 + + # Create a large body and title with random data + body = ''.join(choice(str_alpha + str_num + ' ') for _ in range(body_len)) + body = '\r\n'.join([body[i: i + row] for i in range(0, len(body), row)]) + + # the new lines add a large amount to our body; lets force the content + # back to being 1024 characters. + body = body[0:1024] + + # Create our title using random data + title = ''.join(choice(str_alpha + str_num) for _ in range(title_len)) + + # + # First Test: Truncated Title + # + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 10 + + # No amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # We should throw an exception because our specified overflow is wrong. + with pytest.raises(TypeError): + # Load our object + obj = TestNotification(overflow='invalid') + + # Load our object + obj = TestNotification(overflow=OverflowMode.TRUNCATE) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title, overflow=None) + chunks = obj._apply_overflow(body="", title="", overflow=None) + chunks = obj._apply_overflow(body=body, title="", overflow=None) + chunks = obj._apply_overflow(body=body, title=title, overflow=None) + chunks = obj._apply_overflow( + body=body, title=title, overflow=OverflowMode.SPLIT) + assert len(chunks) == 1 + assert body.lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Line Count Control + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 5 + + # Maximum number of lines + body_max_line_count = 5 + + # No amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.TRUNCATE) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert len(chunks[0].get('body').split('\n')) == obj.body_max_line_count + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Truncated body + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length of just 10 + body_maxlen = 10 + + # No amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.TRUNCATE) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert body[0:obj.body_maxlen]\ + .lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') + assert title == chunks[0].get('title') + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length of just 10 + body_maxlen = 10 + + # No amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.TRUNCATE) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + # No amalgamation set so our body aligns in size (no -2 like previous + # test) + assert body[0:obj.body_maxlen]\ + .lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') + assert title == chunks[0].get('title') + + # + # Next Test: Append title to body + Truncated body + # + + class TestNotification(NotifyBase): + + # Enforce no title + title_maxlen = 0 + + # Enforce a body length of just 100 + body_maxlen = 100 + + # No amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.TRUNCATE) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + + obj.notify_format = NotifyFormat.HTML + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + + obj.notify_format = NotifyFormat.MARKDOWN + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + + obj.notify_format = NotifyFormat.TEXT + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + + # The below line should be read carefully... We're actually testing to see + # that our title is matched against our body. Behind the scenes, the title + # was appended to the body. The body was then truncated to the maxlen. + # The thing is, since the title is so large, all of the body was lost + # and a good chunk of the title was too. The message sent will just be a + # small portion of the title + assert len(chunks[0].get('body')) == obj.body_maxlen + assert title[0:obj.body_maxlen] == chunks[0].get('body') + + +def test_notify_overflow_split_with_amalgamation(): + """ + API: Overflow With Amalgamation Splits Functionality Testing """ @@ -251,6 +550,9 @@ def test_notify_overflow_split(): # Test title max length title_maxlen = 10 + # With amalgamation + overflow_amalgamate_title = True + def __init__(self, *args, **kwargs): super().__init__(**kwargs) @@ -264,10 +566,13 @@ def test_notify_overflow_split(): # Verify that we break the title to a max length of our title_max # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) assert len(chunks) == 1 - assert body == chunks[0].get('body') - assert title[0:TestNotification.title_maxlen] == chunks[0].get('title') + assert body.lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') + assert title[0:obj.title_maxlen] == chunks[0].get('title') # # Next Test: Line Count Control @@ -281,6 +586,9 @@ def test_notify_overflow_split(): # Maximum number of lines body_max_line_count = 5 + # With amalgamation + overflow_amalgamate_title = True + def __init__(self, *args, **kwargs): super().__init__(**kwargs) @@ -294,11 +602,13 @@ def test_notify_overflow_split(): # Verify that we break the title to a max length of our title_max # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) assert len(chunks) == 1 - assert len(chunks[0].get('body').split('\n')) == \ - TestNotification.body_max_line_count - assert title[0:TestNotification.title_maxlen] == chunks[0].get('title') + assert len(chunks[0].get('body').split('\n')) == obj.body_max_line_count + assert title[0:obj.title_maxlen] == chunks[0].get('title') # # Next Test: Split body @@ -312,6 +622,9 @@ def test_notify_overflow_split(): # Enforce a body length. Make sure it's an int. body_maxlen = int(body_len / 4) + # With amalgamation + overflow_amalgamate_title = True + def __init__(self, *args, **kwargs): super().__init__(**kwargs) @@ -325,34 +638,254 @@ def test_notify_overflow_split(): # Verify that we break the title to a max length of our title_max # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) offset = 0 - assert len(chunks) == 4 + assert len(chunks) == 5 for idx, chunk in enumerate(chunks, start=1): - # Our title has a counter added to it - assert title[:-8] == chunk.get('title')[:-8] - assert chunk.get('title')[-8:] == \ - ' [{:02}/{:02}]'.format(idx, len(chunks)) + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # No counter is displayed because our title is so enormous + # We switch to a display title on first message only + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The length of the body prevails our title due to it being + # so much smaller then our title length + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + # Our body is only broken up; not lost _body = chunk.get('body') - assert body[offset: len(_body) + offset].rstrip() == _body + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body offset += len(_body) # Another edge case where the title just isn't that long leaving # a lot of space for the [xx/xx] entries (no truncation needed) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title[:20]) offset = 0 - assert len(chunks) == 4 + c_len = len(' [X/X]') + assert len(chunks) == 5 for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + # Our title has a counter added to it - assert title[:20] == chunk.get('title')[:-8] - assert chunk.get('title')[-8:] == \ - ' [{:02}/{:02}]'.format(idx, len(chunks)) + assert title[:20] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:01}/{:01}]'.format(idx, len(chunks)) # Our body is only broken up; not lost _body = chunk.get('body') - assert body[offset: len(_body) + offset].rstrip() == _body + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body offset += len(_body) + # + # Test forcing overflow_display_title_once + # + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Only display title once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The body length prevails due to our amalgamation flag + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 400 + + # With amalgamation + overflow_amalgamate_title = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + c_len = len(' [XXXX/XXXX]') + assert len(chunks) == 2048 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has a counter added to it + assert title[:obj.title_maxlen][:-c_len] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:04}/{:04}]'.format(idx, len(chunks)) + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Body chunk is beyond 4 digits, so [XXXX/XXXX] is turned off + new_body = (body * 2500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 10240 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 150 + + # With amalgamation + overflow_amalgamate_title = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 5) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 36 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # Because 150 is what we set the title limit to + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + # # Next Test: Append title to body + split body # @@ -365,6 +898,9 @@ def test_notify_overflow_split(): # Enforce a body length based on the title. Make sure it's an int. body_maxlen = int(title_len / 4) + # With amalgamation + overflow_amalgamate_title = True + def __init__(self, *args, **kwargs): super().__init__(**kwargs) @@ -378,6 +914,9 @@ def test_notify_overflow_split(): # Verify that we break the title to a max length of our title_max # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) # Our final product is that our title has been appended to our body to @@ -392,16 +931,28 @@ def test_notify_overflow_split(): assert len(chunks) == ( # wrap division in int() so Python 3 doesn't convert it to a float on # us - int(len(bulk) / TestNotification.body_maxlen) + - (1 if len(bulk) % TestNotification.body_maxlen else 0)) + int(len(bulk) / obj.body_maxlen) + + (1 if len(bulk) % obj.body_maxlen else 0)) for chunk in chunks: + # Verification + assert len(chunk.get('title')) == 0 + assert len(chunk.get('body')) <= obj.body_maxlen + # Our title is empty every time assert chunk.get('title') == '' - + # Our body is only broken up; not lost _body = chunk.get('body') - assert bulk[offset: len(_body) + offset] == _body - offset += len(_body) + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(bulk[offset: len(_body) + offset]) - \ + len(bulk[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert bulk[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) # # Test case where our title_len is shorter then the value @@ -416,6 +967,9 @@ def test_notify_overflow_split(): # Enforce a body length. Make sure it's an int. body_maxlen = int(body_len / 4) + # With amalgamation + overflow_amalgamate_title = True + def __init__(self, *args, **kwargs): super().__init__(**kwargs) @@ -429,18 +983,2942 @@ def test_notify_overflow_split(): # Verify that we break the title to a max length of our title_max # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) offset = 0 assert len(chunks) == 7 for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + # Our title is truncated and no counter added - assert title[:100] == chunk.get('title') + assert title[:obj.title_maxlen] == chunk.get('title') # Our body is only broken up; not lost _body = chunk.get('body') - assert body[offset: len(_body) + offset].rstrip() == _body + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(body[offset: len(_body) + offset]) - \ + len(body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Scenario where the title length is larger than the body + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 50 + + # With amalgamation + overflow_amalgamate_title = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 22 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it due to it's length + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # Because 150 is what we set the title limit to 50 due + # to amalamation. The lowest value always prevails + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(body[offset: len(_body) + offset]) - \ + len(body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + +def test_notify_overflow_split_with_amalgamation_force_title_always(): + """ + API: Overflow With Amalgamation (title alaways Split Functionality Testing + + """ + + # + # A little preparation + # + + # Number of characters per line + row = 24 + + # Some variables we use to control the data we work with + body_len = 1024 + title_len = 1024 + + # Create a large body and title with random data + body = ''.join(choice(str_alpha + str_num) for _ in range(body_len)) + body = '\r\n'.join([body[i: i + row] for i in range(0, len(body), row)]) + + # the new lines add a large amount to our body; lets force the content + # back to being 1024 characters. + body = body[0:1024] + + # Create our title using random data + title = ''.join(choice(str_alpha + str_num) for _ in range(title_len)) + + # + # First Test: Truncated Title + # + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 10 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert body.lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Line Count Control + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 5 + + # Maximum number of lines + body_max_line_count = 5 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert len(chunks[0].get('body').split('\n')) == obj.body_max_line_count + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Split body + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # No counter is displayed because our title is so enormous + # We switch to a display title on first message only + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The length of the body prevails our title due to it being + # so much smaller then our title length + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body offset += len(_body) + # Another edge case where the title just isn't that long leaving + # a lot of space for the [xx/xx] entries (no truncation needed) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title[:20]) + offset = 0 + c_len = len(' [X/X]') + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has a counter added to it + assert title[:20] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:01}/{:01}]'.format(idx, len(chunks)) + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # + # Test forcing overflow_display_title_once + # + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The body length prevails due to our amalgamation flag + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 400 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + c_len = len(' [XXXX/XXXX]') + assert len(chunks) == 2048 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has a counter added to it + assert title[:obj.title_maxlen][:-c_len] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:04}/{:04}]'.format(idx, len(chunks)) + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Body chunk is beyond 4 digits, so [XXXX/XXXX] is turned off + new_body = (body * 2500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 10240 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 150 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 5) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + # overflow_display_title_once whle set to False is still ignored + # because our title_maxlen > body_maxlen and a full title was + # provided + assert len(chunks) == 36 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # Because 150 is what we set the title limit to + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + # overflow_display_title_once whle set to False is still ignored + # because our title_maxlen > body_maxlen and a full title was + # provided + assert len(chunks) == 36 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # Because 150 is what we set the title limit to + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Run again but with a smaller title + # + chunks = obj._apply_overflow(body=new_body, title=title[:30]) + offset = 0 + # overflow_display_title_once whle set to False is still ignored + # because our body_maxlen (after title has been calculated with it) + # is less then the overflow_display_count_threshold; hence a message + # must be a certain minimum size in order to kick in + assert len(chunks) == 48 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title doesn't change + assert len(chunk.get('title')) == 30 + assert title[:30] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset].rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Next Test: Append title to body + split body + # + + class TestNotification(NotifyBase): + + # Enforce no title + title_maxlen = 0 + + # Enforce a body length based on the title. Make sure it's an int. + body_maxlen = int(title_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + + # Our final product is that our title has been appended to our body to + # create one great big body. As a result we'll get quite a few lines back + # now. + offset = 0 + + # Our body will look like this in small chunks at the end of the day + bulk = title + '\r\n' + body + + # Due to the new line added to the end + assert len(chunks) == ( + # wrap division in int() so Python 3 doesn't convert it to a float on + # us + int(len(bulk) / obj.body_maxlen) + + (1 if len(bulk) % obj.body_maxlen else 0)) + + for chunk in chunks: + # Verification + assert len(chunk.get('title')) == 0 + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title is empty every time + assert chunk.get('title') == '' + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(bulk[offset: len(_body) + offset]) - \ + len(bulk[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert bulk[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Test case where our title_len is shorter then the value + # that would otherwise trigger the [XX/XX] elements + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 7 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title is truncated and no counter added + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(body[offset: len(_body) + offset]) - \ + len(body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Scenario where the title length is larger than the body + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 50 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + + # overflow_display_title_once whle set to False is still ignored + # because our title_maxlen > body_maxlen and a full title was + # provided + assert len(chunks) == 22 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it due to it's length + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # Because 150 is what we set the title limit to 50 due + # to amalamation. The lowest value always prevails + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(body[offset: len(_body) + offset]) - \ + len(body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + +def test_notify_overflow_split_with_amalgamation_force_title_once(): + """ + API: Overflow With Amalgamation (title once) Split Functionality Testing + + """ + + # + # A little preparation + # + + # Number of characters per line + row = 24 + + # Some variables we use to control the data we work with + body_len = 1024 + title_len = 1024 + + # Create a large body and title with random data + body = ''.join(choice(str_alpha + str_num) for _ in range(body_len)) + body = '\r\n'.join([body[i: i + row] for i in range(0, len(body), row)]) + + # the new lines add a large amount to our body; lets force the content + # back to being 1024 characters. + body = body[0:1024] + + # Create our title using random data + title = ''.join(choice(str_alpha + str_num) for _ in range(title_len)) + + # + # First Test: Truncated Title + # + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 10 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert body.lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Line Count Control + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 5 + + # Maximum number of lines + body_max_line_count = 5 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert len(chunks[0].get('body').split('\n')) == obj.body_max_line_count + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Split body + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # No counter is displayed because our title is so enormous + # We switch to a display title on first message only + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The length of the body prevails our title due to it being + # so much smaller then our title length + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # Another edge case where the title just isn't that long leaving + # a lot of space for the [xx/xx] entries (no truncation needed) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title[:20]) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + + else: + # The body length prevails due to our amalgamation flag + assert len(chunk.get('title')) == 20 + assert title[:20] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # + # Test forcing overflow_display_title_once + # + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Only display title once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The body length prevails due to our amalgamation flag + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 400 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 1281 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The body length prevails due to our amalgamation flag + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Body chunk is beyond 4 digits, so [XXXX/XXXX] is turned off + new_body = (body * 2500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 6401 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The body length prevails due to our amalgamation flag + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 150 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 5) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 36 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # Because 150 is what we set the title limit to + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Next Test: Append title to body + split body + # + + class TestNotification(NotifyBase): + + # Enforce no title + title_maxlen = 0 + + # Enforce a body length based on the title. Make sure it's an int. + body_maxlen = int(title_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + + # Our final product is that our title has been appended to our body to + # create one great big body. As a result we'll get quite a few lines back + # now. + offset = 0 + + # Our body will look like this in small chunks at the end of the day + bulk = title + '\r\n' + body + + # Due to the new line added to the end + assert len(chunks) == ( + # wrap division in int() so Python 3 doesn't convert it to a float on + # us + int(len(bulk) / obj.body_maxlen) + + (1 if len(bulk) % obj.body_maxlen else 0)) + + for chunk in chunks: + # Verification + assert len(chunk.get('title')) == 0 + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title is empty every time + assert chunk.get('title') == '' + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(bulk[offset: len(_body) + offset]) - \ + len(bulk[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert bulk[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Test case where our title_len is shorter then the value + # that would otherwise trigger the [XX/XX] elements + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The body length prevails due to our amalgamation flag + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(body[offset: len(_body) + offset]) - \ + len(body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Scenario where the title length is larger than the body + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 50 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 22 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it due to it's length + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # Because 150 is what we set the title limit to 50 due + # to amalamation. The lowest value always prevails + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(body[offset: len(_body) + offset]) - \ + len(body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + +def test_notify_overflow_split_no_amalgamation(): + """ + API: Overflow No Amalgamation Splits Functionality Testing + + """ + + # + # A little preparation + # + + # Number of characters per line + row = 24 + + # Some variables we use to control the data we work with + body_len = 1024 + title_len = 1024 + + # Create a large body and title with random data + body = ''.join(choice(str_alpha + str_num) for _ in range(body_len)) + body = '\r\n'.join([body[i: i + row] for i in range(0, len(body), row)]) + + # the new lines add a large amount to our body; lets force the content + # back to being 1024 characters. + body = body[0:1024] + + # Create our title using random data + title = ''.join(choice(str_alpha + str_num) for _ in range(title_len)) + + # + # First Test: Truncated Title + # + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 10 + + # No amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert body.lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Line Count Control + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 5 + + # Maximum number of lines + body_max_line_count = 5 + + # No amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert len(chunks[0].get('body').split('\n')) == obj.body_max_line_count + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Split body + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # No amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + c_len = len(' [X/X]') + assert len(chunks) == 4 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has a counter added to it + assert title[:-c_len] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:01}/{:01}]'.format(idx, len(chunks)) + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # Another edge case where the title just isn't that long leaving + # a lot of space for the [xx/xx] entries (no truncation needed) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title[:20]) + offset = 0 + assert len(chunks) == 4 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has a counter added to it + assert title[:20] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:01}/{:01}]'.format(idx, len(chunks)) + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 400 + + # No amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + c_len = len(' [XXXX/XXXX]') + assert len(chunks) == 1280 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + # Our title has a counter added to it + assert title[:obj.title_maxlen][:-c_len] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:04}/{:04}]'.format(idx, len(chunks)) + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Body chunk is beyond 4 digits, so [XXXX/XXXX] is turned off + new_body = (body * 4500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 11520 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + assert title[:obj.title_maxlen] == chunk.get('title') + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 150 + + # No amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 5) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 35 + c_len = len(' [XX/XX]') + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has a counter added to it + assert title[:obj.title_maxlen][:-c_len] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:02}/{:02}]'.format(idx, len(chunks)) + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Next Test: Append title to body + split body + # + + class TestNotification(NotifyBase): + + # Enforce no title + title_maxlen = 0 + + # Enforce a body length based on the title. Make sure it's an int. + body_maxlen = int(title_len / 4) + + # No Amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + + # Our final product is that our title has been appended to our body to + # create one great big body. As a result we'll get quite a few lines back + # now. + offset = 0 + + # Our body will look like this in small chunks at the end of the day + bulk = title + '\r\n' + body + + # Due to the new line added to the end + assert len(chunks) == ( + # wrap division in int() so Python 3 doesn't convert it to a float on + # us + int(len(bulk) / obj.body_maxlen) + + (1 if len(bulk) % obj.body_maxlen else 0)) + + for chunk in chunks: + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title is empty every time + assert chunk.get('title') == '' + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(bulk[offset: len(_body) + offset]) - \ + len(bulk[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert bulk[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Test case where our title_len is shorter then the value + # that would otherwise trigger the [XX/XX] elements + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # No Amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 4 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title is truncated and no counter added + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # + # Scenario where the title length is larger than the body + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 50 + + # No Amalgamation + overflow_amalgamate_title = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 21 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title is truncated and no counter added + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(body[offset: len(_body) + offset]) - \ + len(body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + +def test_notify_overflow_split_no_amalgamation_force_title_always(): + """ + API: Overflow No Amalgamation (title always) Split Functionality Testing + + """ + + # + # A little preparation + # + + # Number of characters per line + row = 24 + + # Some variables we use to control the data we work with + body_len = 1024 + title_len = 1024 + + # Create a large body and title with random data + body = ''.join(choice(str_alpha + str_num) for _ in range(body_len)) + body = '\r\n'.join([body[i: i + row] for i in range(0, len(body), row)]) + + # the new lines add a large amount to our body; lets force the content + # back to being 1024 characters. + body = body[0:1024] + + # Create our title using random data + title = ''.join(choice(str_alpha + str_num) for _ in range(title_len)) + + # + # First Test: Truncated Title + # + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 10 + + # No amalgamation + overflow_amalgamate_title = False + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert body.lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Line Count Control + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 5 + + # Maximum number of lines + body_max_line_count = 5 + + # No amalgamation + overflow_amalgamate_title = False + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert len(chunks[0].get('body').split('\n')) == obj.body_max_line_count + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Split body + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # No amalgamation + overflow_amalgamate_title = False + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + c_len = len(' [X/X]') + assert len(chunks) == 4 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has a counter added to it + assert title[:-c_len] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:01}/{:01}]'.format(idx, len(chunks)) + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # Another edge case where the title just isn't that long leaving + # a lot of space for the [xx/xx] entries (no truncation needed) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title[:20]) + offset = 0 + assert len(chunks) == 4 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has a counter added to it + assert title[:20] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:01}/{:01}]'.format(idx, len(chunks)) + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 400 + + # No amalgamation + overflow_amalgamate_title = False + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + c_len = len(' [XXXX/XXXX]') + assert len(chunks) == 1280 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + # Our title has a counter added to it + assert title[:obj.title_maxlen][:-c_len] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:04}/{:04}]'.format(idx, len(chunks)) + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Body chunk is beyond 4 digits, so [XXXX/XXXX] is turned off + new_body = (body * 4500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 11520 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + assert title[:obj.title_maxlen] == chunk.get('title') + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 150 + + # No amalgamation + overflow_amalgamate_title = False + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 5) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 35 + c_len = len(' [XX/XX]') + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has a counter added to it + assert title[:obj.title_maxlen][:-c_len] == chunk.get('title')[:-c_len] + assert chunk.get('title')[-c_len:] == \ + ' [{:02}/{:02}]'.format(idx, len(chunks)) + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Next Test: Append title to body + split body + # + + class TestNotification(NotifyBase): + + # Enforce no title + title_maxlen = 0 + + # Enforce a body length based on the title. Make sure it's an int. + body_maxlen = int(title_len / 4) + + # No Amalgamation + overflow_amalgamate_title = False + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + + # Our final product is that our title has been appended to our body to + # create one great big body. As a result we'll get quite a few lines back + # now. + offset = 0 + + # Our body will look like this in small chunks at the end of the day + bulk = title + '\r\n' + body + + # Due to the new line added to the end + assert len(chunks) == ( + # wrap division in int() so Python 3 doesn't convert it to a float on + # us + int(len(bulk) / obj.body_maxlen) + + (1 if len(bulk) % obj.body_maxlen else 0)) + + for chunk in chunks: + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title is empty every time + assert chunk.get('title') == '' + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(bulk[offset: len(_body) + offset]) - \ + len(bulk[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert bulk[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Test case where our title_len is shorter then the value + # that would otherwise trigger the [XX/XX] elements + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # No Amalgamation + overflow_amalgamate_title = False + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 4 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title is truncated and no counter added + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # + # Scenario where the title length is larger than the body + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 50 + + # No Amalgamation + overflow_amalgamate_title = False + + # Force title to be displayed always + overflow_display_title_once = False + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 21 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title is truncated and no counter added + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(body[offset: len(_body) + offset]) - \ + len(body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + +def test_notify_overflow_split_no_amalgamation_force_title_once(): + """ + API: Overflow No Amalgamation (title once) Split Functionality Testing + + """ + + # + # A little preparation + # + + # Number of characters per line + row = 24 + + # Some variables we use to control the data we work with + body_len = 1024 + title_len = 1024 + + # Create a large body and title with random data + body = ''.join(choice(str_alpha + str_num) for _ in range(body_len)) + body = '\r\n'.join([body[i: i + row] for i in range(0, len(body), row)]) + + # the new lines add a large amount to our body; lets force the content + # back to being 1024 characters. + body = body[0:1024] + + # Create our title using random data + title = ''.join(choice(str_alpha + str_num) for _ in range(title_len)) + + # + # First Test: Truncated Title + # + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 10 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert body.lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Line Count Control + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 5 + + # Maximum number of lines + body_max_line_count = 5 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + assert len(chunks) == 1 + assert len(chunks[0].get('body').split('\n')) == obj.body_max_line_count + assert title[0:obj.title_maxlen] == chunks[0].get('title') + + # + # Next Test: Split body + # + + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # No counter is displayed because our title is so enormous + # We switch to a display title on first message only + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The length of the body prevails our title due to it being + # so much smaller then our title length + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # Another edge case where the title just isn't that long leaving + # a lot of space for the [xx/xx] entries (no truncation needed) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title[:20]) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # No counter is displayed because our title is so enormous + # We switch to a display title on first message only + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The length of the body prevails our title due to it being + # so much smaller then our title length + assert len(chunk.get('title')) == 20 + assert title[:20] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # + # Test forcing overflow_display_title_once + # + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = title_len + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The body length prevails due to our amalgamation flag + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + assert body[offset: len(_body) + offset]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += len(_body) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 400 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 1281 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # No counter is displayed because our title is so enormous + # We switch to a display title on first message only + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The length of the body prevails our title due to it being + # so much smaller then our title length + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Body chunk is beyond 4 digits, so [XXXX/XXXX] is turned off + new_body = (body * 2500) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 6401 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # No counter is displayed because our title is so enormous + # We switch to a display title on first message only + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The length of the body prevails our title due to it being + # so much smaller then our title length + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # Test larger messages + # and that the body remains untouched + class TestNotification(NotifyBase): + + # Test title max length + title_maxlen = 150 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 150 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + new_body = (body * 5) + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=new_body, title="") + chunks = obj._apply_overflow(body=new_body, title=title) + offset = 0 + assert len(chunks) == 36 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # Because 150 is what we set the title limit to + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(new_body[offset: len(_body) + offset]) - \ + len(new_body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert new_body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Next Test: Append title to body + split body + # + + class TestNotification(NotifyBase): + + # Enforce no title + title_maxlen = 0 + + # Enforce a body length based on the title. Make sure it's an int. + body_maxlen = int(title_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + + # Our final product is that our title has been appended to our body to + # create one great big body. As a result we'll get quite a few lines back + # now. + offset = 0 + + # Our body will look like this in small chunks at the end of the day + bulk = title + '\r\n' + body + + # Due to the new line added to the end + assert len(chunks) == ( + # wrap division in int() so Python 3 doesn't convert it to a float on + # us + int(len(bulk) / obj.body_maxlen) + + (1 if len(bulk) % obj.body_maxlen else 0)) + + for chunk in chunks: + # Verification + assert len(chunk.get('title')) == 0 + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title is empty every time + assert chunk.get('title') == '' + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(bulk[offset: len(_body) + offset]) - \ + len(bulk[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert bulk[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Test case where our title_len is shorter then the value + # that would otherwise trigger the [XX/XX] elements + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = int(body_len / 4) + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 5 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # No counter is displayed because our title is so enormous + # We switch to a display title on first message only + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # The length of the body prevails our title due to it being + # so much smaller then our title length + assert len(chunk.get('title')) == obj.title_maxlen + assert title[:obj.title_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(body[offset: len(_body) + offset]) - \ + len(body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + + # + # Scenario where the title length is larger than the body + # + + class TestNotification(NotifyBase): + + # Set a small title length + title_maxlen = 100 + + # Enforce a body length. Make sure it's an int. + body_maxlen = 50 + + # With amalgamation + overflow_amalgamate_title = True + + # Force title displayed once + overflow_display_title_once = True + + def __init__(self, *args, **kwargs): + super().__init__(**kwargs) + + def notify(self, *args, **kwargs): + # Pretend everything is okay + return True + + # Load our object + obj = TestNotification(overflow=OverflowMode.SPLIT) + assert obj is not None + + # Verify that we break the title to a max length of our title_max + # and that the body remains untouched + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") + chunks = obj._apply_overflow(body=body, title=title) + offset = 0 + assert len(chunks) == 22 + for idx, chunk in enumerate(chunks, start=1): + # Verification + assert len(chunk.get('title')) <= obj.title_maxlen + assert len(chunk.get('body')) <= obj.body_maxlen + + # Our title has no counter added to it due to it's length + if idx > 1: + # Empty (no title displayed on following entries + assert chunk.get('title') == "" + else: + # Because 150 is what we set the title limit to 50 due + # to amalamation. The lowest value always prevails + assert len(chunk.get('title')) == obj.body_maxlen + assert title[:obj.body_maxlen] == chunk.get('title') + + # Our body is only broken up; not lost + _body = chunk.get('body') + + # Un-used whitespace is always cleaned up; make sure we account for + # this in our new calculation + ws_diff = len(body[offset: len(_body) + offset]) - \ + len(body[offset: len(_body) + offset] + .lstrip('\r\n\x0b\x0c').rstrip()) + + assert body[offset: len(_body) + offset + ws_diff]\ + .lstrip('\r\n\x0b\x0c').rstrip() == _body + offset += (len(_body) + ws_diff) + def test_notify_markdown_general(): """ @@ -478,6 +3956,9 @@ def test_notify_markdown_general(): title = " # " body = "**Test Body**" + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow(body=body, title=title) assert len(chunks) == 1 # whitspace is trimmed @@ -485,11 +3966,14 @@ def test_notify_markdown_general(): assert chunks[0].get('title') == "" # If we know our input is text however, we perform manipulation + chunks = obj._apply_overflow(body="", title=title) + chunks = obj._apply_overflow(body="", title="") + chunks = obj._apply_overflow(body=body, title="") chunks = obj._apply_overflow( body=body, title=title, body_format=NotifyFormat.TEXT) assert len(chunks) == 1 # Our title get's stripped off since it's not of valid markdown - assert body == chunks[0].get('body') + assert body.lstrip('\r\n\x0b\x0c').rstrip() == chunks[0].get('body') assert chunks[0].get('title') == ""