1/41Санкт-Петербург. 31 мая–1 июня 2019
Software quality assurance days
25 Международная конференция
по вопросам качества ПО
sqadays.com
Сергей Хренов
PVS-Studio, Тула, Россия
Специфика разработки и тестирования
статического анализатора
2/41
Название темы. Может быть длинное, даже не одна строка, а две или три
О статическом анализе,
сложностях разработки и
обеспечении качества
3/41
• Не заменяет, но дополняет
обзоры кода
• Поиск ошибок в коде без его
выполнения
• Позволяет контролировать
качество кода в больших
проектах
Статический анализ…
4/41
Типовая ошибка (проект Mono)
5/41
Типовая ошибка (проект Mono)
6/41
V3012 The '?:' operator, regardless of its conditional expression, always
returns one and the same value: Color.FromArgb (150, 179, 225).
ProfessionalColorTable.cs 258
Типовая ошибка (проект Mono)
7/41
Проблематика
• Наукоёмкость
• Различные языки программирования
• Кроссплатформенность
• Поддержка стандартов (CWE, MISRA, SEI CERT…)
• Классических методов тестирования недостаточно
8/41
Как добиться качества?
• Совместные обзоры кода (это работает!)
• Unit-тесты
• UI-тесты
• Функциональные тесты
• Нагрузочное тестирование
• Статический анализ
• Проверка пула реальных проектов (SelfTester)
9/41
Как устроен и работает C#-анализатор
10/41
MSBuild, Roslyn и все, все, все
11/41
MSBuild, Roslyn и все, все, все
12/41
int x = 1;
Синтаксическое дерево
13/41
Семантическая модель
 Получение информации об объекте
 Получение информации о типе объекта
 Получение константных значений
