Moscow Python Conf 2016. Почему 100% покрытие это плохо?

96
Цыганов Иван Positive Technologies Почему 100% покрытие это плохо

Transcript of Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Page 1: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Цыганов Иван Positive Technologies

Почему 100% покрытие это плохо

Page 2: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Обо мне

✤ Спикер PyCon Russia 2016, PiterPy, PyCon Siberia 2016

✤ Люблю OpenSource

✤ Не умею frontend

Page 3: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

✤ 15 лет практического опыта на рынке ИБ

✤ Более 650 сотрудников в 9 странах

✤ Каждый год находим более 200 уязвимостей нулевого дня

✤ Проводим более 200 аудитов безопасности в крупнейших компаниях мира ежегодно

Page 4: Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Page 5: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

MaxPatrol

✤ Pentest. Тестирование на проникновение.

✤ Audit. Системные проверки.

✤ Compliance. Соответствие стандартам.

✤ Одна из крупнейших баз знаний в мире

Система контроля защищенности и соответствия стандартам.

Page 6: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

✤ Тестирование на проникновение (Pentest)

✤ Системные проверки (Audit)

✤ Соответствие стандартам (Compliance)

✤ Одна из крупнейших баз знаний в мире

Система контроля защищенности и соответствия стандартам.

✤Системные проверки (Audit)

MaxPatrol

Page 7: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

> 50 000 строк кода

Page 8: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Зачем мы тестируем?

✤ Уверенность, что написанный код работает

✤ Ревью кода становится проще

✤ Гарантия, что ничего не сломалось при изменениях

Page 9: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Зачем проверять покрытие?

✤ Видно какой именно код протестирован

✤ Позволяет увидеть все ветви исполнения

Page 10: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Зачем проверять покрытие?

✤ Видно какой именно код протестирован

✤ Позволяет увидеть все ветви исполнения

✤ Метрика качества тестов (?)

Page 11: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Зачем нам 100%?

✤ Ачивка «У нас в проекте 100% coverage»

Page 12: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Зачем нам 100%?

✤ Ачивка «У нас в проекте 100% coverage»

✤ Уверенность, что код протестирован полностью

Page 13: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

100% coverage != 100% протестировано

Page 14: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

coverage.py

✤ Позволяет проверить покрытие кода тестами

✤ Есть плагин для pytest

✤ В основном работает

Page 15: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

coverage.py

def get_longest(a, b): if len(a) > len(b): return a return b

assert get_longest([1,2,3], [4,5]) == [1,2,3] assert get_longest([1,2], [3,4,5]) == [3,4,5] 

Page 16: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

coverage.py

def apply_discount(prices): result = {'Total': sum(prices)} if result['Total'] >= 1000: result['Discount'] = result['Total'] * 0.25 return result['Total'] - result['Discount']   assert apply_discount([400, 600]) == 750

Name Stmts Miss Cover Missing ---------------------------------------------------------- samples/apply_discount.py 5 0 100%

Page 17: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

coverage.py

def apply_discount(prices): result = {'Total': sum(prices)} if result['Total'] >= 1000: result['Discount'] = result['Total'] * 0.25 return result['Total'] - result['Discount']   assert apply_discount([400, 600]) == 750

>>> apply_discount([200])

Page 18: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

coverage.py

def apply_discount(prices): result = {'Total': sum(prices)} if result['Total'] >= 1000: result['Discount'] = result['Total'] * 0.25 return result['Total'] - result['Discount']   assert apply_discount([400, 600]) == 750

>>> apply_discount([200]) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in apply_discount KeyError: 'Discount'

Page 19: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

coverage.py --branch

1 def apply_discount(prices): 2 result = {'Total': sum(prices)} 3 if result['Total'] >= 1000: 4 result['Discount'] = result['Total'] * 0.25 5 return result['Total'] - result['Discount'] 6   7 assert apply_discount([400, 600]) == 750

Name Stmts Miss Branch BrPart Cover Missing --------------------------------------------------------------------- samples/apply_discount.py 5 0 2 1 85.71% 3 ->5

Page 20: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

coverage.py --branch

1 def apply_discount(prices): 2 result = {'Total': sum(prices)} 3 if result['Total'] >= 1000: 4 result['Discount'] = result['Total'] * 0.25 5 return result['Total'] - result['Discount'] 6   7 assert apply_discount([400, 600]) == 750

