CUTE A Concolic Unit Testing Engine for C
- Slides: 63
CUTE: A Concolic Unit Testing Engine for C Koushik Sen Darko Marinov University of Illinois Urbana-Champaign Gul Agha
Goal n Automated Scalable Unit Testing of realworld C Programs q q Generate test inputs Execute unit under test on generated test inputs n q so that all reachable statements are executed Any assertion violation gets caught 2
Goal n Automated Scalable Unit Testing of realworld C Programs q q Generate test inputs Execute unit under test on generated test inputs n q n so that all reachable statements are executed Any assertion violation gets caught Our Approach: q Explore all execution paths of an Unit for all possible inputs n Exploring all execution paths ensure that all reachable statements are executed 3
Execution Paths of a Program n Can be seen as a binary tree with possibly infinite depth q n n n Computation tree Each node represents the execution of a “if then else” statement Each edge represents the execution of a sequence of non-conditional statements Each path in the tree represents an equivalence class of inputs 1 0 0 1 1 0 1 4
Example of Computation Tree void test_me(int x, int y) { if(2*x==y){ if(x != y+10){ printf(“I am fine here”); } else { printf(“I should not reach here”); ERROR; } } } N 2*x==y N Y x!=y+10 Y ERROR 5
Existing Approach I n Random testing q q n generate random inputs execute the program on generated inputs Probability of reaching an error can be astronomically less test_me(int x){ if(x==94389){ ERROR; } } Probability of hitting ERROR = 1/232 6
Existing Approach II n Symbolic Execution q q n use symbolic values for input variables execute the program symbolically on symbolic input values collect symbolic path constraints use theorem prover to check if a branch can be taken Does not scale for large programs test_me(int x){ if((x%10)*4!=17){ ERROR; } else { ERROR; } } Symbolic execution will say both branches are reachable: False positive 7
Approach n Combine concrete and symbolic execution for unit testing q n Concrete + Symbolic = Concolic In a nutshell q q Use concrete execution over a concrete input to guide symbolic execution Concrete execution helps Symbolic execution to simplify complex and unmanageable symbolic expressions n n by replacing symbolic values by concrete values Achieves Scalability q q Higher branch coverage than random testing No false positives or scalability issue like in symbolic execution based testing 8
Example typedef struct cell { int v; struct cell *next; } cell; int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } • Random Test Driver: • random memory graph reachable from p • random value for x • Probability of reaching abort( ) is extremely low 9
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } concrete state Symbolic Execution symbolic state constraints p , x=236 NULL p=p 0, x=x 0 10
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p , x=236 NULL p=p 0, x=x 0 11
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } x 0>0 p , x=236 NULL p=p 0, x=x 0 12
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } x 0>0 !(p 0!=NULL) p , x=236 NULL p=p 0, x=x 0 13
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete symbolic state solve: x 0>0 and p 0 NULL int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } Symbolic Execution constraints x 0>0 p 0=NULL p , x=236 NULL p=p 0, x=x 0 14
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete symbolic state solve: x 0>0 and p 0 NULL int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } Symbolic Execution x 0=236, p 0 constraints NULL 634 x 0>0 p 0=NULL p , x=236 NULL p=p 0, x=x 0 15
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } concrete state p 634 NULL , x=236 Symbolic Execution symbolic state constraints p=p 0, x=x 0, p->v =v 0, p->next=n 0 16
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p 634 NULL , x=236 p=p 0, x=x 0, p->v =v 0, p->next=n 0 x 0>0 17
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p 634 NULL , x=236 p=p 0, x=x 0, p->v =v 0, p->next=n 0 x 0>0 p 0 NULL 18
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p 634 NULL , x=236 p=p 0, x=x 0, p->v =v 0, p->next=n 0 x 0>0 p 0 NULL 2 x 0+1 v 0 19
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } x 0>0 p 0 NULL 2 x 0+1 v 0 p 634 NULL , x=236 p=p 0, x=x 0, p->v =v 0, p->next=n 0 20
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete symbolic state solve: x 0>0 and p 0 NULL and 2 x 0+1=v 0 int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } Symbolic Execution constraints x 0>0 p 0 NULL 2 x 0+1 v 0 p 634 NULL , x=236 p=p 0, x=x 0, p->v =v 0, p->next=n 0 21
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete symbolic state solve: x 0>0 and p 0 NULL and 2 x 0+1=v 0 int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } Symbolic Execution x 0=1, p 0 constraints NULL 3 x 0>0 p 0 NULL 2 x 0+1 v 0 p 634 NULL , x=236 p=p 0, x=x 0, p->v =v 0, p->next=n 0 22
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p 3 Symbolic Execution concrete state symbolic state NULL , x=1 p=p 0, x=x 0, p->v =v 0, p->next=n 0 constraints 23
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p NULL , x=1 3 p=p 0, x=x 0, p->v =v 0, p->next=n 0 x 0>0 24
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p NULL , x=1 3 p=p 0, x=x 0, p->v =v 0, p->next=n 0 x 0>0 p 0 NULL 25
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p NULL , x=1 3 p=p 0, x=x 0, p->v =v 0, p->next=n 0 x 0>0 p 0 NULL 2 x 0+1=v 0 26
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } x 0>0 p 0 NULL p NULL , x=1 3 p=p 0, x=x 0, p->v =v 0, p->next=n 0 2 x 0+1=v 0 n 0 p 0 27
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } x 0>0 p 0 NULL p NULL , x=1 3 2 x 0+1=v 0 n 0 p=p 0, x=x 0, p->v =v 0, p->next=n 0 28
CUTE Approach Concrete Execution Symbolic Execution typedef struct cell { int v; struct cell *next; } cell; concrete state int f(int v) { return 2*v + 1; } solve: x 0>0 and p 0 NULL and 2 x 0+1=v 0 and n 0=p 0 int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } symbolic state . p NULL , x=1 3 constraints x 0>0 p 0 NULL 2 x 0+1=v 0 n 0 p=p 0, x=x 0, p->v =v 0, p->next=n 0 29
CUTE Approach Concrete Execution Symbolic Execution typedef struct cell { int v; struct cell *next; } cell; concrete state int f(int v) { return 2*v + 1; } solve: x 0>0 and p 0 NULL and 2 x 0+1=v 0 and n 0=p 0 int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } symbolic state x 0=1, p 0 3 p NULL , x=1 3 constraints x 0>0 p 0 NULL 2 x 0+1=v 0 n 0 p=p 0, x=x 0, p->v =v 0, p->next=n 0 30
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } concrete state symbolic state , x=1 p=p 0, x=x 0, p->v =v 0, p->next=n 0 p 3 Symbolic Execution constraints 31
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p , x=1 3 p=p 0, x=x 0, p->v =v 0, p->next=n 0 x 0>0 32
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p , x=1 3 p=p 0, x=x 0, p->v =v 0, p->next=n 0 x 0>0 p 0 NULL 33
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } p , x=1 3 p=p 0, x=x 0, p->v =v 0, p->next=n 0 x 0>0 p 0 NULL 2 x 0+1=v 0 34
CUTE Approach Concrete Execution typedef struct cell { int v; struct cell *next; } cell; concrete state Symbolic Execution symbolic state constraints int f(int v) { return 2*v + 1; } int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0; } x 0>0 p 0 NULL Program Error p , x=1 3 p=p 0, x=x 0, p->v =v 0, p->next=n 0 2 x 0+1=v 0 n 0=p 0 35
Explicit Path (not State) Model Checking n Traverse all execution paths one by one to detect errors q q check for assertion violations check for program crash combine with valgrind to discover memory leaks detect invariants 1 0 0 1 1 0 1 37
Explicit Path (not State) Model Checking n Traverse all execution paths one by one to detect errors q q check for assertion violations check for program crash combine with valgrind to discover memory leaks detect invariants 1 0 0 1 1 0 1 38
Explicit Path (not State) Model Checking n Traverse all execution paths one by one to detect errors q q check for assertion violations check for program crash combine with valgrind to discover memory leaks detect invariants 1 0 0 1 1 0 1 39
Explicit Path (not State) Model Checking n Traverse all execution paths one by one to detect errors q q check for assertion violations check for program crash combine with valgrind to discover memory leaks detect invariants 1 0 0 1 1 0 1 40
Explicit Path (not State) Model Checking n Traverse all execution paths one by one to detect errors q q check for assertion violations check for program crash combine with valgrind to discover memory leaks detect invariants 1 0 0 1 1 0 1 41
Explicit Path (not State) Model Checking n Traverse all execution paths one by one to detect errors q q check for assertion violations check for program crash combine with valgrind to discover memory leaks detect invariants 1 0 0 1 1 0 1 42
Explicit Path (not State) Model Checking n Traverse all execution paths one by one to detect errors q q check for assertion violations check for program crash combine with valgrind to discover memory leaks detect invariants 1 0 0 1 1 0 1 43
Explicit Path (not State) Model Checking n Traverse all execution paths one by one to detect errors q q check for assertion violations check for program crash combine with valgrind to discover memory leaks detect invariants 1 0 0 1 1 0 1 44
CUTE in a Nutshell n Generate concrete inputs one by one q each input leads program along a different path 45
CUTE in a Nutshell n n Generate concrete inputs one by one q each input leads program along a different path On each input execute program both concretely and symbolically 46
CUTE in a Nutshell n n Generate concrete inputs one by one q each input leads program along a different path On each input execute program both concretely and symbolically q Both cooperate with each other n concrete execution guides the symbolic execution 47
CUTE in a Nutshell n n Generate concrete inputs one by one q each input leads program along a different path On each input execute program both concretely and symbolically q Both cooperate with each other n concrete execution guides the symbolic execution n concrete execution enables symbolic execution to overcome incompleteness of theorem prover q replace symbolic expressions by concrete values if symbolic expressions become complex q resolve aliases for pointer using concrete values q handle arrays naturally 48
CUTE in a Nutshell n n Generate concrete inputs one by one q each input leads program along a different path On each input execute program both concretely and symbolically q Both cooperate with each other n concrete execution guides the symbolic execution n concrete execution enables symbolic execution to overcome incompleteness of theorem prover q replace symbolic expressions by concrete values if symbolic expressions become complex q resolve aliases for pointer using concrete values q handle arrays naturally n symbolic execution helps to generate concrete input for next execution q increases coverage 49
Testing Data-structures of CUTE itself n Unit tested several non-standard datastructures implemented for the CUTE tool q q q n cu_depend (used to determine dependency during constraint solving using graph algorithm) cu_linear (linear symbolic expressions) cu_pointer (pointer symbolic expressions) Discovered a few memory leaks and a couple of segmentation faults q q these errors did not show up in other uses of CUTE for memory leaks we used CUTE in conjunction with Valgrind 50
SGLIB: popular library for C data-structures n n Used in Xrefactory a commercial tool for refactoring C/C++ programs Found two bugs in sglib 1. 0. 1 q q n reported them to authors fixed in sglib 1. 0. 2 Bug 1: q doubly-linked list library n n n segmentation fault occurs when a non-zero length list is concatenated with a zero-length list discovered in 140 iterations ( < 1 second) Bug 2: q hash-table n q an infinite loop in hash table is member function 193 iterations (1 second) 51
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } n Let initially x = -3 and y = 7 generated by random testdriver 52
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } n Let initially x = -3 and y = 7 generated by random testdriver n concrete z = 9 symbolic z = x*x*x + 3*x*x+9 n n take then branch with constraint x*x*x+ 3*x*x+9 != y 53
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } n Let initially x = -3 and y = 7 generated by random testdriver n concrete z = 9 symbolic z = x*x*x + 3*x*x+9 n n take then branch with constraint x*x*x+ 3*x*x+9 != y solve x*x*x+ 3*x*x+9 = y to take else branch Don’t know how to solve !! q Stuck ? 54
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } n Let initially x = -3 and y = 7 generated by random testdriver n concrete z = 9 symbolic z = x*x*x + 3*x*x+9 n n take then branch with constraint x*x*x+ 3*x*x+9 != y solve x*x*x+ 3*x*x+9 = y to take else branch Don’t know how to solve !! q Stuck ? q NO : CUTE handles this smartly 55
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } n Let initially x = -3 and y = 7 generated by random testdriver 56
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } n Let initially x = -3 and y = 7 generated by random testdriver n concrete z = 9 symbolic z = x*x*x + 3*x*x+9 q cannot handle symbolic value of z n 57
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } n Let initially x = -3 and y = 7 generated by random testdriver n concrete z = 9 symbolic z = x*x*x + 3*x*x+9 q cannot handle symbolic value of z q make symbolic z = 9 and proceed n 58
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } n Let initially x = -3 and y = 7 generated by random testdriver n concrete z = 9 symbolic z = x*x*x + 3*x*x+9 q cannot handle symbolic value of z q make symbolic z = 9 and proceed n n take then branch with constraint 9 != y 59
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } n Let initially x = -3 and y = 7 generated by random testdriver n concrete z = 9 symbolic z = x*x*x + 3*x*x+9 q cannot handle symbolic value of z q make symbolic z = 9 and proceed n n take then branch with constraint 9 != y solve 9 = y to take else branch execute next run with x = -3 and y= 9 q got error (reaches abort) 60
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } Replace symbolic expression by } concrete value when symbolic expression becomes unmanageable (i. e. non-linear) n Let initially x = -3 and y = 7 generated by random testdriver n concrete z = 9 symbolic z = x*x*x + 3*x*x+9 q cannot handle symbolic value of z q make symbolic z = 9 and proceed n n take then branch with constraint 9 != y solve 9 = y to take else branch execute next run with x = -3 and y= 9 q got error (reaches abort) 61
Simultaneous Symbolic & Concrete Execution void again_test_me(int x, int y){ z = x*x*x + 3*x*x + 9; if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } void again_test_me(int x, int y){ z = black_box_fun(x); if(z != y){ printf(“Good branch”); } else { printf(“Bad branch”); abort(); } } 62
Related Work n “DART: Directed Automated Random Testing” by Patrice Godefroid, Nils Klarlund, and Koushik Sen (PLDI’ 05) q n handles only arithmetic constraints CUTE q Supports C with n q Highly efficient constraint solver n q q q pointers, data-structures 100 -1000 times faster arithmetic, pointers Provides Bounded Depth-First Search and Random Search strategies Publicly available tool that works on ALL C programs 63
Discussion n CUTE is q light-weight q dynamic analysis (compare with static analysis) n q concrete execution and symbolic execution run simultaneously n q n n symbolic execution consults concrete execution whenever dynamic analysis becomes intractable real tool that works on all C programs n n ensures no false alarms completely automatic Requires actual code that can be fully compiled Can sometime reduce to Random Testing Complementary to Static Analysis Tools 64
- Cute: a concolic unit testing engine for c
- Functional testing vs unit testing
- My cute one
- Monkey life cycle
- Finish verb 3
- Life cycle of a
- As cute as a kitten simile
- She look cute
- 666 nnn
- Lion writing
- Flat adverbs examples
- Sleeping pig
- Cute pronouns
- How to remember reactivity series
- He looks cute
- O vicryl suture
- Cute superlative
- Pdf cute
- Cute pine marten
- External and internal combustion engine
- Volumetric efficiency of engine
- Motoring test is used to measure
- Unit 10, unit 10 review tests, unit 10 general test
- Domain testing in software testing
- Motivational overview of logic based testing
- Du path testing
- Positive vs negative testing
- Cs 3250
- Localization globalization testing
- Language testing
- Control structure testing in software testing
- Decision table testing in software testing
- Decision table testing example
- Pengertian black box testing
- Behavior testing adalah
- Decision table based testing in software testing
- Rigorous testing in software testing
- Testing blindness in software testing
- Component testing is a black box testing
- Software domain examples
- Formuö
- Novell typiska drag
- Tack för att ni lyssnade bild
- Ekologiskt fotavtryck
- Varför kallas perioden 1918-1939 för mellankrigstiden
- En lathund för arbete med kontinuitetshantering
- Adressändring ideell förening
- Personlig tidbok
- Sura för anatom
- Förklara densitet för barn
- Datorkunskap för nybörjare
- Tack för att ni lyssnade bild
- Tes debattartikel
- Delegerande ledarskap
- Nyckelkompetenser för livslångt lärande
- Påbyggnader för flakfordon
- Formel för lufttryck
- Offentlig förvaltning
- Jag har nigit för nymånens skära
- Presentera för publik crossboss
- Jiddisch
- Kanaans land
- Treserva lathund
- Luftstrupen för medicinare