Be#er Tes(ng with Less Work: QuickCheck Tes(ng in Prac(ce - - PowerPoint PPT Presentation

be er tes ng with less work quickcheck tes ng in prac ce
SMART_READER_LITE
LIVE PREVIEW

Be#er Tes(ng with Less Work: QuickCheck Tes(ng in Prac(ce - - PowerPoint PPT Presentation

Be#er Tes(ng with Less Work: QuickCheck Tes(ng in Prac(ce John Hughes QuickCheck in a Nutshell Test case Test case Test case Proper(es Test


slide-1
SLIDE 1

Be#er ¡Tes(ng ¡with ¡Less ¡Work: ¡ QuickCheck ¡Tes(ng ¡in ¡Prac(ce ¡

John ¡Hughes ¡

slide-2
SLIDE 2

QuickCheck ¡in ¡a ¡Nutshell ¡

Proper(es ¡

Test ¡case ¡ Test ¡case ¡ Test ¡case ¡ Test ¡case ¡ Test ¡case ¡ Minimal Test ¡case ¡

slide-3
SLIDE 3

Benefits ¡

  • Less ¡(me ¡spent ¡wri(ng ¡test ¡code ¡

– One ¡property ¡replaces ¡many ¡tests ¡

  • Be#er ¡tes(ng ¡

– Lots ¡of ¡combina(ons ¡you’d ¡never ¡test ¡by ¡hand ¡

  • Less ¡(me ¡spent ¡on ¡diagnosis ¡

– Failures ¡minimized ¡automagically ¡

slide-4
SLIDE 4

Tests ¡for ¡Base ¡64 ¡encoding ¡

base64_encode(Config) when is_list(Config) -> %% Two pads <<"QWxhZGRpbjpvcGVuIHNlc2FtZQ==">> = base64:encode("Aladdin:open sesame"), %% One pad <<"SGVsbG8gV29ybGQ=">> = base64:encode(<<"Hello World">>), %% No pad "QWxhZGRpbjpvcGVuIHNlc2Ft" = base64:encode_to_string("Aladdin:open sesam"), "MDEyMzQ1Njc4OSFAIzBeJiooKTs6PD4sLiBbXXt9" = base64:encode_to_string( <<"0123456789!@#0^&*();:<>,. []{}">>),

  • k.

Test ¡cases ¡ Expected ¡results ¡

slide-5
SLIDE 5

Wri(ng ¡a ¡Property ¡

prop_base64() -> ?FORALL(Data,list(choose(0,255)), base64:encode(Data)==???).

slide-6
SLIDE 6

Back ¡to ¡the ¡tests… ¡

base64_encode(Config) when is_list(Config) -> %% Two pads <<"QWxhZGRpbjpvcGVuIHNlc2FtZQ==">> = base64:encode("Aladdin:open sesame"), %% One pad <<"SGVsbG8gV29ybGQ=">> = base64:encode(<<"Hello World">>), %% No pad "QWxhZGRpbjpvcGVuIHNlc2Ft" = base64:encode_to_string("Aladdin:open sesam"), "MDEyMzQ1Njc4OSFAIzBeJiooKTs6PD4sLiBbXXt9" = base64:encode_to_string( <<"0123456789!@#0^&*();:<>,. []{}">>),

  • k.

Where ¡did ¡ these ¡come ¡ from? ¡

slide-7
SLIDE 7

Possibili(es ¡

  • Someone ¡converted ¡the ¡data ¡by ¡hand ¡
  • Another ¡base64 ¡encoder ¡
  • The ¡same ¡base64 ¡encoder! ¡

– Only ¡tests ¡that ¡changes ¡don’t ¡affect ¡the ¡result, ¡not ¡ that ¡the ¡result ¡is ¡right ¡ Use ¡the ¡other ¡ encoder ¡as ¡an ¡

  • racle ¡

Use ¡an ¡old ¡ version ¡(or ¡a ¡ simpler ¡version) ¡ as ¡an ¡oracle ¡

slide-8
SLIDE 8

Round-­‑trip ¡Proper(es ¡

What ¡does ¡this ¡test? ¡

  • NOT ¡a ¡complete ¡test—will ¡not ¡find ¡a ¡

consistent ¡misunderstanding ¡of ¡base64 ¡

  • WILL ¡find ¡mistakes ¡in ¡encoder ¡or ¡decoder ¡

Simple ¡proper6es ¡find ¡a ¡lot ¡of ¡bugs! ¡

prop_encode_decode() -> ?FORALL(L,list(choose(0,255)), base64:decode(base64:encode(L)) == list_to_binary(L)).

slide-9
SLIDE 9

Lessons ¡

  • Coming ¡up ¡with ¡proper(es ¡can ¡require ¡a ¡li#le ¡

ingenuity… ¡

  • …but ¡simple ¡proper(es ¡are ¡surprisingly ¡

effec(ve! ¡

  • Comparing ¡two ¡ways ¡to ¡compute ¡the ¡same ¡

thing ¡is ¡o"en ¡powerful! ¡

slide-10
SLIDE 10

Time ¡to ¡test ¡some ¡C ¡code… ¡

slide-11
SLIDE 11

Modelling ¡in ¡Erlang ¡

API ¡ Calls ¡ API ¡ Calls ¡ API ¡ Calls ¡ API ¡ Calls ¡

Model ¡ state ¡ Model ¡ state ¡ Model ¡ state ¡ Model ¡ state ¡

postcondi6ons ¡

slide-12
SLIDE 12

Example ¡

put ¡ 2 ¡ get ¡ put ¡ 3 ¡ get ¡ put ¡ 1 ¡

[1] ¡

[1,2] ¡

[2] ¡

[2,3] ¡

[] ¡

1 ¡ 2 ¡

Use ¡an ¡Erlang ¡list ¡to ¡model ¡the ¡contents ¡of ¡the ¡queue! ¡

slide-13
SLIDE 13

Code ¡Fragments ¡

next_state_data(_,_,S,_,{call,_,put,[_,X]}) ¡-­‑> ¡ ¡ ¡ ¡ ¡S#state{elements=S#state.elements++[X]}; ¡ next_state_data(_,_,S,_,{call,_,get,_}) ¡-­‑> ¡ ¡ ¡ ¡ ¡S#state{elements=tl(S#state.elements)}; ¡ postcondi(on(_,_,S,{call,_,get,_},Res) ¡-­‑> ¡ ¡ ¡ ¡ ¡Res ¡== ¡hd(S#state.elements); ¡ postcondi(on(_,_,S,{call,_,size,_},Res) ¡-­‑> ¡ ¡ ¡ ¡ ¡Res ¡== ¡length(S#state.elements); ¡

slide-14
SLIDE 14

A ¡QuickCheck ¡Property ¡

prop_q() -> ?FORALL(Cmds,commands(?MODULE), begin {H,S,Res} = run_commands(?MODULE,Cmds), Res == ok) end).

slide-15
SLIDE 15

Let’s ¡run ¡some ¡tests… ¡

slide-16
SLIDE 16

Lessons ¡

  • One ¡property ¡can ¡find ¡many ¡bugs ¡
  • Shrinking ¡makes ¡diagnosis ¡very ¡simple ¡
slide-17
SLIDE 17

AutoSAR ¡

  • Joint ¡project ¡with ¡Quviq, ¡SP, ¡Volvo ¡Cars ¡and ¡

Mentor ¡Graphics ¡

slide-18
SLIDE 18
slide-19
SLIDE 19

AutoSAR ¡Architecture ¡

slide-20
SLIDE 20

The ¡Story ¡So ¡Far… ¡

  • QuickCheck ¡state-­‑machine ¡models ¡for ¡

AutoSAR ¡clusters ¡ ¡

– Com, ¡PDUR, ¡CAN, ¡FlexRay, ¡Lin, ¡DEM… ¡

  • Used ¡to ¡test ¡soqware ¡from ¡3 ¡suppliers ¡

– test ¡code ¡10x ¡smaller ¡than ¡implementa(on ¡ – 20x ¡smaller ¡than ¡the ¡standard! ¡

  • Bugs ¡and ¡inconsistencies ¡revealed ¡in ¡all! ¡

– And ¡in ¡the ¡standard… ¡

slide-21
SLIDE 21
slide-22
SLIDE 22
slide-23
SLIDE 23

"We ¡know ¡there ¡is ¡a ¡lurking ¡bug ¡somewhere ¡ in ¡the ¡dets ¡code. ¡We ¡have ¡got ¡'bad ¡object' ¡ and ¡'premature ¡eof' ¡every ¡other ¡month ¡the ¡ last ¡year. ¡We ¡have ¡not ¡been ¡able ¡to ¡track ¡the ¡ bug ¡down ¡since ¡the ¡dets ¡files ¡is ¡repaired ¡ automa(cally ¡next ¡(me ¡it ¡is ¡opened.“ ¡ Tobbe ¡Törnqvist, ¡Klarna, ¡2007 ¡

slide-24
SLIDE 24

What ¡is ¡it? ¡

Applica(on ¡ Mnesia ¡ Dets ¡ File ¡system ¡

Invoicing ¡services ¡for ¡web ¡shops ¡ Distributed ¡database: ¡ transac(ons, ¡distribu(on, ¡ replica(on ¡ Tuple ¡storage ¡ 500+ ¡ people ¡in ¡ 5 ¡years ¡ Race ¡ condi(ons? ¡

slide-25
SLIDE 25

Imagine ¡Tes(ng ¡This… ¡

dispenser:take_(cket() ¡ dispenser:reset() ¡

slide-26
SLIDE 26
  • k =

1 = 2 = 3 =

  • k =

1 =

A ¡Unit ¡Test ¡in ¡Erlang ¡

test_dispenser() -> Expected ¡ results ¡

BUT… ¡

reset(), take_ticket(), take_ticket(), take_ticket(), reset(), take_ticket().

slide-27
SLIDE 27

A ¡Parallel ¡Unit ¡Test ¡

  • Three ¡possible ¡correct ¡
  • utcomes! ¡

reset ¡ take_(cket ¡ take_(cket ¡ take_(cket ¡ 1 ¡ 2 ¡ 3 ¡ 1 ¡ 3 ¡ 2 ¡ 1 ¡ 2 ¡ 1 ¡

  • k ¡
slide-28
SLIDE 28

Another ¡Parallel ¡Test ¡

  • 42 ¡possible ¡correct ¡outcomes! ¡

reset ¡ take_(cket ¡ take_(cket ¡ take_(cket ¡ take_(cket ¡ reset ¡

slide-29
SLIDE 29

Modelling ¡the ¡dispenser ¡

reset ¡

take ¡ take ¡ take ¡ 0 ¡ 0 ¡ 1 ¡ 2 ¡

  • k ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡1 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡2 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡3 ¡
slide-30
SLIDE 30

Parallel ¡Test ¡Cases ¡

reset ok ¡

take 1 ¡ take 3 ¡ take 2 ¡ 0 ¡ 0 ¡ 1 ¡ 2 ¡

slide-31
SLIDE 31

prop_parallel() -> ?FORALL(Cmds,parallel_commands(?MODULE), begin start(), {H,Par,Res} = run_parallel_commands(?MODULE,Cmds), Res == ok) end)).

Generate ¡parallel ¡ test ¡cases ¡ Run ¡tests, ¡check ¡for ¡a ¡ matching ¡serializa(on ¡

slide-32
SLIDE 32

Let’s ¡run ¡some ¡tests ¡

slide-33
SLIDE 33

Prefix: ¡ Parallel: ¡

  • 1. ¡take_(cket() ¡-­‑-­‑> ¡1 ¡
  • 2. ¡take_(cket() ¡-­‑-­‑> ¡1 ¡

Result: ¡no_possible_interleaving ¡ take_(cket() ¡-­‑> ¡ ¡ ¡ ¡ ¡N ¡= ¡read(), ¡ ¡ ¡ ¡ ¡write(N+1), ¡ ¡ ¡ ¡ ¡N+1. ¡

slide-34
SLIDE 34

dets ¡

  • Tuple ¡store: ¡

¡{Key, ¡Value1, ¡Value2…} ¡

  • Opera(ons: ¡

– insert(Table,ListOfTuples) ¡ – delete(Table,Key) ¡ – insert_new(Table,ListOfTuples) ¡ – … ¡

  • Model: ¡

– List ¡of ¡tuples ¡(almost) ¡

slide-35
SLIDE 35

QuickCheck ¡Specifica(on ¡

... ¡… ¡ ... ¡… ¡

<100 ¡LOC ¡

> ¡6,000 ¡ LOC ¡

slide-36
SLIDE 36

DEMO ¡

  • Sequen(al ¡tests ¡to ¡validate ¡the ¡model ¡
  • Parallel ¡tests ¡to ¡find ¡race ¡condiAons ¡
slide-37
SLIDE 37

Bug ¡#1 ¡

Prefix:

  • pen_file(dets_table,[{type,bag}]) -->

dets_table Parallel:

  • 1. insert(dets_table,[]) --> ok
  • 2. insert_new(dets_table,[]) --> ok

Result: no_possible_interleaving

insert_new(Name, ¡Objects) ¡-­‑> ¡Bool ¡ Types: ¡ Name ¡= ¡name() ¡ Objects ¡= ¡object() ¡| ¡[object()] ¡ Bool ¡= ¡bool() ¡

slide-38
SLIDE 38

Bug ¡#2 ¡

Prefix:

  • pen_file(dets_table,[{type,set}]) --> dets_table

Parallel:

  • 1. insert(dets_table,{0,0}) --> ok
  • 2. insert_new(dets_table,{0,0}) --> …time out…

=ERROR ¡REPORT==== ¡4-­‑Oct-­‑2010::17:08:21 ¡=== ¡ ** ¡dets: ¡Bug ¡was ¡found ¡when ¡accessing ¡table ¡dets_table ¡

slide-39
SLIDE 39

Bug ¡#3 ¡

Prefix:

  • pen_file(dets_table,[{type,set}]) --> dets_table

Parallel:

  • 1. open_file(dets_table,[{type,set}]) --> dets_table
  • 2. insert(dets_table,{0,0}) --> ok

get_contents(dets_table) --> [] Result: no_possible_interleaving

! ¡

slide-40
SLIDE 40

What’s ¡going ¡on? ¡

Dets ¡ server ¡ Reordering ¡and ¡ concurrency! ¡

slide-41
SLIDE 41

Is ¡the ¡file ¡corrupt? ¡

slide-42
SLIDE 42

Bug ¡#4 ¡

Prefix:

  • pen_file(dets_table,[{type,bag}]) --> dets_table

close(dets_table) --> ok

  • pen_file(dets_table,[{type,bag}]) --> dets_table

Parallel:

  • 1. lookup(dets_table,0) --> []
  • 2. insert(dets_table,{0,0}) --> ok
  • 3. insert(dets_table,{0,0}) --> ok

Result: ok

premature ¡eof ¡

slide-43
SLIDE 43

Bug ¡#5 ¡

Prefix:

  • pen_file(dets_table,[{type,set}]) --> dets_table

insert(dets_table,[{1,0}]) --> ok Parallel:

  • 1. lookup(dets_table,0) --> []

delete(dets_table,1) --> ok

  • 2. open_file(dets_table,[{type,set}]) --> dets_table

Result: ok false

bad ¡object ¡

slide-44
SLIDE 44

"We ¡know ¡there ¡is ¡a ¡lurking ¡bug ¡somewhere ¡ in ¡the ¡dets ¡code. ¡We ¡have ¡got ¡'bad ¡object' ¡ and ¡'premature ¡eof' ¡every ¡other ¡month ¡the ¡ last ¡year.” ¡ Tobbe ¡Törnqvist, ¡Klarna, ¡2007 ¡ Each ¡bug ¡fixed ¡the ¡day ¡aqer ¡ repor(ng ¡the ¡failing ¡case ¡

slide-45
SLIDE 45

How ¡come? ¡

  • The ¡bugs ¡weren’t ¡found ¡earlier? ¡

– Mature ¡produc(on ¡code ¡ – >6 ¡weeks ¡of ¡work ¡ – …files ¡of ¡over ¡4GB? ¡ – …rehashing ¡could ¡be ¡the ¡problem? ¡ – Diagnosing ¡races ¡in ¡produc(on ¡is ¡hopeless ¡

  • The ¡bugs ¡weren’t ¡found ¡in ¡tes(ng? ¡

– Unit ¡tests ¡for ¡races ¡are ¡hard ¡to ¡write… ¡ – …so ¡people ¡don’t! ¡ – Races=feature ¡interac(on ¡ ¡imprac(cally ¡many ¡tests ¡

slide-46
SLIDE 46

The ¡Ini(al ¡Phases ¡

  • Lots ¡of ¡work ¡to ¡develop ¡specifica(on ¡

– Understanding ¡and ¡genera(ng ¡test ¡inputs ¡

  • Many ¡errors ¡to ¡fix ¡in ¡the ¡specificaAon, ¡due ¡to… ¡

– New ¡code ¡is ¡buggy ¡ – Misunderstandings ¡of ¡the ¡informal ¡spec ¡ – Undocumented ¡features ¡of ¡the ¡system ¡ – Undocumented ¡limitaAons ¡of ¡the ¡system ¡

  • ”happy ¡case” ¡programming ¡
slide-47
SLIDE 47

Making ¡Progress ¡

  • QuickCheck ¡tends ¡to ¡find ¡the ¡same ¡problem ¡in ¡

every ¡run ¡

– There ¡is ¡a ¡”most ¡likely ¡bug” ¡ – Other ¡bugs ¡usually ¡shrink ¡to ¡the ¡most ¡likely ¡one ¡

  • To ¡make ¡progress, ¡the ¡most ¡likely ¡bug ¡must ¡be ¡

excluded ¡

– Bug ¡precondiAons ¡document ¡the ¡limita(ons ¡of ¡the ¡ system ¡

slide-48
SLIDE 48

The ¡Payoff ¡

  • Once ¡the ¡spec ¡is ¡corrected, ¡and ¡limita(ons ¡

accounted ¡for, ¡real ¡bugs ¡start ¡to ¡appear ¡

  • Each ¡extension ¡to ¡the ¡spec ¡yields ¡a ¡non-­‑linear ¡

improvement ¡in ¡the ¡variety ¡of ¡tests ¡

  • The ¡same ¡spec ¡can ¡find ¡many, ¡many ¡bugs ¡
slide-49
SLIDE 49

Summary: ¡Property-­‑Based ¡Tes(ng ¡

Be#er ¡tes(ng ¡for ¡ less ¡work! ¡