Name Stmts Miss Branch BrPart Cover Missing --------------------------------------------------------------------- samples/apply_discount.py 5 0 2 1 85.71% 3 ->5

Page 21: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Как считать покрытие?

Все строкиРеально выполненные

строки- Непокрытые строки=

Page 22: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Все строки

Source

coverage.parser.PythonParser

Statements

Page 23: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

coverage.parser.PythonParser

✤ Обходит все токены и отмечает «интересные» факты

✤ Компилирует код. Обходит code-object и сохраняет номера строк

Page 24: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Обход токенов

✤ Запоминает определения классов

✤ «Сворачивает» многострочные выражения

✤ Исключает комментарии

Page 25: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Обход байткода

✤ Полностью повторяет метод dis.findlinestarts

✤ Анализирует code_obj.co_lnotab

✤ Генерирует пару (номер байткода, номер строки)

Page 26: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Как считать coverage --branch?

Все переходыРеально выполненные

переходы- Непокрытые переходы=

Page 27: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Все переходы

Source

coverage.parser.AstArcAnalyzer

(from_line, to_line)

coverage.parser.PythonParser

Page 28: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

coverage.parser.AstArcAnalyzer

✤ Обходит AST с корневой ноды

✤ Обрабатывает отдельно каждый тип нод отдельно

Page 29: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Обработка ноды

class While(stmt): _fields = ( 'test', 'body', 'orelse', )

while i<10: print(i) i += 1

Page 30: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Обработка ноды

class While(stmt): _fields = ( 'test', 'body', 'orelse', )

while i<10: print(i) i += 1 else: print('All done')

Page 31: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Выполненные строки

sys.settrace(tracefunc)Set the system’s trace function, which allows you to implement a Python source code debugger in Python.

Trace functions should have three arguments: frame, event, and arg. frame is the current stack frame. event is a string: 'call', 'line', 'return', 'exception', 'c_call', 'c_return', or 'c_exception'. arg depends on the event type.

Page 32: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

PyTracer «call» event

✤ Сохраняем данные предыдущего контекста

✤ Начинаем собирать данные нового контекста

✤ Учитываем особенности генераторов

Page 33: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

PyTracer «line» event

✤ Запоминаем выполняемую строку

✤ Запоминаем переход между строками

Page 34: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

PyTracer «return» event

✤ Отмечаем выход из контекста

✤ Помним о том, что yield это тоже return

Page 35: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Отчет

✤ Что выполнялось

✤ Что должно было выполниться

✤ Ругаемся

Page 36: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Зачем такие сложности?

1 for i in some_list: 2 if i == 'Hello': 3 print(i + ' World!') 4 elif i == 'Skip': 5 continue 6 else: 7 break 8 else: 9 print(r'¯\_(ツ)_/¯')

Page 37: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Серебряная пуля?

Page 38: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Не совсем…

Page 39: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Что может пойти не так?

def make_user(name, email): return dict( ID=get_id(), Name=name, Role='Editor' if check_employee(email) else 'Guest' )  

Page 40: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Что может пойти не так?

def positive_squares(items): return map(lambda x: x **2 if x>0 else x, items)

Page 41: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Что может пойти не так?

def positive_squares(items): return [ item **2 for item in items if item>0 ]

Page 42: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Что может пойти не так?

def positive_squares(items): return [ item **2 for item in items if item>0 ]

def positive_squares(items): return map(lambda x: x **2 if x>0 else x, items)

def make_user(name, email): return dict( ID=get_id(), Name=name, Role='Editor' if check_employee(email) else 'Guest' )  

Page 43: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Что может пойти не так?

def positive_squares(items): return [ item **2 for item in items if item>0 ]

def positive_squares(items): return map(lambda x: x **2 if x>0 else x, items)

def make_user(name, email): return dict( ID=get_id(), Name=name, Role='Editor' if check_employee(email) else 'Guest' )  

Page 44: Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Page 45: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Непокрываемый код

def some_method(a, b, c): if a and b or c: return True return False

Page 46: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

sys.settrace(tracefunc)

✤ Устанавливаем свою функцию трассировки

✤ Смотрим что происходит и делаем выводы

Page 47: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

sys.settrace(tracefunc)

Ограниченное количество событий:✤ call ✤ line ✤ return ✤ exception

