Вы находитесь на странице: 1из 41

Проблема наблюдаемости

Software Testing 101

Марат Ахин

Санкт-Петербургский политехнический университет

2018

Марат Ахин (СПбПУ) PP 2018 109 / 347


Quiz

Марат Ахин (СПбПУ) PP 2018 110 / 347


Recap

Марат Ахин (СПбПУ) PP 2018 111 / 347


Проблемы тестирования

What’s up, Doc? (с)


Проблема тестовых входных данных
Проблема наблюдаемости
Проблема «останова»
Проблема тестового оракула

Марат Ахин (СПбПУ) PP 2018 112 / 347


Содержание

1 Проблема наблюдаемости
Обеспечение распространения сбоя
Assertions
Журналирование

Марат Ахин (СПбПУ) PP 2018 113 / 347


Обеспечение распространения сбоя

Какими способами можно управлять выполнением кода?


Изменением входных данных
Изменением самого исходного кода

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


наблюдаемым снаружи (Propagation)

Марат Ахин (СПбПУ) PP 2018 114 / 347


Обеспечение распространения сбоя

Марат Ахин (СПбПУ) PP 2018 115 / 347


Assertions

Основной способ обеспечения наблюдаемости – assertions

1 private void c he ck I nv a ri an t s () {
2 assert elements [ tail ] == null ;
3 assert head == tail
4 ? elements [ head ] == null
5 : ( elements [ head ] != null &&
6 elements [( tail - 1) & ( elements . length - 1)] != null );
7 assert elements [( head - 1) & ( elements . length - 1)] == null ;
8 }

Марат Ахин (СПбПУ) PP 2018 116 / 347


Assertions

Что такое assertion?

Формула в логике первого порядка


Проверяется на истинность во время выполнения программы
Также может проверяться на истинность статически
Допускает возможность отключения проверки истинности

Марат Ахин (СПбПУ) PP 2018 117 / 347


Что дает использование assertions?

Проверка корректности внутреннего состояния

Внутреннее состояние обычно недоступно снаружи (полностью


или частично)
При изменении состояния хочется проверить, что оно остается
корректным

Марат Ахин (СПбПУ) PP 2018 118 / 347


Что дает использование assertions?

Неудача происходит ближе к причине ее возникновения

Чем больше задержка перед обнаружением неудачи, тем сложнее


найти ее исходную причину
Assertions позволяют найти неудачу практически в любой точке
программы

Марат Ахин (СПбПУ) PP 2018 119 / 347


Что дает использование assertions?

Явное документирование пред- и пост-условий

В общем случае программист ничего не знает о контракте


используемой функции
Использование assertions позволяет в явном виде описать
внешний контракт функции

Марат Ахин (СПбПУ) PP 2018 120 / 347


Assertions

Марат Ахин (СПбПУ) PP 2018 121 / 347


Какие проблемы связаны с assertions?

Ошибки в assertions

Побочные эффекты в assertions


Неправильное логическое условие срабатывания

Марат Ахин (СПбПУ) PP 2018 122 / 347


Какие проблемы связаны с assertions?

Влияние на производительность

Проверка assertions занимает время


Чем сложнее assertion, тем больше он замедляет работу
программы

Марат Ахин (СПбПУ) PP 2018 123 / 347


Какие проблемы связаны с assertions?

Эффект «вышибалы»

Сработавший assertion превращает любую ошибку в неудачу


Это полностью останавливает возможность дальнейшего
тестирования

Марат Ахин (СПбПУ) PP 2018 124 / 347


Какие проблемы связаны с assertions?

Сложность проверки определенных условий

Некоторые просто формулируемые условия крайне сложно


проверить на практике
Их реализация в виде assertion является крайне затруднительной

Марат Ахин (СПбПУ) PP 2018 125 / 347


Работают ли assertions?

Марат Ахин (СПбПУ) PP 2018 126 / 347


Работают ли assertions?

1 int * ptr = malloc ( sizeof ( int ) * 10);


2 assert ( ptr );

Адекватный assertion в правильном месте

Марат Ахин (СПбПУ) PP 2018 127 / 347


Работают ли assertions?

1 int * ptr = malloc ( sizeof ( int ) * 10);


2 assert ( ptr );

Замена обработки ошибок на assertion

Марат Ахин (СПбПУ) PP 2018 128 / 347