x = 1;
x
Semantic Model
System.Int32 x = 1;
14/41
Получение проекта
void GetProjects(string solutionPath, string projectPath)
{
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
Solution currSolution = workspace.OpenSolutionAsync(solutionPath)
.Result;
IEnumerable<Project> projects = currSolution.Projects;
Project currProject = workspace.OpenProjectAsync(projectPath)
.Result;
}
15/41
Получение проекта (все проекты в решении)
void GetProjects(string solutionPath, string projectPath)
{
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
Solution currSolution = workspace.OpenSolutionAsync(solutionPath)
.Result;
IEnumerable<Project> projects = currSolution.Projects;
Project currProject = workspace.OpenProjectAsync(projectPath)
.Result;
}
16/41
Получение проекта (заданный проект)
void GetProjects(string solutionPath, string projectPath)
{
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
Solution currSolution = workspace.OpenSolutionAsync(solutionPath)
.Result;
IEnumerable<Project> projects = currSolution.Projects;
Project currProject = workspace.OpenProjectAsync(projectPath)
.Result;
}
17/41
Анализ проекта
void ProjectAnalysis(Project project)
{
Compilation compilation = project.GetCompilationAsync().Result;
foreach (var file in project.Documents)
{
SyntaxTree tree = file.GetSyntaxTreeAsync().Result;
SemanticModel model = compilation.GetSemanticModel(tree);
Visit(tree.GetRoot());
}
}
18/41
Анализ проекта (обход всех файлов)
void ProjectAnalysis(Project project)
{
Compilation compilation = project.GetCompilationAsync().Result;
foreach (var file in project.Documents)
{
SyntaxTree tree = file.GetSyntaxTreeAsync().Result;
SemanticModel model = compilation.GetSemanticModel(tree);
Visit(tree.GetRoot());
}
}
19/41
Анализ проекта (получение и обход дерева)
void ProjectAnalysis(Project project)
{
Compilation compilation = project.GetCompilationAsync().Result;
foreach (var file in project.Documents)
{
SyntaxTree tree = file.GetSyntaxTreeAsync().Result;
SemanticModel model = compilation.GetSemanticModel(tree);
Visit(tree.GetRoot());
}
}
20/41
Переопределённые методы обхода узлов
public override void VisitIfStatement(IfStatementSyntax node)
{
base.VisitIfStatement(node);
}
public override void VisitForStatement(ForStatementSyntax node)
{
base.VisitForStatement(node);
}
….
21/41
Диагностика
22/41
Диагностика V3006: пропущенный throw
public void DoSomething(int index)
{
if (index < 0)
new ArgumentOutOfRangeException(); // <= V3006
else
....
}
// правильный вариант кода:
throw new ArgumentOutOfRangeException();
23/41
1. Подписаться на обход узлов типа ObjectCreationExpressionSyntax
(создание объекта с использованием оператора new);
2. Проверить, что тип создаваемого объекта System.Exception или
производный (используем семантическую модель);
3. Проверить, что созданный объект никак не используется;
4. Выдать предупреждение.
Диагностика V3006: пропущенный throw
24/41
public class V3006CSharpRule : IVisitObjectCreationExpressionRule
{
....
public void VisitObjectCreationExpression(
SemanticModelAdapter model,
VisitInfo visitInfo,
ObjectCreationExpressionSyntax node,
AnalysisResults results)
{
....
}
}
Диагностика V3006: пропущенный throw
25/41
Разработка диагностики
1. Создание позитивных и негативных тестов
2. Разработка прототипа, отвечающего требованиям
тестов
3. Доработка диагностики и тестов по результатам
прогона на пуле реальных проектов (SelfTester)
4. Обработка исключений, минимизация ложных
срабатываний
5. Повторный прогон на реальных проектах, фиксация
изменений
26/41
Немного статистики
27/41
Позитивные тесты
28/41
Негативные тесты
29/41
Негативные и позитивные тесты
30/41
Проверка работы на реальных проектах
(SelfTester)
31/41
• Инструмент пакетной проверки пула
реальных проектов
• SelfTester для С/С++ и C# использует
локальный пул проектов
• SelfTester для Java выкачивает проекты
определенной версии с репозитория на
GitHub
PVS-Studio SelfTester
32/41
Требования к SelfTester
• Кроссплатформенность
• Параллелизм
• Наличие GUI
• Гибкие настройки
• Представление результата в удобном виде
• Сравнение с результатом предыдущего
запуска
• Возможность быстрой фиксации или
игнорирования различий
33/41
Задачи SelfTester
• Любое изменение ядра требует перезапуска тестов
• Ядро – совокупность общих внутренних механизмов
анализатора
• Создание новой диагностики требует перезапуска
тестов
• Включение нового проекта в тестовый пул требует
перезапуска тестов
• Главная задача – выявление аномалий в поведении
анализатора
• Часто аномалии – ожидаемое поведение
34/41
Отчёт о найденных ошибках (*.plog)
35/41
SelfTester: алгоритм работы
Доработка
анализатора
Различия?
Тестовый
проект
Анализ
Эталонный
лог (plog)
Текущий
лог (plog)
Сравнение
логов
Ожидаемо?
Замена
эталонного
лога текущим
да
данет
нет
36/41
Результат сравнения логов
37/41
PVS-Studio SelfTester (С#)
38/41
Вывод в продуктив
• Доработки по результатам
откликов от пользователей
(включая внутренних)
• Доработки, связанные с
изменением поведения ядра
(доработка механизмов,
поддержка новых стандартов
языка и т.п.)
39/41
Пять признаков хорошего статического анализатора
по версии PVS-Studio
• Быстрая обработка кода
• Минимум ложных срабатываний
• Развитые средства интеграции
• Простота внедрения на больших проектах
• Поддержка
40/41
Вопросы
41/41
Сергей Хренов
C# разработчик, PVS-Studio
khrenov@viva64.com
www.viva64.com
Контакты

Специфика разработки и тестирования статического анализатора

  • 1.
    1/41Санкт-Петербург. 31 мая–1июня 2019 Software quality assurance days 25 Международная конференция по вопросам качества ПО sqadays.com Сергей Хренов PVS-Studio, Тула, Россия Специфика разработки и тестирования статического анализатора
  • 2.
    2/41 Название темы. Можетбыть длинное, даже не одна строка, а две или три О статическом анализе, сложностях разработки и обеспечении качества
  • 3.
    3/41 • Не заменяет,но дополняет обзоры кода • Поиск ошибок в коде без его выполнения • Позволяет контролировать качество кода в больших проектах Статический анализ…
  • 4.
  • 5.
  • 6.
    6/41 V3012 The '?:'operator, regardless of its conditional expression, always returns one and the same value: Color.FromArgb (150, 179, 225). ProfessionalColorTable.cs 258 Типовая ошибка (проект Mono)
  • 7.
    7/41 Проблематика • Наукоёмкость • Различныеязыки программирования • Кроссплатформенность • Поддержка стандартов (CWE, MISRA, SEI CERT…) • Классических методов тестирования недостаточно
  • 8.
    8/41 Как добиться качества? •Совместные обзоры кода (это работает!) • Unit-тесты • UI-тесты • Функциональные тесты • Нагрузочное тестирование • Статический анализ • Проверка пула реальных проектов (SelfTester)
  • 9.
    9/41 Как устроен иработает C#-анализатор
  • 10.
    10/41 MSBuild, Roslyn ивсе, все, все
  • 11.
    11/41 MSBuild, Roslyn ивсе, все, все
  • 12.
    12/41 int x =1; Синтаксическое дерево
  • 13.
    13/41 Семантическая модель  Получениеинформации об объекте  Получение информации о типе объекта  Получение константных значений x = 1; x Semantic Model System.Int32 x = 1;
  • 14.
    14/41 Получение проекта void GetProjects(stringsolutionPath, string projectPath) { MSBuildWorkspace workspace = MSBuildWorkspace.Create(); Solution currSolution = workspace.OpenSolutionAsync(solutionPath) .Result; IEnumerable<Project> projects = currSolution.Projects; Project currProject = workspace.OpenProjectAsync(projectPath) .Result; }
  • 15.
    15/41 Получение проекта (всепроекты в решении) void GetProjects(string solutionPath, string projectPath) { MSBuildWorkspace workspace = MSBuildWorkspace.Create(); Solution currSolution = workspace.OpenSolutionAsync(solutionPath) .Result; IEnumerable<Project> projects = currSolution.Projects; Project currProject = workspace.OpenProjectAsync(projectPath) .Result; }
  • 16.
    16/41 Получение проекта (заданныйпроект) void GetProjects(string solutionPath, string projectPath) { MSBuildWorkspace workspace = MSBuildWorkspace.Create(); Solution currSolution = workspace.OpenSolutionAsync(solutionPath) .Result; IEnumerable<Project> projects = currSolution.Projects; Project currProject = workspace.OpenProjectAsync(projectPath) .Result; }
  • 17.
    17/41 Анализ проекта void ProjectAnalysis(Projectproject) { Compilation compilation = project.GetCompilationAsync().Result; foreach (var file in project.Documents) { SyntaxTree tree = file.GetSyntaxTreeAsync().Result; SemanticModel model = compilation.GetSemanticModel(tree); Visit(tree.GetRoot()); } }
  • 18.
    18/41 Анализ проекта (обходвсех файлов) void ProjectAnalysis(Project project) { Compilation compilation = project.GetCompilationAsync().Result; foreach (var file in project.Documents) { SyntaxTree tree = file.GetSyntaxTreeAsync().Result; SemanticModel model = compilation.GetSemanticModel(tree); Visit(tree.GetRoot()); } }
  • 19.
    19/41 Анализ проекта (получениеи обход дерева) void ProjectAnalysis(Project project) { Compilation compilation = project.GetCompilationAsync().Result; foreach (var file in project.Documents) { SyntaxTree tree = file.GetSyntaxTreeAsync().Result; SemanticModel model = compilation.GetSemanticModel(tree); Visit(tree.GetRoot()); } }
  • 20.
    20/41 Переопределённые методы обходаузлов public override void VisitIfStatement(IfStatementSyntax node) { base.VisitIfStatement(node); } public override void VisitForStatement(ForStatementSyntax node) { base.VisitForStatement(node); } ….
  • 21.
  • 22.
    22/41 Диагностика V3006: пропущенныйthrow public void DoSomething(int index) { if (index < 0) new ArgumentOutOfRangeException(); // <= V3006 else .... } // правильный вариант кода: throw new ArgumentOutOfRangeException();
  • 23.
    23/41 1. Подписаться наобход узлов типа ObjectCreationExpressionSyntax (создание объекта с использованием оператора new); 2. Проверить, что тип создаваемого объекта System.Exception или производный (используем семантическую модель); 3. Проверить, что созданный объект никак не используется; 4. Выдать предупреждение. Диагностика V3006: пропущенный throw
  • 24.
    24/41 public class V3006CSharpRule: IVisitObjectCreationExpressionRule { .... public void VisitObjectCreationExpression( SemanticModelAdapter model, VisitInfo visitInfo, ObjectCreationExpressionSyntax node, AnalysisResults results) { .... } } Диагностика V3006: пропущенный throw
  • 25.
    25/41 Разработка диагностики 1. Созданиепозитивных и негативных тестов 2. Разработка прототипа, отвечающего требованиям тестов 3. Доработка диагностики и тестов по результатам прогона на пуле реальных проектов (SelfTester) 4. Обработка исключений, минимизация ложных срабатываний 5. Повторный прогон на реальных проектах, фиксация изменений
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
    30/41 Проверка работы нареальных проектах (SelfTester)
  • 31.
    31/41 • Инструмент пакетнойпроверки пула реальных проектов • SelfTester для С/С++ и C# использует локальный пул проектов • SelfTester для Java выкачивает проекты определенной версии с репозитория на GitHub PVS-Studio SelfTester
  • 32.
    32/41 Требования к SelfTester •Кроссплатформенность • Параллелизм • Наличие GUI • Гибкие настройки • Представление результата в удобном виде • Сравнение с результатом предыдущего запуска • Возможность быстрой фиксации или игнорирования различий
  • 33.
    33/41 Задачи SelfTester • Любоеизменение ядра требует перезапуска тестов • Ядро – совокупность общих внутренних механизмов анализатора • Создание новой диагностики требует перезапуска тестов • Включение нового проекта в тестовый пул требует перезапуска тестов • Главная задача – выявление аномалий в поведении анализатора • Часто аномалии – ожидаемое поведение
  • 34.
  • 35.
    35/41 SelfTester: алгоритм работы Доработка анализатора Различия? Тестовый проект Анализ Эталонный лог(plog) Текущий лог (plog) Сравнение логов Ожидаемо? Замена эталонного лога текущим да данет нет
  • 36.
  • 37.
  • 38.
    38/41 Вывод в продуктив •Доработки по результатам откликов от пользователей (включая внутренних) • Доработки, связанные с изменением поведения ядра (доработка механизмов, поддержка новых стандартов языка и т.п.)
  • 39.
    39/41 Пять признаков хорошегостатического анализатора по версии PVS-Studio • Быстрая обработка кода • Минимум ложных срабатываний • Развитые средства интеграции • Простота внедрения на больших проектах • Поддержка
  • 40.
  • 41.
    41/41 Сергей Хренов C# разработчик,PVS-Studio khrenov@viva64.com www.viva64.com Контакты