Page 48: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

sys.settrace(tracefunc)

Ограниченное количество событий:✤ call ✤ line ✤ return ✤ exception

Page 49: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

ast.NodeTransformer

✤ Обходим ноды

✤ Оборачиваем в «нечто» каждую ноду

✤ Запускаем и отслеживаем что выполнялось

Page 50: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

ast.NodeTransformer

✤ Сложно обернуть код, не изменив логику

✤ Не все ноды можно обернуть

Page 51: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

ast.NodeTransformer

✤ Сложно обернуть код, не изменив логику

✤ Не все ноды можно обернуть

Page 52: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Идея

✤ Перехватить контроль во время импорта

✤ Обойти байткод модуля

✤ Добавить вызов функции

✤ Собрать code-object

Page 53: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Идея

✤ Перехватить контроль во время импорта

✤ Обойти байткод модуля

✤ Добавить вызов функции

✤ Собрать code-object

Page 54: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

OpTracehttps: //github.com/tsyganov-ivan/OpTrace

Page 55: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

План

✤ Устанавливаем import hook

✤ Модифицируем и подменяем code-object

✤ Запускаем тесты

✤ Анализируем результаты

Page 56: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Import hook. Finder.

✤ Пропускаем неинтересные модули

✤ Создаем свой Loader для нужных модулей

Page 57: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Import hook. Loader.

✤ Получаем байт-код модуля

✤ Получаем исходный код модуля

✤ Модифицируем байт-код

✤ Возвращаем измененный байт-код

Page 58: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

План

✤ Устанавливаем import hook

✤ Модифицируем и подменяем code-object

✤ Запускаем тесты

✤ Анализируем результаты

Page 59: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrapper. Модифицируем байт-код.

# ... wrapper = Wrapper( trace_func=self.make_visitor(module_name), mark_func=self.make_marker(module_name, source) ) new_code = wrapper.wrap_code(code) return new_code # ...

Page 60: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrapper. Callbacks.

def make_marker(self, module, source): self.module_opcodes[module] = FileOpcode(module, source) def mark(codeobj_id, opcode): self.module_opcodes[module].add(codeobj_id, opcode.offset, opcode) return mark   def make_visitor(self, module): def visit(codeobj_id, opcode): self.module_opcodes[module].visit(codeobj_id, opcode.offset, opcode) return visit

Page 61: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

dis.dis(some_method)

def some_method(a, b, c): if a and b or c: return True return False

2 0 LOAD_FAST 0 (a) 3 POP_JUMP_IF_FALSE 18 6 LOAD_FAST 1 (b) 9 POP_JUMP_IF_TRUE 18 12 LOAD_FAST 2 (c) 15 POP_JUMP_IF_FALSE 22

3 >> 18 LOAD_CONST 1 (True) 21 RETURN_VALUE

4 >> 22 LOAD_CONST 2 (False) 25 RETURN_VALUE

Page 62: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

dis.get_instructions(some_method)

def some_method(a, b, c): if a and b or c: return True return False

