Какие тест‑кейсы и методы тестирования вы примените для парсера CSV, чтобы покрыть кавычки, экранирование, разные кодировки, многострочные полета и большие файлы; как тестировать производительность и корректность одновременно
Кратко — какие тест‑кейсы и методы и как одновременно проверять корректность и производительность. 1) Типы тестов (кратко) - Unit — отдельные функции парсера (разбор строки, обработка кавычек, escape, кодировок). - Integration / E2E — полные файлы, включая разные диалекты CSV. - Differential / reference — сравнение с надёжным парсером (например, Python csv) на тех же входах. - Property‑based (Hypothesis) и фуззинг — генерация случайных/крайних вариантов. - Streaming / chunk‑boundary tests — вход разбивается на разные размеры чанков. - Load / stress — большие файлы и многопотоковая подача для измерения throughput и памяти. 2) Обязательные тест‑кейсы (конкретика) - Простые: - пустая строка, только заголовок, только поля: `a,b,c`. - пустые поля: `,,`. - Кавычки и экранирование: - поля в кавычках: `"a","b,c","d"`. - кавычки внутри поля, экранирование удвоением: `"a","b""c","d"`. - кавычка не закрыта (ошибка / восстановление). - разные escape‑стили (backslash vs doubling) если поддерживаются. - Разделители и кавычки внутри: - разделитель в поле: `"a,b",c`. - нестандартный разделитель (таба, `;`). - Многострочные поля: - поле с переносами: `"line1\nline2"`. - переносы CRLF vs LF и смешанные: `\r\n`, `\n`. - Кодировки: - BOM и без BOM: UTF‑8 BOM. - UTF‑8, UTF‑16LE/BE, windows‑1251, ISO‑8859‑1. - неверные байтовые последовательности — как парсер их обрабатывает. - мультибайтовые символы, границы буфера делят символ: проверять, что парсер корректно собирает. - Границы буфера / стриминг: - поля и кавычки разделены между чанками: тесты с разбиением входа на буферы размеров 4kB4\text{kB}4kB, 64kB64\text{kB}64kB, 1MB1\text{MB}1MB. - Нестандартные и ошибочные: - строки с разным количеством столбцов. - лишние разделители в конце/начале строки. - очень длинные поля (см. ниже про большие файлы). - Большие файлы: - размеры файлов: 1MB\,1\text{MB}1MB, 100MB\,100\text{MB}100MB, 1GB\,1\text{GB}1GB, 10GB\,10\text{GB}10GB. - много колонок: 100100100, 100010001000 столбцов. - много строк: миллионы строк (генерация синтетики). 3) Методики проверки корректности - Golden files — заранее известный правильный вывод. - Differential testing — парсер vs референс; при больших файлах сравнивать контрольные суммы по строкам (CRC32/MD5) вместо полного сравнения. - Row invariants — постоянное число колонок, no unescaped quotes, round‑trip: parse → serialize → parse равен исходу. - Сэмплинг при больших файлах — проверка рандомных строк (например, 1%1\%1% или 10,00010{,}00010,000 строк) для экономии времени. - Фаззинг + контроль invariants для обнаружения краёв. - Тесты на границы буфера: генерировать входы и подаёте их с разными chunk sizes, убеждаясь, что результат одинаков. 4) Как тестировать производительность и корректность одновременно - Differential + throughput: запускайте парсер и reference параллельно; для каждого разбираемого блока проверяйте, что контрольная сумма (например, CRC32 каждой строки) совпадает, но измеряйте throughput (MB/s), latency p95/p99 и peak memory. - Streamed checksums: при парсинге вычисляйте checksum на выходные поля (на лету) и сравнивайте с эталонным checksum; это позволяет проверять корректность без хранения всего результата. - Нагрузочные сценарии с ассертами: - синтетический файл размера SSS (см. выше), разделённый на чанки размера BBB, подаётся под нагрузкой с concurrency CCC (от 111 до числа ядер); собирайте метрики и одновременно проверяйте row count и sample checksums. - Gradual scaling: цепочка тестов с файлами {1MB,100MB,1GB,10GB} \{1\text{MB}, 100\text{MB}, 1\text{GB}, 10\text{GB}\} {1MB,100MB,1GB,10GB} и chunk sizes {4kB,64kB,1MB} \{4\text{kB},64\text{kB},1\text{MB}\} {4kB,64kB,1MB}. На каждом шаге сравнивайте контрольные суммы/диффы. - Автоматизированный differential fuzzing под нагрузкой: долго работающий фуззер генерирует входы и отправляет их под нагрузкой; при первом несоответствии — лог и дамп. - Проверка памяти и утечек: профилировать heap и peak RSS под длительной нагрузкой; комбинировать с correctness asserts. - Параллельная верификация: парсер работает в production‑режиме, а отдельный верификатор (референс или менее оптимизированный режим) в фоне проверяет выборочные блоки. 5) Практические советы и матрица покрытия - Генерируйте матрицу комбинаций: кавычки modes {none,double,backslash} \{none, double, backslash\} {none,double,backslash} × newline {LF,CRLF} \{LF, CRLF\} {LF,CRLF} × encoding {UTF8,UTF16LE,CP1251} \{UTF8, UTF16LE, CP1251\} {UTF8,UTF16LE,CP1251} × chunk sizes {4kB,64kB,1MB} \{4\text{kB},64\text{kB},1\text{MB}\} {4kB,64kB,1MB} × file sizes {1MB,100MB,1GB} \{1\text{MB},100\text{MB},1\text{GB}\} {1MB,100MB,1GB}. Покрывать все комбинации выборочно (приоритизировать). - В CI: быстрые unit + несколько ключевых integration тестов; nightly/weekly — полные стресс‑тесты и фуззинг. - Логирование и репродуцируемость: сохранять seed генератора для фуззинга и чанки входных данных, чтобы можно было воспроизвести баг. - Метрики, которые обязательно собирать: throughput (MB/s), rows/s, p95/p99 latency, peak memory, количество ошибок/строк с parse error. 6) Инструменты и подходы - Hypothesis (property‑based) или custom generator. - AFL/LibFuzzer для фуззинга. - Reference parsers (Python csv, pandas) для differential. - Streamed checksum (CRC32/xxHash) для больших файлов. - CI + nightly stress + long‑running fuzzer на отдельном инстансе. Резюме: покрывайте базовые и краевые кейсы (кавычки, экранирование, мультистроки, кодировки, границы чанков), комбинируйте differential подход с checksum‑проверками и фуззингом, а производительность тестируйте скалированием файлов/чанков/параллелизма одновременно с автоматическими корректностными проверками (checksums, row invariants, выборочные диффы).
1) Типы тестов (кратко)
- Unit — отдельные функции парсера (разбор строки, обработка кавычек, escape, кодировок).
- Integration / E2E — полные файлы, включая разные диалекты CSV.
- Differential / reference — сравнение с надёжным парсером (например, Python csv) на тех же входах.
- Property‑based (Hypothesis) и фуззинг — генерация случайных/крайних вариантов.
- Streaming / chunk‑boundary tests — вход разбивается на разные размеры чанков.
- Load / stress — большие файлы и многопотоковая подача для измерения throughput и памяти.
2) Обязательные тест‑кейсы (конкретика)
- Простые:
- пустая строка, только заголовок, только поля: `a,b,c`.
- пустые поля: `,,`.
- Кавычки и экранирование:
- поля в кавычках: `"a","b,c","d"`.
- кавычки внутри поля, экранирование удвоением: `"a","b""c","d"`.
- кавычка не закрыта (ошибка / восстановление).
- разные escape‑стили (backslash vs doubling) если поддерживаются.
- Разделители и кавычки внутри:
- разделитель в поле: `"a,b",c`.
- нестандартный разделитель (таба, `;`).
- Многострочные поля:
- поле с переносами: `"line1\nline2"`.
- переносы CRLF vs LF и смешанные: `\r\n`, `\n`.
- Кодировки:
- BOM и без BOM: UTF‑8 BOM.
- UTF‑8, UTF‑16LE/BE, windows‑1251, ISO‑8859‑1.
- неверные байтовые последовательности — как парсер их обрабатывает.
- мультибайтовые символы, границы буфера делят символ: проверять, что парсер корректно собирает.
- Границы буфера / стриминг:
- поля и кавычки разделены между чанками: тесты с разбиением входа на буферы размеров 4kB4\text{kB}4kB, 64kB64\text{kB}64kB, 1MB1\text{MB}1MB.
- Нестандартные и ошибочные:
- строки с разным количеством столбцов.
- лишние разделители в конце/начале строки.
- очень длинные поля (см. ниже про большие файлы).
- Большие файлы:
- размеры файлов: 1MB\,1\text{MB}1MB, 100MB\,100\text{MB}100MB, 1GB\,1\text{GB}1GB, 10GB\,10\text{GB}10GB.
- много колонок: 100100100, 100010001000 столбцов.
- много строк: миллионы строк (генерация синтетики).
3) Методики проверки корректности
- Golden files — заранее известный правильный вывод.
- Differential testing — парсер vs референс; при больших файлах сравнивать контрольные суммы по строкам (CRC32/MD5) вместо полного сравнения.
- Row invariants — постоянное число колонок, no unescaped quotes, round‑trip: parse → serialize → parse равен исходу.
- Сэмплинг при больших файлах — проверка рандомных строк (например, 1%1\%1% или 10,00010{,}00010,000 строк) для экономии времени.
- Фаззинг + контроль invariants для обнаружения краёв.
- Тесты на границы буфера: генерировать входы и подаёте их с разными chunk sizes, убеждаясь, что результат одинаков.
4) Как тестировать производительность и корректность одновременно
- Differential + throughput: запускайте парсер и reference параллельно; для каждого разбираемого блока проверяйте, что контрольная сумма (например, CRC32 каждой строки) совпадает, но измеряйте throughput (MB/s), latency p95/p99 и peak memory.
- Streamed checksums: при парсинге вычисляйте checksum на выходные поля (на лету) и сравнивайте с эталонным checksum; это позволяет проверять корректность без хранения всего результата.
- Нагрузочные сценарии с ассертами:
- синтетический файл размера SSS (см. выше), разделённый на чанки размера BBB, подаётся под нагрузкой с concurrency CCC (от 111 до числа ядер); собирайте метрики и одновременно проверяйте row count и sample checksums.
- Gradual scaling: цепочка тестов с файлами {1MB,100MB,1GB,10GB} \{1\text{MB}, 100\text{MB}, 1\text{GB}, 10\text{GB}\} {1MB,100MB,1GB,10GB} и chunk sizes {4kB,64kB,1MB} \{4\text{kB},64\text{kB},1\text{MB}\} {4kB,64kB,1MB}. На каждом шаге сравнивайте контрольные суммы/диффы.
- Автоматизированный differential fuzzing под нагрузкой: долго работающий фуззер генерирует входы и отправляет их под нагрузкой; при первом несоответствии — лог и дамп.
- Проверка памяти и утечек: профилировать heap и peak RSS под длительной нагрузкой; комбинировать с correctness asserts.
- Параллельная верификация: парсер работает в production‑режиме, а отдельный верификатор (референс или менее оптимизированный режим) в фоне проверяет выборочные блоки.
5) Практические советы и матрица покрытия
- Генерируйте матрицу комбинаций: кавычки modes {none,double,backslash} \{none, double, backslash\} {none,double,backslash} × newline {LF,CRLF} \{LF, CRLF\} {LF,CRLF} × encoding {UTF8,UTF16LE,CP1251} \{UTF8, UTF16LE, CP1251\} {UTF8,UTF16LE,CP1251} × chunk sizes {4kB,64kB,1MB} \{4\text{kB},64\text{kB},1\text{MB}\} {4kB,64kB,1MB} × file sizes {1MB,100MB,1GB} \{1\text{MB},100\text{MB},1\text{GB}\} {1MB,100MB,1GB}. Покрывать все комбинации выборочно (приоритизировать).
- В CI: быстрые unit + несколько ключевых integration тестов; nightly/weekly — полные стресс‑тесты и фуззинг.
- Логирование и репродуцируемость: сохранять seed генератора для фуззинга и чанки входных данных, чтобы можно было воспроизвести баг.
- Метрики, которые обязательно собирать: throughput (MB/s), rows/s, p95/p99 latency, peak memory, количество ошибок/строк с parse error.
6) Инструменты и подходы
- Hypothesis (property‑based) или custom generator.
- AFL/LibFuzzer для фуззинга.
- Reference parsers (Python csv, pandas) для differential.
- Streamed checksum (CRC32/xxHash) для больших файлов.
- CI + nightly stress + long‑running fuzzer на отдельном инстансе.
Резюме: покрывайте базовые и краевые кейсы (кавычки, экранирование, мультистроки, кодировки, границы чанков), комбинируйте differential подход с checksum‑проверками и фуззингом, а производительность тестируйте скалированием файлов/чанков/параллелизма одновременно с автоматическими корректностными проверками (checksums, row invariants, выборочные диффы).