Статьи

Тест-управляемый обратный инжиниринг (TDRE)

Еще одно тематическое исследование по TDRE. Предоставлено: 2938 строк кода Python, которые обрабатывают несколько больших файлов, чтобы создать ряд выходных данных. [Детали не могут быть раскрыты.] Цель: Рефакторинг, чтобы отличить общую последовательность шагов трансформации от деталей каждого отдельного шага.

наблюдения

Код почти чисто процедурный. Есть 11 определений классов. 6 из этих встроенных типов переноса с преобразованием типов и обработкой нуля. 1 является новым исключением. 1 является общей «таблицей», которая по существу дублирует функции SQLite. Остальные 3 являются частью проблемной области.

Одной из причин реверс-инжиниринга является то, что код достиг интеллектуального предела. Это маленький, но «плотный» с высоко оптимизированными этапами обработки. Тип сплоченности почти весь «временный». Обработка сгруппирована в последовательные циклы обработки; каждый цикл содержит кластер этапов обработки. Следовательно, довольно сложно отделить алгоритм, чтобы получить «общую картину» того, что происходит. Это просто густой ствол деревьев. Нет леса.

Другая причина обратного инжиниринга заключается в поддержке бесконечной адаптации и модификации кодовой базы. Программа является своего рода «электронной таблицей на стероидах». Это не упрощенная коллекция ячеек и формул, которая позволяет проводить простой анализ «что если». Это более сложный набор формул, который будет сложно (но не невозможно) реализовать в виде электронной таблицы. Однако вариант использования — это сценарий использования таблицы: подумайте, настройте, создайте результаты, повторите.

TDRE подход

Начните с Начального обзора устаревшей базы кода и примеров файлов .

Создайте Outline или «эскиз» модели предметной области и основной программы. Это будут модули (или пакет) с комментариями и некоторыми предварительными определениями классов. Еще немного.

Выберите шаг обработки в устаревшем коде. Это часто требует создания сводок обработки унаследованного кода. Большая часть устаревшего кода является процедурной, поэтому обработка имеет тенденцию быть последовательной по своей природе.

Примените устаревший код с печатными инструкциями для сбора данных. Это может быть просто. Вывод может быть сложно интерпретировать.

with open("tdre_results_1","w") as tdre:
# some legacy processing
print( "Case:", foo, bar, ", Expect:", baz, file=tdre )

Исходя из результатов, создайте юнит-тесты . Заполните части последовательности обработки и модели предметной области. Отладка кода до прохождения тестов.

Первоначальный опрос

Первоначальное обследование обнаруживает несколько вещей.

  1. Полезные, работающие модули. Похоже, что все обратное проектирование включает в себя кодовую базу с мертвым или неиспользуемым кодом. Даже небольшой проект (3000 строк) будет содержать значительное количество мертвого кода.
  2. Приоритеты для реализованного функционала. Не каждый «основной» модуль актуален.
  3. Пример входов и выходов.

Если программное обеспечение не может быть запущено (как в случае с органически разработанными системами, которые зависят от больших, сложных корпоративных баз данных), тогда входные и выходные данные примера могут фактически не соответствовать программному обеспечению. Если программное обеспечение может быть запущено, оно должно быть запущено, и фактические данные сравниваются с образцами, чтобы подтвердить, что предоставленная база кода действительно выдает выходные данные образца.

Ожидайте, что предоставленный устаревший код немного отличается от кода в производственном использовании. В некоторых случаях это не может быть решено; например, когда исполняемые файлы старше исходного. В других случаях код совпадает, и для установления устаревшего базового уровня дальнейшая работа не требуется.

Выходные данные образца указывают в направлении приемочного теста. Образец вывода не может быть взят буквально как единственный приемочный тест. Несмотря на то, что для реверс-инжиниринга желательно воспроизвести пример выходных данных, большинство реверс-инжиниринга будет включать улучшения или исправления ошибок. Ожидайте, что ошибки будут найдены (или могут быть известны, что существуют) в выходных данных примера.

Создать план

Схема — изначально — просто общий MVP. Должна быть модель предметной области, какой-то «презентатор», имеющий логику приложения, и некоторое «представление» для отображения результатов.

В нашем примере выше, «представление» представляет собой набор (в основном текстовых) выходных файлов. Модель не была определена в унаследованном коде, который был логикой приложения «презентатор».

Цель состояла в том, чтобы извлечь базовую модель, разбить логику «презентатора» приложения на два уровня (лес и деревья) и построить несколько представлений для каждого из выходных файлов.

Выберите шаг обработки

Это может быть сложно, в зависимости от унаследованного кода. Есть два пути через базу процедурного кода.

  • Задом наперед. Начните с окончательных результатов и проведите модульное тестирование последних шагов на основе предыдущих шагов, которые будут определены позже.
  • Спереди к спине. Начните с первого распознаваемого промежуточного результата на основе входных файлов. Модульное тестирование начальных шагов.

Работать спереди назад выгоднее, потому что прогресс можно показать немного яснее.

Лучшая архитектура может быть создана путем обратной работы, так как зависимости легче понять.

Объем модульных испытаний, кромки и углы

Есть две проблемы проектирования модульных тестов при выполнении обратного инжиниринга.

  • Объем. Данные выборки могут быть большими. 100 000 строк данных образца слишком много для тестирования. Найти «представительное» подмножество сложно. Как правило, для начала необходимо использовать произвольные подмножества. Как только приложение в основном работает, необходимо создать более усовершенствованные модульные тесты.
  • Краевые и угловые чехлы. Хотя код может быть пронизан if- утверждениями, все еще может быть трудно найти примеры входных данных, которые выполняют различные условия в коде. Создавать данные рискованно — мы должны предположить, что унаследованный код делает неожиданные вещи. Во многих случаях операторы print необходимо помещать в сложные операторы if, чтобы найти любые фактические данные, которые используют этот логический путь.

Как только модульные тесты созданы, это всего лишь разработка через тестирование (TDD).

От http://slott-softwarearchitect.blogspot.com/2011/04/test-driven-reverse-engineering-tdre.html