Instruction(opname='LOAD_FAST', opcode=124, arg=0, ... Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, ...Instruction(opname='LOAD_FAST', opcode=124, arg=1, ... Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, ... Instruction(opname='LOAD_FAST', opcode=124, arg=2, ... Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, ... Instruction(opname='LOAD_CONST', opcode=100, arg=1, ... Instruction(opname='RETURN_VALUE', opcode=83, arg=None ... Instruction(opname='LOAD_CONST', opcode=100, arg=2, ... Instruction(opname='RETURN_VALUE', opcode=83, arg=None ...

Page 63: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrap code. Все опкоды.

✤ Просто вызываем функцию, переданную из Loader’a

self.mark(codeobj_id, st)

Page 64: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrap code. Трассировка.

✤ Добавляем lambda-функцию в константы

constants.append( lambda co_id=codeobj_id, opcode=st: self.visit(co_id, opcode) )

Page 65: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrap code. Трассировка.

✤ Добавляем lambda-функцию в константы

constants.append( lambda co_id=codeobj_id, opcode=st: self.visit(co_id, opcode) )

PyCodeObject* PyCode_New( /* ... */ PyObject *code, PyObject *consts, PyObject *names, /* ... */)

Page 66: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrap code. Трассировка.

✤ Добавляем lambda-функцию в константы

✤ Добавляем байт-код для вызова

def make_trace(self, constant_index): yield opcode.opmap['LOAD_CONST'] yield from self.make_args(constant_index) yield opcode.opmap['CALL_FUNCTION'] yield from self.make_args(0) yield opcode.opmap['POP_TOP']

Page 67: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrap code. Трассировка.

✤ Добавляем lambda-функцию в константы

✤ Добавляем байт-код для вызова

✤ Не забываем про оригинальный опкод и его параметры!

Page 68: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrap code. Трассировка.

✤ Добавляем lambda-функцию в константы

✤ Добавляем байт-код для вызова

✤ Не забываем про оригинальный опкод и его параметры!

✤ Учитываем смещение в последующих опкодах

Page 69: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrap сode. Результат.

def some_method(a, b, c): if a and b or c: return True return False

2 0 LOAD_FAST 0 (a) 3 POP_JUMP_IF_FALSE 18 6 LOAD_FAST 1 (b) 9 POP_JUMP_IF_TRUE 18 12 LOAD_FAST 2 (c) 15 POP_JUMP_IF_FALSE 22

3 >> 18 LOAD_CONST 1 (True) 21 RETURN_VALUE

4 >> 22 LOAD_CONST 2 (False) 25 RETURN_VALUE

Page 70: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrap сode. Результат.

6 LOAD_FAST 1 (b) 9 POP_JUMP_IF_TRUE 18 . . . 3 >> 18 LOAD_CONST 1 (True) 21 RETURN_VALUE

def some_method(a, b, c): if a and b or c: return True return False

Page 71: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Wrap сode. Результат.

20 LOAD_CONST 5 (<function ...<locals>.<lambda>) 23 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 26 POP_TOP 27 LOAD_FAST 1 (b) 30 LOAD_CONST 6 (<function ...<locals>.<lambda>) 33 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 36 POP_TOP 37 POP_JUMP_IF_TRUE 60 . . . >> 60 LOAD_CONST 9 (<function ...<locals>.<lambda>) 63 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 66 POP_TOP 67 LOAD_CONST 1 (True) 70 LOAD_CONST 10 (<function ...<locals>.<lambda>) 73 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 76 POP_TOP 77 RETURN_VALUE

Page 72: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

План

✤ Устанавливаем import hook

✤ Модифицируем и подменяем code-object

✤ Запускаем тесты

✤ Анализируем результаты

Page 73: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Тестируем. Все опкоды.

def some_method(a, b, c): if a and b or c: return True return False   some_method(1, 1, 0)

Instruction(opname='LOAD_FAST', opcode=124, arg=0, ... Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, ...Instruction(opname='LOAD_FAST', opcode=124, arg=1, ... Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, ... Instruction(opname='LOAD_FAST', opcode=124, arg=2, ... Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, ... Instruction(opname='LOAD_CONST', opcode=100, arg=1, ... Instruction(opname='RETURN_VALUE', opcode=83, arg=None ... Instruction(opname='LOAD_CONST', opcode=100, arg=2, ... Instruction(opname='RETURN_VALUE', opcode=83, arg=None ...

Page 74: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Тестируем. Непокрытые опкоды.

def some_method(a, b, c): if a and b or c: return True return False   some_method(1, 1, 0)

Instruction(opname='LOAD_FAST', arg=2, argval='c', argrepr='c', offset=12)Instruction(opname='POP_JUMP_IF_FALSE', arg=22, argval=22, argrepr='')Instruction(opname='LOAD_CONST', arg=2, argval=False, starts_line=3)Instruction(opname='RETURN_VALUE', arg=None, argval=None)

Page 75: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Тестируем. Непокрытые опкоды.

def some_method(a, b, c): if a and b or c: return True return False   some_method(1, 1, 0)

Instruction(opname='LOAD_FAST', arg=2, argval='c', argrepr='c', offset=12)Instruction(opname='POP_JUMP_IF_FALSE', arg=22, argval=22, argrepr='')Instruction(opname='LOAD_CONST', arg=2, argval=False, starts_line=3)Instruction(opname='RETURN_VALUE', arg=None, argval=None)

Page 76: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

План

✤ Устанавливаем import hook

✤ Модифицируем и подменяем code-object

✤ Запускаем тесты

✤ Анализируем результаты

Page 77: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

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

Page 78: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

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

Page 79: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Отчет. Ищем строки.

✤ При обходе сохраняем текущую строку

✤ При выводе опкода выводим текущую строку

Page 80: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Отчет. Ищем строки.

if a and b or c: Instruction(opname='LOAD_FAST', arg=2, argval='c', argrepr='c', offset=12)

if a and b or c: Instruction(opname='POP_JUMP_IF_FALSE', arg=22, argval=22, argrepr='')

return False Instruction(opname='LOAD_CONST', arg=2, argval=False, starts_line=3)

return FalseInstruction(opname='RETURN_VALUE', arg=None, argval=None)

✤ При обходе сохраняем текущую строку

✤ При выводе опкода выводим текущую строку

Page 81: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

✤ При обходе сохраняем текущую строку

✤ При выводе опкода выводим текущую строку

Отчет. Ищем строки.

if a and b or c: Instruction(opname='LOAD_FAST', arg=2, argval='c', argrepr='c', offset=12)

if a and b or c: Instruction(opname='POP_JUMP_IF_FALSE', arg=22, argval=22, argrepr='')

return False Instruction(opname='LOAD_CONST', arg=2, argval=False, starts_line=3)

return FalseInstruction(opname='RETURN_VALUE', arg=None, argval=None)

Page 82: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Отчет. Позиция в строке.

✤ Строка уже известна

✤ Вычислим позицию в строке для каждого типа опкода

Page 83: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Отчет. Позиция в строке.

if a and b or c:

Instruction( opname='LOAD_FAST', opcode=124, offset=12, starts_line=None, is_jump_target=True, arg=2, argval='c', argrepr=‘c' )

Page 84: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Отчет. Позиция в строке.

if a and b or c:

Instruction( opname='LOAD_FAST', opcode=124, offset=12, starts_line=None, is_jump_target=True, arg=2, argval='c', argrepr=‘c' )

Instruction( opname='POP_JUMP_IF_FALSE', opcode=114, offset=15, starts_line=None, is_jump_target=False, arg=22, argval=22, argrepr='' )

Page 85: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Отчет. Позиция в строке.

✤ Покрыв 70 типов опкодов удалось получить отчет

✤ Многие опкоды невозможно покрыть

Page 86: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Отчет. Позиция в строке.

✤ Покрыв 70 типов опкодов удалось получить отчет

✤ Многие опкоды невозможно покрыть

----------- Report tests.test_code -------------- 1: if a and b or c: ^ LOAD_FAST 1: if a and b or c: ^^^^^^^^^^^^^^^^ POP_JUMP_IF_FALSE 3: return False ^^^^^ LOAD_CONST 3: return False ^^^^^^^^^^^^ RETURN_VALUE

Page 87: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Отчет. Позиция в строке.

----------- Report tests.test_code -------------- 1: if a and b or c: ^ LOAD_FAST 1: if a and b or c: ^^^^^^^^^^^^^^^^ POP_JUMP_IF_FALSE 3: return False ^^^^^ LOAD_CONST 3: return False ^^^^^^^^^^^^ RETURN_VALUE

✤ Покрыв 70 типов опкодов удалось получить отчет

✤ Многие опкоды невозможно покрыть

Page 88: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

OpTrace. Что не так?

✤ Переменные в отчете не всегда отмечаются правильно

✤ Часть опкодов приходится пропускать

✤ Производительность неизвестна

Page 89: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

OpTrace. Что так?

✤ Трассировка работает хорошо

✤ Идея имеет право на жизнь

Page 90: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

OpTrace. Планы.

✤ Услышать мнение и критику сообщества

Page 91: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

OpTrace. Планы.

✤ Услышать мнение и критику сообщества

✤ Рефакторинг

✤ Тестирование

✤ Работа над улучшением отчета

✤ Плагин для pytest

Page 92: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

К чему это все?

Page 93: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Библиотеки несовершенны

Page 94: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

100% coverage расслабляет команду

Библиотеки несовершенны

Page 95: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

100% coverage расслабляет команду

Библиотеки несовершенны

100% coverage - просто ачивка

Page 96: Moscow Python Conf 2016. Почему 100% покрытие это плохо?

Спасибо за внимание! Вопросы?mi.0-0.im

tsyganov-ivan.com