Работают ли assertions?

Марат Ахин (СПбПУ) PP 2018 129 / 347


Работают ли assertions?

Microsoft Office ≈ 1%
Proprietary software ≈ 3%
Open source software ≈ 5%
Eiffel software ≈ 7%

Сейчас assertions используются еще более широко

Марат Ахин (СПбПУ) PP 2018 130 / 347


Работают ли assertions?1

LLVM

≈ 500,000 SLOC
≈ 7000 assertions
> 400 ошибок, относящихся к assertions

1
http://blog.regehr.org/
Марат Ахин (СПбПУ) PP 2018 131 / 347
Работают ли assertions?1

GCC

≈ 1,000,000 SLOC
≈ 9500 assertions
> 200 ошибок, относящихся к assertions

1
http://blog.regehr.org/
Марат Ахин (СПбПУ) PP 2018 131 / 347
Работают ли assertions?2
1 assert ( B l o c k A d d r F w d R e f s . empty () && ...);
2 assert ( Ty == V - > getType () && ...);
3 assert (( Ty == 0 || Ty == V - > getType ()) && ...);
4 assert ( It != R e s o l v e C o n s t a n t s . end () && ...);
5 assert ( isa < ConstantExpr >( UserC ) && ...);
6 assert (V - > getType () - > isMetadataTy () && ...);
7 assert ((! Alignment || isPowerOf2_32 ( Alignment )) && ...);
8 assert (( Record [ i ] == 3 || Record [ i ] == 4) && ...);
9 assert ( Record [ i ] == 0 && ...);
10 assert ( Record [ i ] == 0 && ...);
11 assert ( ResultTy && ...);
12 assert ( TypeList [ NumRecords ] == 0 && ...);
13 assert ( NextBitCode == bitc :: M E T A D A T A _ N A M E D _ N O D E );
14 assert (( CT != Landi ngPadI nst :: Catch
15 || ! isa < ArrayType >( Val - > getType ())) && ...);
16 assert (( CT != Landi ngPadI nst :: Filter
17 || isa < ArrayType >( Val - > getType ())) && ...);
18 assert ( DFII != D e f e r r e d F u n c t i o n I n f o . end () && ...);
19 assert ( D e f e r r e d F u n c t i o n I n f o . count ( F ) && ...);
20 assert ( M == TheModule && ...);

2
http://blog.regehr.org/
Марат Ахин (СПбПУ) PP 2018 132 / 347
Журналирование

Марат Ахин (СПбПУ) PP 2018 133 / 347


Журналирование

Журналирование (logging)

Запись хода выполнения программы в том или ином виде


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

Марат Ахин (СПбПУ) PP 2018 134 / 347


Журналирование

Журналирование для Журналирование для


пользователя программиста

Высокоуровневые Низкоуровневые
сообщения сообщения
Как можно меньше Допустим любой шум
«мусора» Никаких ограничений на
Чем проще и понятнее формат сообщений
формат сообщений, тем
лучше

Марат Ахин (СПбПУ) PP 2018 135 / 347


Как вести журнал?

1 Result :: Ptr pr oc e ss B at ch J ob ( Job :: Ptr job ) {


2 // do the heavy lifting ...
3 }

Как записать ход выполнения программы?

Марат Ахин (СПбПУ) PP 2018 136 / 347


Как вести журнал?

1 Result :: Ptr T h r e a d e d P r o c e s s o r :: p r oc es s Ba tc h Jo b ( Job :: Ptr job ) {


2 log () << " Start of : " << job << endl ;
3 // do the heavy lifting ...
4 log () << " End of : " << job << endl ;
5 }

Ручная вставка журналирующих вызовов

Марат Ахин (СПбПУ) PP 2018 137 / 347


Как вести журнал?

1 aspect ProcessTracer {
2 advice call ( " % % Processor :: process %(%) " ) : before () {
3 log () << " Start of : " << * JoinedPoint :: arg <0 >() << endl ;
4 }
5 advice call ( " % % Processor :: process %(%) " ) : after () {
6 log () << " End of : " << * JoinedPoint :: arg <0 >() << endl ;
7 }
8 };

Журналирующие аспекты / интерсепторы

Марат Ахин (СПбПУ) PP 2018 138 / 347


Как вести журнал?

Logging as a Service

