Анализ структуры покрытий

Анализ структуры предопределенных покрытий

Во время работы тестовой системы может потребоваться отслеживать достижение различных ситуаций, определенных как элементы некоторого покрытия. Часть покрытий определяется явно при помощи указания способов определения того факта, реализовалась ли входящая в них ситуация, или нет. Часть покрытий вычисляется на основе операций над другими покрытиями. Здесь мы описываем третью часть, которая опеределяется неявно на основе операторов branch, mark и ветвлений в пред- и постусловиях.

Это 4 стандартных покрытия - функциональные ветви, маркированные пути, предикаты ветвлений и дизъюнкты. Далее определяется идентификация элементов разбиений по предикатам и дизъюнктам и строится таблица соответствия значений элементарных логических формул элементам 4-х стандартных покрытий, приводятся определения используемых терминов и алгоритм построения таблицы с примерами.

Существенным ветвлением называется разветвление потока управления в предусловии операции или постусловии операции, определяемое операторами if( ... ) { ... }, if( ... ) { ... } else { ... }, if( ... ) { ... } else if( ... ) { ... } else { ... } и пр., или switch( ... ) { ... }, имеющими в качестве условия или выражения выбора выражение, не являющееся константой, и не входящими в блок, помеченный как irrelevant, в блок одного их операторов try, catch или finally, или в цикл (такие блоки и циклы называем несущественными блоками).
Запрещено как-то прерывать поток выполнения от предусловия операции до операторов branch, или вторгаться в этот поток извне.
В частности,


Иначе, невозможно выделить ациклический граф потока управления предусловия и части постусловия, находящейся до операторовbranch, по которому можно вычислить стандартные критерии покрытия.

Элементарной формулой называется одно из следующих выражений Имена типов, используемые в элементарных формулах должны быть расширены до полных имен. Кроме того, надо освободить формулу от всех внешних скобок.
Элементарные формулы, являющиеся сравнениями встроенных типов, т.е., формулами вида $x ( == | != | <= | >= | < | > ) $y, которые по правилам Java приводятся к сравнениям выражений одного из целочисленных встроенных типов (включая char), а также сравнения значений перечислимого типа или объектов, должны приводится в каноническую форму при помощи следующих преобразований. Для сравнений выражений, имеющих типы float и double, действуют более сложные правила (приведены правила для типа double, для float - аналогично, с заменой java.lang.Double на java.lang.Float).

Заметим, что при этом могут появиться новые элементарные формулы вида java.lang.Double.IsNaN( $x) и java.lang.Float.IsNaN( $x).

После указанных преобразований элементарные формулы, отождествляются по текстуальному совпадению, за одним исключением - перед этим отождествлением локальным переменным, декларированным в пред- и постусловиях с одинаковыми именами, присваиваются различные имена, чтобы формулы с участием различных переменных не совпадали. Однако при подсчете дизъюнктов мы используем различные вхождения формул - здесь серьезное различие с подходами в предшествующих инструментах.
Это сделано для предотвращения пропуска некоторых путей при подсчете дизъюнктов из-за использования одной формулы несколько раз. Такой пропуск возникает, если перебирать значения формул так, как описано ниже, а совпадающие текстуально формулы помещать в таблицу только один раз.
Некоторые из них можно отождествить по текстуальному совпадению - это отождествление влияет на список элементарных формул в данном методе, заносимый в информацию о покрытиях, но не должно влиять на таблицу дизъюнктов - там все вхождения одной и той же формулы в код должны отражаться отдельно, в порядке их положения в коде.

Спецификация одной операции должна быть написаны таким образом, чтобы текстуально совпадающие элементарные формулы имели один и тот же смысл и одно и то же значение до предусловия, в предусловии и в постусловии до branch. Они могут поменять значение после branch.

