<reponame>rtweeks/intercom_test
from intercom_test.augmentation import origin_mapping_stream as subject

from contextlib import ExitStack
import io
from should_dsl import should, should_not
from tempfile import NamedTemporaryFile
import yaml

class StreamTests:
    def setUp(self):
        self.context = ExitStack()
        setup = self.context.enter_context
        
        self.content_file = setup(NamedTemporaryFile())
    
    def tearDown(self):
        self.context.close()
    
    def write_content(self, data: bytes):
        self.content_file.write(data)
        self.content_file.flush()
    
    def open_with_subject(self):
        return self.context.enter_context(
            subject.open(self.content_file.name)
        )
    
    def prep_case(self, content: str):
        self.unicode_content = content
        content = content.encode('utf8')
        self.write_content(content)
        inf = self.open_with_subject()
        yaml_events = YamlEvents(inf)
        return content, yaml_events
    
    def test_sequence_with_nonascii(self):
        content, yaml_events = self.prep_case('- abc\n- dé\n- fg\n')
        
        # Mapping doesn't change indexes because the first sequence item is ASCII
        yaml_events.start_index(3) |should| equal_to(2)
        yaml_events.start_byte(3) |should| equal_to(2)
        content[yaml_events.item_slice(3)] |should| equal_to(b'abc')
        content[yaml_events.item_slice_mapped(3)] |should| equal_to(b'abc')
        
        # The start point is the same, but the end gets mapped because
        # of the two-byte character representation
        yaml_events.start_index(4) |should| equal_to(8)
        yaml_events.start_byte(4) |should| equal_to(8)
        content[yaml_events.item_slice(4)] |should_not| equal_to(b'd\xc3\xa9')
        content[yaml_events.item_slice_mapped(4)] |should| equal_to(b'd\xc3\xa9')
        
        # The preceding multibyte character causes ongoing differences
        yaml_events.start_index(5) |should| equal_to(13)
        yaml_events.start_byte(5) |should| equal_to(14)
        content[yaml_events.item_slice(5)] |should_not| equal_to(b'fg')
        content[yaml_events.item_slice_mapped(5)] |should| equal_to(b'fg')
    
    def test_three_byte_utf8_handling(self):
        content, yaml_events = self.prep_case(
            'SAMARITAN LETTER ALAF: \u0800\nLATIN CAPITAL LETTER A: A\n'
        )
        
        content[yaml_events.item_slice(3)] |should| equal_to(b'SAMARITAN LETTER ALAF')
        yaml_events.item_slice_mapped(3) |should| equal_to(yaml_events.item_slice(3))
        
        content[yaml_events.item_slice(4)] |should_not| equal_to(b'\xe0\xa0\x80')
        content[yaml_events.item_slice_mapped(4)] |should| equal_to(b'\xe0\xa0\x80')
        
        content[yaml_events.item_slice(5)] |should_not| equal_to(b'LATIN CAPITAL LETTER A')
        content[yaml_events.item_slice_mapped(5)] |should| equal_to(b'LATIN CAPITAL LETTER A')
    
    def test_sequence_with_crlf_eols(self):
        content, yaml_events = self.prep_case(
            '- abc\r\n- dé\r\n- fg\r\n'
        )
        
        # Mapping doesn't change indexes because the first sequence item is
        # ASCII and no newlines yet
        yaml_events.start_index(3) |should| equal_to(2)
        yaml_events.start_byte(3) |should| equal_to(2)
        content[yaml_events.item_slice(3)] |should| equal_to(b'abc')
        content[yaml_events.item_slice_mapped(3)] |should| equal_to(b'abc')
        
        # The start point is mapped because of the newline, and the end gets
        # mapped more because of the two-byte character representation
        yaml_events.start_index(4) |should| equal_to(8)
        yaml_events.start_byte(4) |should| equal_to(9)
        content[yaml_events.item_slice(4)] |should_not| equal_to(b'd\xc3\xa9')
        content[yaml_events.item_slice_mapped(4)] |should| equal_to(b'd\xc3\xa9')
        
        # The preceding multibyte character and CRLF newlines cause ongoing
        # and accumulating differences
        yaml_events.start_index(5) |should| equal_to(13)
        yaml_events.start_byte(5) |should| equal_to(16)
        content[yaml_events.item_slice(5)] |should_not| equal_to(b'fg')
        content[yaml_events.item_slice_mapped(5)] |should| equal_to(b'fg')
    
    def test_mapping_with_crlf_followed_by_nonascii(self):
        content, yaml_events = self.prep_case(
            'A: LATIN CAPITAL LETTER A\r\n\u0800: SAMARITAN LETTER ALAF\r\n'
        )
        
        content[yaml_events.item_slice(3)] |should| equal_to(b'A')
        yaml_events.item_slice_mapped(3) |should| equal_to(yaml_events.item_slice(3))
        
        content[yaml_events.item_slice(4)] |should| equal_to(b'LATIN CAPITAL LETTER A')
        yaml_events.item_slice_mapped(4) |should| equal_to(yaml_events.item_slice(4))
        
        content[yaml_events.item_slice(5)] |should_not| equal_to(b'\xe0\xa0\x80')
        content[yaml_events.item_slice_mapped(5)] |should| equal_to(b'\xe0\xa0\x80')
        
        content[yaml_events.item_slice(6)] |should_not| equal_to(b'SAMARITAN LETTER ALAF')
        content[yaml_events.item_slice_mapped(6)] |should| equal_to(b'SAMARITAN LETTER ALAF')

