﻿# Почему я не люблю синтетические тесты

Мне не нравится, когда кто\-то пытается использовать созданные вручную примеры кода для оценки возможностей статического анализатора кода\. Сейчас на конкретном примере я продемонстрирую, почему негативно отношусь к синтетическим тестам\.

![0471_Why_I_Dislike_Synthetic_Tests_ru/image1.png](https://import.viva64.com/docx/blog/0471_Why_I_Dislike_Synthetic_Tests_ru/image1.png)

Не так давно Bill Torpey написал в своем блоге заметку "[Even Mo' Static](http://btorpey.github.io/blog/2016/11/12/even-mo-static/)", где рассказал, как, на его взгляд, показали себя инструменты Cppcheck и PVS\-Studio при анализе проекта [itc\-benchmarks](https://github.com/regehr/itc-benchmarks)\. Проект itc\-benchmarks \- это static analysis benchmarks from Toyota ITC\.

Мне не понравилось, что после прочтения статьи создается впечатление, что анализаторы Cppcheck и PVS\-Studio приблизительно равны в своих возможностях\. Из статьи следует, что один анализатор показывает себя лучше в одном, второй в другом, но в целом их диагностические возможности похожи\.

Я думаю, что это не так\. Мое мнение \- наш анализатор PVS\-Studio в несколько раз мощнее, чем Cppcheck\. И вообще, это не "мнение", я знаю это\! 

Однако, раз со стороны не видно, что PVS\-Studio в 10 раз лучше Cppcheck, то надо попытаться понять причину\. Я решил посмотреть на этот самый itc\-benchmarks и разобраться, почему PVS\-Studio показал себя на этой тестовой базе не лучшим образом\.

Чем дальше я разбирался, тем большее раздражение я испытывал\. А один пример совсем вывел меня из равновесия, и о нем я расскажу чуть ниже\. Мои выводы такие: у меня нет претензий к Bill Torpey\. Он написал хорошую, честную статью\. Спасибо, Bill\. Зато у меня есть претензии к Toyota ITC\. Мое личное мнение: их тестовая база \- хрень\. Это, конечно, громкое заявление, но я считаю, что имею достаточную квалификацию и опыт, чтобы рассуждать о статических анализаторах кода и способах их оценки\. По моему мнению, itc\-benchmarks не может быть использован для адекватной оценки возможностей того или иного анализатора\.

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

[Это тест](https://github.com/regehr/itc-benchmarks/blob/master/01.w_Defects/null_pointer.c) на разыменование нулевого указателя:

```cpp
void null_pointer_001 ()
{
  int *p = NULL;
  *p = 1; /*Tool should detect this line as error*/
          /*ERROR:NULL pointer dereference*/
}
```

Анализатор Cppcheck заявляет, что нашел ошибку в этом коде:

```cpp
Null pointer dereference: p
```

Анализатор PVS\-Studio на этом коде молчит, хотя для таких случаев в нём существует диагностика [V522](https://pvs-studio.ru/ru/blog/examples/v522/)\.

Так что же получается, PVS\-Studio на этом примере слабее чем Cppcheck? Нет, он как раз сильнее\!

Анализатор PVS\-Studio понимает, что этот код написан сознательно и никакой ошибки здесь нет\.

Бывают ситуации, когда аналогичный код пишут **специально**, чтобы добиться возникновения исключения при разыменовании нулевого указателя\. Такое можно встретить в тестах или в специфических участках кода\. Подобный код мы встречали неоднократно\. Вот, например, как такое может выглядеть в [реальном проекте](https://github.com/adobe/chromium/blob/master/content/gpu/gpu_child_thread.cc):

```cpp
void GpuChildThread::OnCrash() {
  LOG(INFO) << "GPU: Simulating GPU crash";
  // Good bye, cruel world.
  volatile int* it_s_the_end_of_the_world_as_we_know_it = NULL;
  *it_s_the_end_of_the_world_as_we_know_it = 0xdead;
}
```

Поэтому в анализаторе PVS\-Studio в диагностике V522 реализовано несколько исключений, чтобы как раз не ругаться на такой код\. Анализатор видит, что _null\_pointer\_001_ является ненастоящей функцией\. Не бывает в реальном коде ошибок в функциях, когда ноль записывают в указатель и сразу его разыменовывают\. Да и имя функции говорит анализатору, что "нулевой указатель" здесь неспроста\.

Для подобных случаев, в диагностике V522 реализовано исключение A6\. Под него же попадает и синтетическая функция _null\_pointer\_001_\. Вот как опасно исключение A6:

_Разыменование переменной находится в функции, в названии которой есть одно из слов:_

* _error_
* _default_
* _crash_
* _null_
* _test_
* _violation_
* _throw_
* _exception_

_При этом, переменной присваивается 0 строчкой выше\._

Синтетический тест полностью подошел под это исключение\. Во\-первых, в названии функции есть слово "null"\. Во\-вторых, присваивание нуля переменной происходит как раз на предыдущей строке\. Исключение выявило ненастоящий код\. И код действительно не настоящий, это синтетический тест\.

Вот из\-за подобных нюансов я и не люблю синтетические тесты\!

У меня есть к itc\-benchmarks и другие претензии\.  Например, всё в том же файле, мы можем видеть вот такой тест:

```cpp
void null_pointer_006 ()
{
  int *p;
  p = (int *)(intptr_t)rand();
  *p = 1; /*Tool should detect this line as error*/
          /*ERROR:NULL pointer dereference*/
}
```

Функция _rand_ может вернуть 0, который затем превратится в NULL\. Анализатор PVS\-Studio пока не знает, что может вернуть _rand_ и поэтому не видит в этом коде ничего подозрительного\.

Я попросил коллег научить анализатор лучше понимать, что из себя представляет функция _rand_\. Деваться некуда, придётся подточить напильником анализатор, чтобы он лучше себя показывал на рассматриваемой тестовой базе\. Это вынужденная мера, раз для оценки анализаторов используют подобные наборы тестов\.

Но не бойтесь\. Я заявляю, что мы по\-прежнему будем работать над настоящими хорошими диагностиками, а не заниматься подгонкой анализатора под тесты\. Пожалуй, мы немного подретушируем PVS\-Studio для itc\-benchmarks, но в фоновом режиме и только в тех местах, которые имеют хоть какой\-то смысл\.

Я хочу, чтобы разработчики понимали, что пример с _rand_ ничего на самом деле не оценивает\. Это синтетический тест, который высосан из пальца\. Не пишут так программы\. Нет таких ошибок\.

Кстати, если функция _rand_ вернёт не 0, а 1400 лучше не будет\. Все равно такой указатель разыменовывать нельзя\. Так что разыменование нулевого указателя какой\-то странный частный случай совершенно некорректного кода, который просто был выдуман и который не встречается в реальных программах\.

Мне известны настоящие программистские беды\. Например, это опечатки, которые мы выявляем сотнями, скажем, с помощью диагностики [V501](https://pvs-studio.ru/ru/blog/examples/v501/)\. Что интересно, в itc\-benchmarks я не заметил ни одного теста, где проверялось, может ли анализатор обнаружить опечатку вида "if \(a\.x \=\= a\.x\)"\. Ни одного теста\!

Таким образом, itc\-benchmarks игнорирует возможности анализаторов по поиску опечаток\. А читатели наших статей знают, насколько это распространенные ошибки\. Зато содержит, на мой взгляд, дурацкие тестовые кейсы, которые в реальных программах не встречаются\. Я не могу представить, что в настоящем серьезном проекте можно повстречать вот такой код, который приводит к выходу за границу массива:

```cpp
void overrun_st_014 ()
{
  int buf[5];
  int index;
  index = rand();
  buf[index] = 1; /*Tool should detect this line as error*/
                  /*ERROR: buffer overrun */
  sink = buf[idx];
}
```

Пожалуй, такое можно встретить разве только в лабораторных работах студентов\.

При этом я знаю, что в серьезном проекте легко встретить опечатку вида:

```cpp
return (!strcmp (a->v.val_vms_delta.lbl1,
                 b->v.val_vms_delta.lbl1)
        && !strcmp (a->v.val_vms_delta.lbl1,
                    b->v.val_vms_delta.lbl1));
```

Эту ошибку анализатор PVS\-Studio [нашел](https://pvs-studio.ru/ru/blog/posts/cpp/0425/) в коде компилятора GCC\. Два раза сравниваются одни и те же строки\.

Получается, что тесты на поиск экзотического кода с _rand_ есть, а на классические опечатки нет\.

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

Предлагаю всем установить и попробовать мощнейший анализатор кода [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/)\.

**Дополнительные ссылки:**

1. [Диагностические возможности PVS\-Studio](https://pvs-studio.ru/ru/docs/warnings/)\.
1. [База реальных ошибок](https://pvs-studio.ru/ru/blog/examples/), найденных с помощью PVS\-Studio в открытых проектах\.
1. Мифы о статическом анализе\. [Миф пятый](https://pvs-studio.ru/ru/blog/posts/0119/) – можно составить маленькую программу, чтобы оценить инструмент\.
1. UPDATE\. [Что там у PVS\-Studio c покрытием Toyota ITC Benchmark?](https://pvs-studio.ru/ru/blog/posts/cpp/0931/)