Для устранения одной из вожможных ошибок можно запретить использование в совпадающих по тексту элементарных формулах различных локальных переменных.
Кроме того, как уже говорилось, локальные пременные пред- и постусловия считаются различными и текстуально одинаковые формулы, использующие одна - переменную из предусловия, другая - переменную из постусловия, считаются разными.

На структуру критериев покрытия влияют только некоторые элементарные формулы. Элементарные формулы, встречающиеся только в существенных ветвлениях, находящихся после операторов branch по потоку управления, в операторах return постусловия и формулы вида reads SE_Access, не влияют на определение покрытия (см. единственное исключение в точной формулировке алгоритма). Мы будем называть такие элементарные формулы несущественными.

Выражения, из которых построена элементарная формула при помощи операторов ==, !=, <=, >=, <, >, и пр., будем называть термами.

Дизъюнктом далее называется набор значений некоторого подмножества элементарных формул, однозначно определяющий путь по графу потока управления (построенного только с учётом существенных ветвлений) предусловия операции и постусловия той же операции до одного из операторов branch (если в постусловии есть хотя бы один, иначе постусловие не учитывается) и положительный результат предусловия, при этом содержащий минимальное число элементарных формул, т.е., из множества элементарных формул, имеющих значения в рамках определяющего дизъюнкта, ни одну нельзя перевести в разряд не имеющих значение так, чтобы полученное множество значений элементарных формул однознозначно определяло указанные путь и результат предусловия.
При этом считается, что элементарная формула использовалась при вычиселнии пути по графу, если при вычислении этого пути использовалось хотя бы одно вхождение этой формулы.
Если в пред- и постусловии нет существенных элементарных формул, то считаем, что единственный имеющийся путь соответствует специальному дизъюнкту true.

Предикатом (пока речь идет о покрытиях) называется логическое выражение, значение которого определяет существенное ветвление в графе потока управления пред- или пост- условия.

При построении предикатов рассматриваются существенные операторы if( ... ) и switch( ... ) такие что

Для оператора if( ... ) строятся два предиката.:

  1. условие if. Определяет переход на then-часть оператора if.
  2. отрицание этого условия. Определяет переход на else-часть оператора if или, если else не было, - на конечную точку if.

Для оператора switch( ... ) по выражением $variable (находящегося там же) предикаты строятся по следующим правилам. Метки (case и default), между которыми нет StatementSwitchElement, будем называть смежными метками.
Набор смежных между собой меток будем называть блоком смежных меток.
Предикат не строится, если

В остальных случаях: Полным предикатомназывается последовательность (конъюнкция) некоторого набора определяющих предикатов, которая однозначно задаёт путь по графу потока управления (построенного только с учётом существенных ветвлений и без учёта того, какие именно составляющиеусловий ветвлений были вычислены) предусловия и предусловия операции до одного из операторов branch и результат предусловия.
Если в пред- и постусловии нет предикатов, то считаем, что единственный имеющийся путь соответствует специальному полному предикату true.

Тавтологией называется логическое выражение, составленное при помощи логических операторов из нескольких элементарных формул, которое должно быть истинно при любых допустимых с точки зрения семантики входящих в него элементарных формул наборах их значений. Тавтологии оформляются в виде выражений в операторах tautology.
Тавтологии, содержащие другие элементарные формулы, помимо определяемых по коду пред- и постусловия, выбрасываются из рассмотрения.
Примитивной тавтологией называется следующее утверждение: для каждой элементарной формулы значения всех её вхождений должны быть равны.
Также среди тавтологий можно выделить множество таких, определить истинность которых можно не зная смысла их элементарных формул, а только на основе их построения из констант, идентификаторов переменных и методов при помощи операторов языка Java. Например, ясно, что (x > 5) || (x <= 5) для всякого числового x. Эти тавтологии мы будем называть естественными тавтологиями. Естественные тавтологии проще отслеживать, приводя элементарные формулы в какой-нибудь канонический вид.