Марат Ахин (СПбПУ) PP 2018 139 / 347


Основная проблема журналирования
INFO [ http - thread - pool -8080(5) ] Received token : e6749451
TRACE [ http - thread - pool -8080(5) ] Calling : AuthStorageBean . getAuthData
TRACE [ http - thread - pool -8080(5) ] Called : AuthStorageBean . getAuthData -> 2.0708 E -5
INFO [ http - thread - pool -8080(5) ] Authentication data found : AuthData { authToken : e6749451
userId :1 firstName : lastName : Admin patrName : role : ru . korus . tmis . core . entity . model .
Role [ id =1] spec : }
TRACE [ http - thread - pool -8080(5) ] Calling : AuthStorageBean . getAuthDateTime
TRACE [ http - thread - pool -8080(5) ] Called : AuthStorageBean . getAuthDateTime -> 1.9825 E -5
INFO [ http - thread - pool -8080(5) ] Token is valid
TRACE [ http - thread - pool -8080(5) ] attempting to get session ; create = false ; session is
null = true ; session has id = false
TRACE [ http - thread - pool -8080(5) ] Authentication attempt received for token [ ru . korus . tmis
. core . auth . T m i s S h i r o T o k e n @ 3 7 b d 2 b 6 ]
DEBUG [ http - thread - pool -8080(5) ] Performing credentials equality check for
t o k e n C r e dentials of type [ java . lang . String and ac c ou n tC r ed e nt i al s of type [ java . lang
. String ]
DEBUG [ http - thread - pool -8080(5) ] Both credentials arguments can be easily converted to
byte arrays . Performing array equals comparison
DEBUG [ http - thread - pool -8080(5) ] Authentication successful for token [ ru . korus . tmis . core .
auth . T m i s S h i r o T o k e n @ 3 7 b d 2 b 6 ]. Returned account [( admin , ru . korus . tmis . core . entity .
model . Role [ id =1]) ]
DEBUG [ http - thread - pool -8080(5) ] No SecurityManager available in subject context map .
Falling back to SecurityUtils . g e tS e cu r it y Ma n ag e r () lookup .

Too much data!

Марат Ахин (СПбПУ) PP 2018 140 / 347


Основная проблема журналирования

Чем больше мы хотим узнать о ходе выполнения программы, тем


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

Марат Ахин (СПбПУ) PP 2018 141 / 347


Ограничение размера журнала

Необходимо ограничивать размер записываемых данных

Марат Ахин (СПбПУ) PP 2018 142 / 347


Уровни журналирования

Сообщения пишутся в журнал с определенным уровнем


В дальнейшем возможно фильтровать сообщения по уровням

Error / Warning / Info / Debug / Trace

Марат Ахин (СПбПУ) PP 2018 143 / 347


Домены журналирования

Домены ортогональны уровням журналирования


В зависимости от типа сообщения пишутся в разные домены

Database / Network / UI / Configuration / ...

Марат Ахин (СПбПУ) PP 2018 144 / 347


Стохастическое журналирование

В случае, если какие-то события встречаются очень часто,


достаточно записывать лишь их часть
1 if ( _ok == true ) {
2 _logger . log ( Level . WARNING , " Server seen down : " + _addr , e );
3 } else if ( Math . random () < 0.1) {
4 _logger . log ( Level . WARNING , " Server seen down : " + _addr );
5 }

Марат Ахин (СПбПУ) PP 2018 145 / 347


Сессионное журналирование

Часто работа ПО разбита на набор слабо связанных сессий


Каждая сессия может журналироваться независимо от других
1 try {
2 // logging ...
3 } catch ( Exception ex ) {
4 ctx . logging . d u m p C u r r e n t S e s s i o n ();
5 throw ;
6 } finally {
7 ctx . logging . reset ();
8 }

Марат Ахин (СПбПУ) PP 2018 146 / 347


Структурированное журналирование

Вместо простого текста журнал ведется в определенном формате


Структурированный формат облегчает поиск и анализ по журналу
1 {
2 " session_id " : " e6749451 " ,
3 " event " : " method_call " ,
4 " class_name " : " A ut h St o ra ge B ea n " ,
5 " method_name " : " getAuthData "
6 }

Марат Ахин (СПбПУ) PP 2018 147 / 347


W.I.L.T.

Марат Ахин (СПбПУ) PP 2018 148 / 347