class YamlEvents:
    def __init__(self, stream):
        super().__init__()
        self.events = list(yaml.parse(stream))
        self.mapper = getattr(stream, 'origin_mapper', None)
    
    def start_index(self, n):
        return self.events[n].start_mark.index
    
    def start_byte(self, n):
        return self.mapper.tell_of_chr(self.start_index(n))
    
    def item_slice(self, n):
        e = self.events[n]
        start, end = e.start_mark.index, e.end_mark.index
        return slice(start, end)
    
    def item_slice_mapped(self, n):
        e = self.events[n]
        start, end = e.start_mark.index, e.end_mark.index
        start, end = (self.mapper.tell_of_chr(p) for p in (start, end))
        return slice(start, end)

from intercom_test.augmentation import origin_mapping_stream as subject

from contextlib import ExitStack
import io
from should_dsl import should, should_not
from tempfile import NamedTemporaryFile
import yaml

class StreamTests:
    def setUp(self):
        self.context = ExitStack()
        setup = self.context.enter_context
        
        self.content_file = setup(NamedTemporaryFile())
    
    def tearDown(self):
        self.context.close()
    
    def write_content(self, data: bytes):
        self.content_file.write(data)
        self.content_file.flush()
    
    def open_with_subject(self):
        return self.context.enter_context(
            subject.open(self.content_file.name)
        )
    
    def prep_case(self, content: str):
        self.unicode_content = content
        content = content.encode('utf8')
        self.write_content(content)
        inf = self.open_with_subject()
        yaml_events = YamlEvents(inf)
        return content, yaml_events
    
    def test_sequence_with_nonascii(self):
        content, yaml_events = self.prep_case('- abc\n- dé\n- fg\n')
        
        # Mapping doesn't change indexes because the first sequence item is ASCII
        yaml_events.start_index(3) |should| equal_to(2)
        yaml_events.start_byte(3) |should| equal_to(2)
        content[yaml_events.item_slice(3)] |should| equal_to(b'abc')
        content[yaml_events.item_slice_mapped(3)] |should| equal_to(b'abc')
        
        # The start point is the same, but the end gets mapped because
        # of the two-byte character representation
        yaml_events.start_index(4) |should| equal_to(8)
        yaml_events.start_byte(4) |should| equal_to(8)
        content[yaml_events.item_slice(4)] |should_not| equal_to(b'd\xc3\xa9')
        content[yaml_events.item_slice_mapped(4)] |should| equal_to(b'd\xc3\xa9')
        
        # The preceding multibyte character causes ongoing differences
        yaml_events.start_index(5) |should| equal_to(13)
        yaml_events.start_byte(5) |should| equal_to(14)
        content[yaml_events.item_slice(5)] |should_not| equal_to(b'fg')
        content[yaml_events.item_slice_mapped(5)] |should| equal_to(b'fg')
    
    def test_three_byte_utf8_handling(self):
        content, yaml_events = self.prep_case(
            'SAMARITAN LETTER ALAF: \u0800\nLATIN CAPITAL LETTER A: A\n'
        )
        
        content[yaml_events.item_slice(3)] |should| equal_to(b'SAMARITAN LETTER ALAF')
        yaml_events.item_slice_mapped(3) |should| equal_to(yaml_events.item_slice(3))
        
        content[yaml_events.item_slice(4)] |should_not| equal_to(b'\xe0\xa0\x80')
        content[yaml_events.item_slice_mapped(4)] |should| equal_to(b'\xe0\xa0\x80')
        
        content[yaml_events.item_slice(5)] |should_not| equal_to(b'LATIN CAPITAL LETTER A')
        content[yaml_events.item_slice_mapped(5)] |should| equal_to(b'LATIN CAPITAL LETTER A')
    
    def test_sequence_with_crlf_eols(self):
        content, yaml_events = self.prep_case(
            '- abc\r\n- dé\r\n- fg\r\n'
        )
        
        # Mapping doesn't change indexes because the first sequence item is
        # ASCII and no newlines yet
        yaml_events.start_index(3) |should| equal_to(2)
        yaml_events.start_byte(3) |should| equal_to(2)
        content[yaml_events.item_slice(3)] |should| equal_to(b'abc')
        content[yaml_events.item_slice_mapped(3)] |should| equal_to(b'abc')
        
        # The start point is mapped because of the newline, and the end gets
        # mapped more because of the two-byte character representation
        yaml_events.start_index(4) |should| equal_to(8)
        yaml_events.start_byte(4) |should| equal_to(9)
        content[yaml_events.item_slice(4)] |should_not| equal_to(b'd\xc3\xa9')
        content[yaml_events.item_slice_mapped(4)] |should| equal_to(b'd\xc3\xa9')
        
        # The preceding multibyte character and CRLF newlines cause ongoing
        # and accumulating differences
        yaml_events.start_index(5) |should| equal_to(13)
        yaml_events.start_byte(5) |should| equal_to(16)
        content[yaml_events.item_slice(5)] |should_not| equal_to(b'fg')
        content[yaml_events.item_slice_mapped(5)] |should| equal_to(b'fg')
    
    def test_mapping_with_crlf_followed_by_nonascii(self):
        content, yaml_events = self.prep_case(
            'A: LATIN CAPITAL LETTER A\r\n\u0800: SAMARITAN LETTER ALAF\r\n'
        )
        
        content[yaml_events.item_slice(3)] |should| equal_to(b'A')
        yaml_events.item_slice_mapped(3) |should| equal_to(yaml_events.item_slice(3))
        
        content[yaml_events.item_slice(4)] |should| equal_to(b'LATIN CAPITAL LETTER A')
        yaml_events.item_slice_mapped(4) |should| equal_to(yaml_events.item_slice(4))
        
        content[yaml_events.item_slice(5)] |should_not| equal_to(b'\xe0\xa0\x80')
        content[yaml_events.item_slice_mapped(5)] |should| equal_to(b'\xe0\xa0\x80')
        
        content[yaml_events.item_slice(6)] |should_not| equal_to(b'SAMARITAN LETTER ALAF')
        content[yaml_events.item_slice_mapped(6)] |should| equal_to(b'SAMARITAN LETTER ALAF')

class YamlEvents:
    def __init__(self, stream):
        super().__init__()
        self.events = list(yaml.parse(stream))
        self.mapper = getattr(stream, 'origin_mapper', None)
    
    def start_index(self, n):
        return self.events[n].start_mark.index
    
    def start_byte(self, n):
        return self.mapper.tell_of_chr(self.start_index(n))
    
    def item_slice(self, n):
        e = self.events[n]
        start, end = e.start_mark.index, e.end_mark.index
        return slice(start, end)
    
    def item_slice_mapped(self, n):
        e = self.events[n]
        start, end = e.start_mark.index, e.end_mark.index
        start, end = (self.mapper.tell_of_chr(p) for p in (start, end))
        return slice(start, end)
# Mapping doesn't change indexes because the first sequence item is ASCII # The start point is the same, but the end gets mapped because # of the two-byte character representation # The preceding multibyte character causes ongoing differences # Mapping doesn't change indexes because the first sequence item is # ASCII and no newlines yet # The start point is mapped because of the newline, and the end gets # mapped more because of the two-byte character representation # The preceding multibyte character and CRLF newlines cause ongoing # and accumulating differences