Пример:
Пусть имеется следующие предусловие и постуловие некоторой операции.
{ 
    pre
{ if( !a && b )
{
mark "1"; return true; } else return x; } post { if( !a && b && !x ) { if( g(m) == 17 ) mark "2"; branch B1; return ( g(m) - 17 ) >= 0; } else if( m != 0 ) { branch B2; return x != a; } else { if( k > 5 )
{ if(!x) {...} branch B3; return f(a); } else { switch(k) { case 4: mark "3"; break; case 3: case2: ...; break; default: ...; } branch B4; return true; } } } }

В данном примере элементарными формулами являются следующие 11 выражений:
a, b, x, g(m) == 17, m == 0, 5 < k, k == 4, k == 3, k == 2, ( g(m) - 17 ) < 0, f(a)

Из них существенными являются только первые 9, остальные 2 встречаются только после операторовbranch.

Определяющими предикатами являются следующие 14 выражений:
P1 = '!a && b'
P2 = '!(!a && b)'
P3 = '!a && b && !x'
P4 = 'g(m) == 17'
P5 = '!(g(m) == 17)'
P6 = '!( !a && b && !x ) && ( m != 0 )'
P7 = '!( !a && b && !x ) && !( m != 0 )'
P8 = 'k > 5'
P9 = '!x'
P10 = '!(!x)'
P11 = '!(k > 5)'
P12 = 'k == 4'
P13 = 'k == 3 || k == 2'
P14 = 'k != 4 && k != 3 && k != 2'

Некоторые из них можно привести к более удобному для восприятия виду.
P1 = '!a && b'
P2 = 'a || !b'
P3 = '!a && b && !x'
P4 = 'g(m) == 17'
P5 = '!(g(m) == 17)'
P6 = '( a || !b || x ) && !( m == 0 )'
P7 = '( a || !b || x ) && ( m == 0 )'
P8 = 'k > 5'
P9 = '!x'
P10 = 'x'
P11 = '!(k > 5)'
P12 = 'k == 4'
P13 = 'k == 3 || k == 2'
P14 = 'k != 4 && k != 3 && k != 2'

Полный набор определяющих дизъюнктов указан в следующей таблице
"+" означает, что данная элементарная формула должна принимать значение true,
"-" - что данная элементарная формула должна принимать значение false,
"*" - что значение данной элементарной формулы не играет роли, она не используется в рамках соответствующего дизъюнкта
a b x g(m) == 17 m == 0 5 < k k == 4 k == 3 k == 2 дизъюнкт полный предикат марки ветвь
- + - + * * * * * d1 P1; P3; P4 1; 2; B1 B1
- + - - * * * * * d2 P1; P3; P5 1; B1
- + + * - * * * * d3 P1; P6 1; B2 B2
- - + * - * * * * d4 P2; P6 B2
+ * + * - * * * * d5
- + + * + + * * * d6 P1; P7; P8; P10 1; B3 B3
- - + * + + * * * d7 P2; P7; P8; P10 B3
+ * + * + + * * * d8
- + + * + - + - - d9 P1; P7; P11; P12 1; 3; B4 B4
- - + * + - + - - d10 P2; P7; P11; P12 3; B4
+ * + * + - + - - d11
- + + * + - - + * d12 P1; P7; P11; P13 1; B4
- + + * + - - - + d13
- + + * + - - - - d14 P1; P7; P11; P14
- - + * + - - + * d15 P2; P7; P11; P13 B4
+ * + * + - - + * d16
- - + * + - - - + d17
+ * + * + - - - + d18
- - + * + - - - - d19 P2; P7; P11; P14
+ * + * + - - - - d20

Отметим, что из таблицы следует, что код, помеченный предикатом P9 недостижим.

Должна быть определена опция работы транслятора-nd (-nodisjuncts ), которая отключает обсчет таблицы соответствия дизъюнктов. При включении этой опции составляются только полный список элементарных формул и список функциональных ветвей, соответсвующих операторам branch (если они есть, иначе считается, что имеется ровно одна функциональная ветвь). В общем случае при этом ничего нельзя сказать о количестве элементов в покрытиях по маркированным путям, предикатам и дизъюнктам.
При этом можно определять маркированные пути, предикаты и дизъюнкты двумя способами:
Можно просто не определять их для скомпилированных таким образом методов.
Можно генерировать некоторые заглушки, так чтобы в каждой ветви оказывалось бы по одному маркированному пути, одному предикату и одному дизъюнкту - для каждой ветви строится маркированный путь с идентификатором, совпадающим с идентификатором ветви, предикат true и дизъюнкт, соответствующий комбинации значений формул "**...*" (не зависит от элементарных формул). Этому дизъюнкту сопоставляется его ветвь, соответствующий маркированный путь и соответствующий предикат.

Алгоритм построения таблицы соответствий
  1. Находим все существенные элементарные формулы.
    Для этого в предусловии операции и её постусловии выделяются все существенные ветвления, находящиеся до операторов branch, определяются соответствующие определяющие предикаты и элементарные формулы.
    Кроме того, существенные элементарные формулы выделяются из выражений, стоящих в операторах return, находящихся в предусловиях.
    Далее в описании алгоритма элементарными формулами называются только существенные, соответственно, вхождениями элементарных формул - только существенные вхождения.
    Для всех определяющих предикатов генерируются числовые идентификаторы (#PID( $predicate ), например, если предикаты просто перенумеровать в порядке нахождения в коде), кроме того запоминается их логическое представление.
  2. Для того, чтобы сделать более эффективным учёт естественных тавтологий определённого вида, выделенные элементарные формулы приводятся к канонической форме, а именно, по указанным выше правилам
  1. В самом начале, до первого шага, набор значений вхождений элементарных формул выглядит так: (false, ... , false). Все вхождения считаются несущественными.
  2. Шаг: В начале каждого шага проверяется выполнение примитивных тавтологий.
    Если на текущем наборе значений вхождений элементарных формул все вхождения каждой элементарной формулы имеют одно и то же значение, переходим к шагу 3.
    Иначе ищем множество смежных несовместных вхождений D. Смежными несовместными вхождениями элементарной формулы ef называется пара её вхождений ( i , j ), имеющих различные значения, и между которыми нет других вхождений ef.
    Поиск элементов D проводится по списку вхождений элементарных формул слева направо.
    Считаем использованными при обсчёте примитивных тавтологий вхождения с номерами i и j такие, что ( i , j ) принадлежит D, и j имеет минимальное значение среди правых частей пар множества D.
    Переходим к п. 6.
  3. Далее проверяется выполнение естественных тавтологий и тавтологий, указанных в операторах tautology. Если значение хотя бы одной из тавтологий оказывается равно false, - переходим к п. 6.
  4. Делаем проход по предусловию операции, используя имеющийся набор значений вхождений формул, запоминаем множество вхождений, использованных при вычислении условий существенных ветвлений при данном проходе (попадание на некоторую ветку оператора switch( ... ) считается использованием формул вида( $variable == $value ), входящих в соответствующий этой ветке предикат). Так же запоминаем

    Если результат предусловия равен false, забываем запомненные последовательности и переходим к п. 6.

  5. Если в постусловии есть хотя бы один оператор branch, делаем проход по постусловию операции до первого встретившегося оператора branch, используя имеющийся набор значений вхождений элементарных формул, запоминаем множество вхождений, использованных при вычислении условий существенных ветвлений при данном проходе. Попадание на некоторую ветку оператора switch( ... ) считается использованием всех формул вида( $variable == $value ), входящих в соответствующий этой ветке предикат а так же в предикаты, порождённые предшествующими ветками того же switch, за исключением default-ветки.
    После этого добавляем в конец запомненных на предыдущем шаге последовательностей

    Если в постусловии нет операторов branch, или не было самого постусловия, сразу добавляем в конец последовательности строковых литералов в пройденных операторах mark идентификатор ветви Single.

    После заносим полученный дизъюнкт (набор true, false и неиспользованных формул) в таблицу. При занесении дизъюнкта в таблицу строим его идентификатор (например, номер), и заносим в таблицу, что он соответствует

  6. Здесь мы поступаем по разному, в зависимости от того, откуда мы попали на этот шаг.
  7. Конец построения таблицы соответствий.

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

Посмотрим, какой результат представленный выше алгоритм даёт на нашем примере (далее в таблице представлены результаты всех шагов алгоритма, при этом показаны текущие значения элементарных формул (+/-), использованные на каждом шаге формулы выделены знаком !, показано, почему текущий набор значений был отброшен, если это так, - проверки выполнения тавтологий, предусловия, проверка наличия эквивалентного дизъюнкта показаны знаками +/-, если же данный набор значений даёт определяющий дизъюнкт, показан идентификатор этого дизъюнкта в терминах предыдущей таблицы).
Добавим тавтологию a => (m=0). Это сделает недостижимым дизъюнкт d5.

a b x a b x g(m) == 17 m == 0 5 < k x k == 4 k == 3 k == 2 совпадение тавтологии предусловие дизъюнкт состав
-! -! -! - - - - - - - - - - + + -    
- - +! - - -! - - - - - - - -        
- - +! - - + - - - -! - - - -        
-! -! +! -! -! + - -! - + - - - + + + d4 --+*-****
- - +! - - + - + - -! - - - -        
-! -! +! -! -! + - +! -! + -! -! -! + + + d19 --+*+----
-! -! +! -! -! + - +! -! + -! -! +! + + + d17 --+*+---+
-! -! +! -! -! + - +! -! + -! +! - + + + d15 --+*+--+*
-! -! +! -! -! + - +! -! + +! - - + + + d10 --+*+-+**
- - +! - - + - + + -! - - - -        
-! -! +! -! -! + - +! +! +! - - - + + + d7 --+*++***
- +! - - -! - - - - - - - - -        
-! +! - -! +! -! -! - - - - - - + + + d2 -+--*****
-! +! - -! +! -! +! - - - - - - + + + d1 -+-+*****
- + -! - + +! - - - - - - - -        
-! + - +! - - - - - - - - - -        
- +! + - -! - - - - - - - - -        
- + +! - + -! - - - - - - - -        
- + +! - + + - - - -! - - - -        
-! +! + -! +! +! - -! - + - - - + + + d3 -++*-****
- + +! - + + - + - -! - - - -        
-! +! + -! +! +! - +! -! + -! -! -! + + + d14 -++*+----
-! +! + -! +! +! - +! -! + -! -! +! + + + d13 -++*+---+
-! +! + -! +! +! - +! -! + -! +! - + + + d12 -++*+--+*
-! +! + -! +! +! - +! -! + +! - - + + + d9 -++*+-+**
- + +! - + + - + + -! - - - -        
-! +! + -! +! +! - +! +! + - - - + + + d6 -++*++***
-! + + +! - - - - - - - - - -        
+! - - -! - - - - - - - - - -        
+! - - + - - - -! - - - - - + -      
+! - -! + - - - + - - - - - + + -    
+! - + -! - - - - - - - - - -        
+ - +! + - -! - - - - - - - -        
+ - +! + - + - - - -! - - - -        
+! - + + - + - -! - + - - - + -   (d5)  
+ - +! + - + - + - -! - - - -        
+! - +! +! - + - +! -! + -! -! -! + + + d20 +*+*+----
+! - +! +! - + - +! -! + -! -! +! + + + d18 +*+*+---+
+! - +! +! - + - +! -! + -! +! - + + + d16 +*+*+--+*
+! - +! +! - + - +! -! + +! - - + + + d11 +*+*+-+**
+ - +! + - + - + + -! - - - -        
+! - +! +! - + - +! +! + - - - + + + d8 +*+*++***