Software Testing for Conventional and Logic Programming 9783110816006, 9783110150711


170 53 7MB

English Pages 251 [252] Year 1996

Report DMCA / Copyright

DOWNLOAD PDF FILE

Table of contents :
List of Figures
Symbols and Notation
1 Introduction
2 Program Testing Concepts
2.1 Background and General Terminology
2.2 Tractability Problems of Formal Verification
2.3 Basic Testing Approaches
2.4 Formal Testing
2.5 Asymptotic Testing
2.6 Resume
3 Logic Programming Concepts
3.1 Syntax of First-Order Predicate Logic
3.2 Model Theoretic Semantics
3.3 Fixed Point Semantics
3.4 Resolution Calculus and Operational Semantics
3.5 Résumé
4 Program Instrumentation
4.1 Types and Typings
4.2 Regular Parametric Types
4.3 Type Relations
4.4 Modes and Data Flow
4.5 Résumé
5 Test Coverage
5.1 Anti-Unification
5.2 Coverage and Anti-Unification
5.3 Coverage Computation
5.4 Résumé
6 Test Input Generation
6.1 Coverage Driven Generation
6.2 Typed Goals
6.3 Moded Goals
6.4 Résumé
7 Complexity Analysis
7.1 Coverage Computation Complexity
7.2 Coverage Generation Complexity
7.3 Résumé
8 Summary
Appendix A The PROTest System
A.1 Product Assurance Environment
A.2 PROTest System Overview
A.3 PROTest Type and Mode Declarations
A.4 Structure Checker
A.5 Test Case Generator
A.6 Test Coverage Analyzer
A.7 Test Driver
A.8 Test Report Generator
Appendix B Case Study
B.1 Program Example
B.2 The Robot Task Scheduling Program
B.3 Coverage Results
B.4 Generated Test Inputs
Bibliography
Index
Recommend Papers

Software Testing for Conventional and Logic Programming
 9783110816006, 9783110150711

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

Programming Complex Systems 10 Edited by Fevzi Belli and Hinrich E. G. Bonin

Oliver Jack

Software Testing for Conventional and Logic Programming

w DE

G

Walter de Gruyter Berlin · New York 1996

Dr.-Ing. Oliver Jack, Department of Electrical a n d Electronics Engineering, University of Paderborn, Paderborn, G e r m a n y

® Printed on acid-free paper which falls within the guidelines of the ANSI to ensure permanence and durability.

Library of Congress Cataloging-in-Publication

Data

Jack, Oliver. Software testing for conventional and logic programming / Oliver Jack. — (Programming complex systems ; 10) Includes bibliographical references and index. ISBN 3-11-015071-9 (cloth ; alk. paper) 1. Computer software-Testing. I. Title. II. Series. QA76.76.T48J33 1996 005.1 ' 4 - d c 2 0 96-16969 CIP

Die Deutsche Bibliothek — CI Ρ-Einheitsaufnahme Jack, Oliver: Software testing for conventional and logic programming / Oliver Jack. - Berlin ; New York : de Gruyter, 1996 (Programming complex systems ; 10) Zugl.: Paderborn, Univ., Diss., 1995 ISBN 3-11-015071-9 NE: Programmierung komplexer Systeme

© Copyright 1996 by Walter de Gruyter & Co., D-10785 Berlin All rights reserved, including those of translation into foreign languages. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopy, recording or any information storage and retrieval system, without permission in writing from the publisher. Printing: W. Hildebrand, Berlin. - Binding: Mikolai G m b H , Berlin. — Cover Design: Johannes Rother, Berlin. - Printed in Germany

A few observations and much reasoning lead to error Many observations and a little reasoning to truth. — Alexis Carrel

Editorial Notes The present book "Software Testing for Conventional and Logic Programming" extends and completes the topic of the Vol. 6 of this series, "Software Reliability Determination for Conventional and Logic Programming", without endangering the capability of being self-containedness of either books. Both authors, A. Azem and O. Jack, handle the same area of the Software Engineering although they have different approaches, i.e. assessment of complex systems which can be viewed as a necessary, because complementary aspect of "Programming Complex Systems". Also this book is very likely to be the first comprehensive monograph handling a problem, the importance of which daily becomes obvious as computers will be increasingly embedded in complex applications: systematic testing of software, both for conventional and logic programs. Testing must not be seen as a competitive activity to validate software; its strength stems from its persuasive capability to demonstrate the performance of the system under test within the host or target environment in accordance with the specific application, including the requirements to be fulfilled concerning real time constrains and scope of functionality, as seen from the user's side. The author identifies not only these advantages, but also precises the problems of testing. He concentrates on the problem of the generation and selection of test cases, based on a solid theoretical foundation he develops. He also demonstrates the feasibility and efficiency of his approach by means of a comfortable test environment. We hope the subject "Software Analysis and Assessment", and also this book will find the attention they both deserve.

The Editors Paderborn and Lüneburg, February 1996

Preface Software testing is by far the most widely used method for software correctness assessment. Nevertheless, testing lacks of commonly accepted mathematical rigorous methods compared to other software examination methods like formal correctness verification. There is no agreement upon definitions of even basic terms such as error and test, not to mention more complex concepts such as the power of a test. In the world of the theoretician, program verification is still a more esteemed research field than testing. And indeed, with testing one cannot hope to settle the correctness problem unequivocally, since testing is a falsification technique. But everyone who ever has written a computer program had felt the need to test it before getting confident about its correctness. Today, verification of computer programs appears far from being applicable to real world programs. Hence testing will be with us for some time to come. However, analytic methods such as verification should be used whenever they are feasible, and any testing theory should retain verification as an ideal limiting case. One should always remember that it is the need of correctness that motivates testing. In the past two decades, attempts have already been made to a better theoretical understanding of testing. However, these attempts have been limited to conventional programming. The programming paradigm, which undoubtly has implications for the process of software development, must also be considered in the view of testing. In contrast to conventional programming, logic programming favors mathematical accountability in giving precise mathematical characterizations of relationships between programs and their results, programs and specifications, and programs and other programs. It separates knowledge from its use. It is a uniform paradigm for software technology. There is one formalism for constructing and manipulating programs, specifications, and databases. The high abstraction level and rigor mathematical foundation of logic programming as well as its uniform framework of programs and specifications put it in a favorite position for software engineering. However, the knowledge of testing logic programs is yet very poor. Since testing is in general not capable of verifying a computer program, the aim of this book is to provide the reader with criteria for judging testing,

χ

Preface

in particular testing of high level declarative programs. Starting with the traditional notions of testing, particularly based on imperative programming, this book concentrates on implementation-based testing for very high level programming languages. The concepts and methods are developed for a typical representative of declarative languages, Prolog. It tackles the problems of testing by introducing mathematical rigor concepts and methods for automated tractable test input generation. Chapter 1 gives a general introduction to the problem of software testing, and discusses software examination methods, particularly formal verification and testing. It is pointed out that the programming paradigm has an impact on both, verification and testing of programs. Chapter 2 develops general concepts of program testing with a focus on conventional (imperative) programming. The basic state-of-the-art techniques are introduced in a notation suitable for the development of our novel program testing concept. Since there are many different concepts and notations, the relations between them are carefully analyzed. Chapter 3 concentrates on the high level mathematical rigor programming paradigm of logic programming. The essential concepts are presented and analyzed, introducing a vocabulary which prepares for the subsequent chapters. In Chapter 4 the field of logic programming is opened to the area of testing. The first concept for program testing is developed: Program instrumentation by additional declarative information within the program to be tested is adopted from previous research in compiler optimization and runtime type checking. The declarative program instrumentation concepts are synthesized with data flow concepts of program modes. This enables the application of a data based and data flow based method to program testing. Chapter 5 introduces the second novel program testing concept: A test coverage notion for logic programming is developed which is based entirely on the computational model of logic programming. A mathematical rigor characterization of test coverage is presented which considers the key concept of unification in the operational semantics of logic programming. In logic programming the emphasis is on data and data flow, while in imperative programming the emphasis is on control flow. Hence the coverage notion for logic programming will be data oriented. Sometimes it is also called data coverage. Chapter 6 utilizes the results of chapters 4 and 5 for a method of automatic test input generation. With the proposed method, a test input set for a program can be generated which is completely data covering. Chapter 7 contains a detailed analysis of the algorithms concerning test coverage. The computational complexity of the test input generation algorithm will be discussed and the complexity, i.e., the cardinality, of the data

XI

Preface

covering test input set with respect to the program and its instrumentation will be analyzed. Finally, Chapter 8 is dedicated to discussions and concluding remarks on the proposed program testing approach. Appendix A describes the program test environment PROTest which is an implementation of the concepts and methods which have been developed in chapters 4 and 5. The environment includes also other features, e.g., a test report generator. Appendix Β presents an extensive example of test input generation using the PROTest system. The example uses a program for concurrent robot programming, in particular an off-line Prolog program for robot task scheduling.

Acknowledgments I wish to thank a number of persons for their involvement in the course of writing this book. I am particularly indebted to Professor Dr.-Ing. Fevzi Belli for his support with my research work at the University of Paderborn and Professor Dr.-Ing. Erik Maehle for valuable hints and discussions. Throughout the years I have been with the Department of Electrical and Electronics Engineering my colleagues deserve thanks for the encouragement they gave me, in particular Alireza Azem, Radu Cri§an, and Alfried Pollmann. My view of software engineering and logic programming has been greatly enriched by discussions with many people. In this regard, I would particularly like to thank my colleagues at the Department of Electrical and Electronics Engineering and Lee Naish who gave me a deeper insight in logic programming. For suggestions and improvements of earlier versions of this work I would like to thank Stefan Schurig. My tanks are also due to Armin Bühler for his support with several implementation aspects concerning the graphical user interface of the PROTest system; and for serving as my safety net, I thank Maria Behrens and my Family. Also, I shall be forever grateful to a host of unnamed friends without whose support I would not have been able to finish this book.

Paderborn,

Oliver February

Jack 1996

Contents List of Figures Symbols and Notation

XV XVII

1

Introduction

1

2

Program Testing Concepts 2.1 Background and General Terminology 2.2 Tractability Problems of Formal Verification 2.3 Basic Testing Approaches 2.4 Formal Testing 2.5 Asymptotic Testing 2.6 Resume

7 7 10 13 14 18 31

3

Logic Programming Concepts 3.1 Syntax of First-Order Predicate Logic 3.2 Model Theoretic Semantics 3.3 Fixed Point Semantics 3.4 Resolution Calculus and Operational Semantics 3.5 Resume

33 34 38 43 45 51

4

Program Instrumentation 4.1 Types and Typings 4.2 Regular Parametric Types 4.3 Type Relations 4.4 Modes and Data Flow 4.5 Resume

53 54 58 64 80 86

5

Test Coverage 5.1 Anti-Unification 5.2 Coverage and Anti-Unification 5.3 Coverage Computation 5.4 Resume

89 91 97 104 109

XIV

Contents

6 Test Input Generation 6.1 Coverage Driven Generation 6.2 Typed Goals 6.3 Moded Goals 6.4 Resume

Ill Ill 116 137 142

7 Complexity Analysis 7.1 Coverage Computation Complexity 7.2 Coverage Generation Complexity 7.3 Resume

145 145 150 154

8 Summary

155

Appendix A The PROTest System A.l Product Assurance Environment A.2 PROTest System Overview A.3 PROTest Type and Mode Declarations A.4 Structure Checker A.5 Test Case Generator A.6 Test Coverage Analyzer A.7 Test Driver A.8 Test Report Generator

159 159 162 164 167 170 172 173 175

Appendix Β Case Study B.l Program Example B.2 The Robot Task Scheduling Program B.3 Coverage Results B.4 Generated Test Inputs

177 177 180 199 201

Bibliography

213

Index

227

List of Figures 1.1 1.2

The tasks of achieving a given software quality Language levels

1 3

2.1 2.2

Axioms and rules of the Hoare calculus Testing process diagram

11 18

3.1 3.2 3.3 3.4 3.5

An interpretation The structure of an SLD tree An SLD refutation Paths in a graph An SLD tree

38 48 49 49 50

4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 4.11 4.12 4.13

Views of types Empty type algorithm Evaluation of empty αηχτ{α(η)) Type expansion algorithm Type selection algorithm Move in type terms algorithm Type comparison algorithm Evaluation of subsetT(evenlist(n, κ), list(n)) Type intersection algorithm Type intersection algorithm, auxiliary Cross-product algorithm Evaluation of intersection(0(7), β(δ),Τ) Evaluation of intersection(evenlist(K, κ), oddlist(κ, κ))

56 65 66 69 70 71 73 75 77 78 79 80 81

5.1 5.2 5.3 5.4 5.5 5.6 5.7

Unification and anti-unification Anti-unification algorithm Set anti-unification algorithm Anti-unify program Test coverage for logic programs Test coverage determination algorithm Update cover program

92 94 96 98 99 105 105

XVI

List of Figures

5.8 5.9 5.10

Cover initialization program Partial coverage computation program Coverage computation program

107 108 110

6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 6.11 6.12

Depth-first iterative deepening program Generate goal in S+(P) program List flattening program Graph coloring program Strict extension of terms Distinct functor generation algorithm Coverage extension algorithm Coverage extension algorithm, auxiliary Extend distinct functor algorithm Moded goal generation algorithm Moded goal set generation algorithm Variation set generation algorithm

114 115 116 118 124 127 131 132 133 139 140 143

A.l A.2 A.3 A.4 A.5 A.6 A.7 A.8 A.9 A. 10 A.11 A. 12 A. 13

The main window of the product assurance environment with the PROTest and PRORool main windows 160 Textual representation of model parameters in PRORool . . . . 161 Visualizing the results in PRORool 162 The system level architecture of PROTest 163 Program instrumentation for shortest paths 166 Sample graph 167 Sample graph defined by the arc/3 facts 167 Shortest paths in a graph 168 Structure report of PROTest 169 Test predicate selection in PROTest 171 Automatically generated test inputs in PROTest 172 A test program in PROTest 174 A test report in PROTest 176

B.l B.2 B.3 B.4

Output of the robot task scheduling program Overall results for taskschedule.pl Maximum typed coverage results for taskschedule.pl (part 1) . . Maximum typed coverage results for taskschedule.pl (part 2) . .

179 199 199 200

Symbols and Notation Set Theory {zi ι

·

·

·

) ^m}

Set of elements x\, X2, • • •, xm

0

The empty set

{*:/>(*)}

Set of all χ with property P(x)

χ e Μ, Μ 3 χ

χ is an element of Μ

Μ C Ν, Ν

Μ is a subset of Ν

DM

ΜΓ)Ν

Intersection of sets Μ and Ν

ΜΌΝ

Union of sets Μ and Ν

Μ\Ν

Difference of sets Μ and Ν

Μ χ Ν

Cartesian product of sets Μ and Ν

ρ{Μ)

Power-set of M, i.e., set of all subsets of Μ

Mappings f-.M

Ν

f is a mapping from Μ to Ν

f(x),f(M)

Image of χ, Μ under mapping /

χ ι-4 y

χ is mapped to y

r1

Mapping from f(M) to p(M) for a mapping / : Μ —> Ν· f~l(y) '•= {x Ε Μ : f(x) — y}. If / is injective, then / - 1 ( / ( M ) ) is identified with Μ

XVIII

Symbols and Notation

Basic Sets IN

The set of natural numbers, {0,1,2,...}

IR

The set of real numbers, sometimes with superscript + denoting the non negative real numbers

η

Set of predicate symbols

τ

Set of function symbols

ν

Set of variable symbols

Τ

Set type function symbols

w

Set of type variable symbols First-order language, set of formulae

C(F, Τ, V, W)

Type language, set of type terms Set of terms

ν)

Set of ground terms Set of pure type terms Set of ground pure type terms {X\/t\,...,

G(M)

Xm/tm}

Substitution. The distinct variables X\,..., Xm are simultaneously replaced by terms t\,... ,tm. tl is different from X{. Identified with a partial mapping from the set of variables to the set of terms Set of ground instances of set Μ of terms or clauses or formulae

Logic A AB

Logical conjunction of A and Β

Α\ί Β

Logical disjunction of A and Β

-..A

Logical negation of A

XIX

Symbols and Notation

Vx F

Universal quantification of χ for F Existential quantification of χ for F

F Vx F

Universal quantification of all free variables in F

3x F

Existential quantification of all free variables in F

ta

The term resulting from the application of substitution σ to term t

t ~ s

Terms t and s are equivalent, i.e., renamings, variants



Empty clause

Μ \= F

Formula F is a logical consequence of formula set Μ

Μ h C

Clause C is derivable from clause set Μ

t Ε s,s • f

Term t is an instance of term s, term s is a generalization of term t

_L

Least element of the set of terms

lca(M)

Least common anti-instance of a set Μ of terms

length(i)

Length of term t, number of symbols occurring in t

Types and Modes {vi/τι,

• • • ·>νη/τη}

Type replacing. Analog to substitution for terms

type(p/n)

Type declaration for predicate p/n

mode(p/n)

Mode declaration for predicate p/n

φ(ι>ι,...,

vn) —> Φ

Parametric type rule, type definition with parameters (type variables) ι/χ,..., v n , Φ is a set { τ χ , . . . , r n } of pure type terms

e

Type symbol denoting the empty type

η

Type symbol denoting the Herbrand universe Ή,

Ρτ,τ

Program associated with ground pure type term τ defined by set Τ of type definitions

XX

Symbols and Notation

[r] T

Type semantics. The subset of the Herbrand universe represented by a ground pure type term τ via set Τ of type definitions

[r(i)] r

Restriction of [r] T to t

[r(f)] T (s)

Corresponding type of s for t with respect to τ

r[t](s)

Type rule defining [r(t)] r (s)

minlengthT(i)

Minimum length of a well-typed ground instance for t with respect to ground pure type term τ

cvg r (C)

Strictly increasing coverage sequence for clause C with respect to type declaration r

cov T (C)

Strictly increasing cover sequence for clause C with respect to type declaration τ

Logic Programming Η 0} Ρ {A = (Q • Β) + R A R < B}. The above example demonstrates that formal verification is a rigor mathematical framework giving a precise meaning of correctness of programs. But its practical application is limited to small programs [123], even its completeness with respect to correctness is given in general.

2.3. Basic Testing Approaches

2.3

13

Basic Testing Approaches

Since the mid-seventies many approaches have been proposed to overcome the deficiency of incompleteness of program testing. Important approaches (cf. [60, 19]) which influenced many research work in this field describe the specificity of program testing as providing a continuous graduation of correctness assessments instead of the correct/incorrect alternatives of proving. Testing appears as a particular case of program, proving. In this context, a precise formulation of the test problem can be given: •





The test case problem is the determination of test cases associated with well-defined program properties. A successful program execution using these test cases should imply the correctness of the program with respect to their associated program property. The set of test cases must further be optimized such that the correctness with respect to all considered program properties implies the correctness of the whole program. The test language problem is the construction of appropriate language constructs for test execution (test program). These language constructs should be embedded in the implementation language, achieving a uniform development system. The test termination problem is the examination of invariants in the test program and their sensibility with respect to program modifications subject to test completeness.

The generation and selection of test cases (test case problem) is the basis of testing. Because of the complex properties in practical programs, test case generation and selection can be time consuming and error-prone. For this reason a reduction of the number of test cases, i.e., by factoring them into equivalence classes related to error types (cf. [53]), is necessary. The test language problem leads to redundancy in the implementation. It should be attempted to minimize this redundancy in order to make the overhead for the programmer as small as possible. The added redundant language constructs must also be such that the programmer can develop an intuition of their meaning. The test language should enable test programs which do not depend on the program under test. This avoids the recoding of the test procedures every time the program to be tested is revised. Instead, the test language should should enable test programs dependent on the specification and, in addition, should lead to generation and selection of test cases which depend on the specification. The test termination problem is usually handled by reliability concepts which supports the decision whether a testing process should be terminated on the basis of a priori given software reliability. Common methods for

14

Chapter 2. Program Testing Concepts

determining software reliability are described in e.g., [59, 86, 108, 109, 124, 126, 148]. These concepts will not be considered in this work. Many different testing methodologies have been proposed [41, 35], of which the formal ones include the test theory of [60], the mutation testing of [27], general mathematical testing theory of [62], the asymptotic testing theory of [19], and recursion theoretic approaches [30]. Algorithms for gene-

ration and selection of test cases and test procedures are proposed using these theories which lead to results similar or equivalent to correctness proofs. Some of the most widely accepted semi-formal testing concepts are • •

fault-based testing [55, 147, 76], functional testing [74, 75, 130],



coverage testing [112, 120] and further developments by [118, 95, 15],



domain testing



partition testing [140, 51, 136], which extensively has been analyzed [65, 56, 87, 139],



symbolic

testing

[141, 32],

[89, 90, 73].

Comparison and detailed analysis of several testing methods have also been published [52, 64, 138]. Besides these formal and semi-formal testing approaches, an axiomatic approach for testing has been proposed by Baggi and Shooman (cf. [11]). These are all subject to testing of imperative programs. The formal approaches of Goodenough and Gerhard [60] and Bouge [19] will be considered in more detail in the next section.

2.4

Formal Testing

Program testing is often introduced in an informal way. It is the intend of this work to introduce a formal program testing framework. Thus, the first thing to be done is a precise development of the vocabulary and terminology to be used in the description of program testing. A program is a finite sequence of statements.

A statement is any elemen-

tary expression of a programming language. In imperative programming, a statement can be an assignment, e.g., χ := χ + 1]

or a control structure, e.g., while χ < y do χ :— ζ 2 ; end. In logic programming, a statement can be a clause ρ(Τ1, T2) om(P) and range 7lan(P) and let S be the specification for P. The correctness relation okpts on Vom(P) is defined as χ G okptS if and only if P(x) = S(x). A test input set Τ C Vom(P) is a ideal test for Ρ if and only if Vi € Τ okpom(P) of test inputs. The criterion C determines the properties of a program to be exercised in order to thorough test the program. A test Τ is thorough for Ρ if a successful execution of Τ implies that there are no faults in P . A relation completePS on the set of test input sets, i.e., p(Vom(P)), and the set of data selection criteria is defined which states that Τ satisfies the above mentioned program properties. Hence completePS(T,C) defines how a test data selection criterion C is used in selecting a set Τ of test inputs. Formally, let Τ C T>om(P), then completePtS{T, C) := Vc € C 3t 6 Τ c(t) Λ Vi € Τ 3c Ε C c{t). Of course, a test data selection criterion C must be such that test input sets Τ satisfying completePS(T, C) lead to consistent and meaningful results. In [60] these properties are called reliable and valid. A test data selection criterion C is reliable if and only if every test input set Τ C Vom{P) satisfying completeP S(T, C) is successful for Ρ or every test input set Τ satisfying completePS(T,C) is not successful for P . This means if C is reliable, then the test input sets selected by C are consistent in the ability to reveal faults. A consequence is that only one complete set of test inputs selected by a reliable C must be executed. All other complete sets of test inputs selected by C have the same properties of revealing faults. A test data selection criterion C is valid if and only if for every fault in a program Ρ there exists a set Τ of test inputs satisfying completePS(T,C) which reveals the fault. Validity does not guarantee that every complete test input set reveals every fault in a program, it only guarantees that it is possible to select test inputs that reveal a fault. Formally, the test data selection properties valid and reliable are given, using the notation successful(T) := Vre € Τ okpts(x)·

17

2.4. Formal Testing

Let Ρ be a program, S be its specification, and C be a test data selection criterion. reliable pts(C) := VTi,T2 C Vom(P) completePtS(TuC) Λ completeP>S(T2,C) ->· (successfulPS(T\) ο successfulPS(T2)), valid pts(C) := Vre e Vom(P) ^okP,s(x) 3T C Vom(P) (completePS(T,C)

Λ ->successfulPS(T)).

These definitions have a crucial implication. Consider a program Ρ and its specification S. Assume that there is a reliable and valid test data selection criterion C and a complete test input set T, i.e., Τ satisfies completePS(T, C), such that Τ is successful for Ρ and S. Now assume that there exists a χ G Vom(P) such that Ρ is not correct on x, i.e., ->okPis(x)· Then validPis(C) implies that there exists a set Τ of test inputs satisfying completePS(T,C) such that -isuccessfulPS(T). The property that C is reliable implies that if for one complete test input set T0, ^successfulΡ3(Τϋ), then for every test input set T, ->successful PS (T). But this contradicts the initial assumption that there exists a successful complete test input set. The above stated implication is essentially the fundamental testing theorem of [60] which is formally stated as Theorem 2.4.1 (Testing) Let Ρ be a program and S be a specification for P. Then ΊΤ C Vom(P) 3C (reliableP>S(C) Λ validP,s(C)A completePS(T,C) A successfulPS(T)) —>• Va; £ Vom(P)

okP,s(x)·

In other words: every complete test input set selected by a reliable and valid test data selection criterion is capable of detecting every fault in a program. The notions of reliability and validity of a test data selection criterion make the relation between program testing and correctness proofs clear. If on one hand the test data selection C is defined such that the only complete test input set is the whole input domain, i.e., completePS(T, C) —> Τ = T>om(P), then C obviously is reliable and valid. Of course exhaustive testing will reveal every fault in a program. On the other hand, if the test data selection criterion is defined such that no input d G Vom(P) satisfies C, then C obviously is reliable. The proof of the validity of C then means the correctness proof of the program, since no testing will be done. The fundamental testing theorem provides the formal notion for a rigor testing theory. In the following section, this will be used as a basis for a more detailed treatment of program testing.

18

Chapter 2. Program Testing Concepts

concrete level

abstract level first-order logic

implementation specification

test Τ

representation

application

initial testing context C0

construction

conclusion

series of tests Τ

Figure 2.2: Testing process diagram according to [19]

2.5

Asymptotic Testing

The asymptotic testing approach, first introduced by Bouge [19], explicitly uses first-order predicate logic as its underlying mathematical tool. It extends the somehow rigid formal approach presented in the previous section by introducing levels of power. Reliability of a test data selection criterion means that all the sets of test inputs selected by a reliable criterion are equivalent with respect to the correctness property of a program to be examined. Instead of a flat reliability criterion levels of power are introduced, leading to projective reliability. Reliability expresses in this case that a test input set Ti which has a higher level of power than some test data set T0 is better with respect to correctness assessment. Validity of a test data selection criterion C means that for each fault in a program to be examined, a set of test inputs can be selected by C which reveals the fault. Now chains are considered. Validity is ordered such that a limit exists which is more powerful than any test input set but can be approximated as closely as required at the cost of testing effort, leading to asymptotic validity. This essentially is the main concept of the asymptotic approach. The associated testing process with the above sketched concept is shown in Figure 2.2 (cf. [19]). Starting with the program, i.e. the implementation, and the specification, in a representation phase an abstract problem is constructed which is equivalent to the concrete problem. This abstract problem, formulated in first-order predicate logic, defines an initial testing context Co- From this testing context an acceptable series of test data is constructed, which involves, as in the formal approach, the search for some ideal criterion. In contrast to the approach from [60], some knowledge about the implementation is also considered. This knowledge is expressed by hypotheses.

19

2.5. Asymptotic Testing

Construction of an acceptable test may not be possible for the initial testing context Co, because the knowledge about the implementation is to weak for a precise test. Now some new hypothesis about the implementation is postulated, restricting the initial testing context to a new testing context C\. Eventually for this testing context, an acceptable series of test data can be constructed. Otherwise another hypothesis is postulated, restricting the testing context C\ further. Postulating new hypotheses must meet the requirement of being consistent with the property being tested, the restriction of the testing context must be conservative. The important feature of the construction of serieses of tests is that it is entirely dependent on first-order logical objects. Postulating hypotheses about the program influences the testing effort. According to the requirements and restrictions on testing effort and successful construction of a series of tests T, some set of test inputs can be picked from Τ and be applied to the given problem, leading to the conclusion. The asymptotic testing approach is now presented in more detail. The mathematical framework is not stretched to its deep end in order to keep the text readable. For the the treatment of asymptotic testing, only the necessary notations are introduced, leaving the precise mathematical meaning to a certain extent to intuition. Some of the following notions and notations will be recalled in the next chapter, elaborating their rigid mathematical meaning. A first-order language will be denoted by £ , and for a set M, the language obtained by adding the members of Μ as constants to £ is denoted by C(M). The extension of languages is denoted by C. Of course, if C\ C C 2 then Ci(M) C C2(M). Logical validity is denoted by f= and provability within the considered first-order language is denoted by Κ A family of elements χ of some set is denoted by (χ), sometimes subscripted with an index set I , (xi)ieiGiven a language £ , the association of a meaning to the symbols of C is referred by structures S. A set of formulae of C is called a theory on £ , denoted by T. A theory is also called a set of axioms. The union of theories is denoted by U. With these notations, it is |= Ti U T2 if and only if (= Tj and

T2.

A theory Τ is called finite if Τ consists of a finite number of axioms. If all the axioms of the theory T2 can be proved using those of the theory Ti, this is denoted by 7\ l· T2. Again, it is Τ l· (7\ U T2) if and only if Τ h 7\ and Τ h T2. Programs to be tested will be seen from their functional behavior, hence an abstract data type model can be assumed naturally. For a program P ,

20

Chapter 2. Program Testing Concepts

its domain Vom(P) is taken as a universe U. A language C containing at least the functional symbols of Ρ and a symbol representing the functional behavior of the program property to be tested is chosen. The model of the program will then be a £(£/)-structure S. Because programs are seen from their functional behavior, not only one implementation must be considered, instead a family (5) of £(f/)-structures is considered. This reflects that no complete knowledge of the program to be tested is available. The specification to be tested against with is modeled by a set A of first-order formulae. An implementation is correct with respect to its specification if and only if the actual structure C(U) validates the formulae of A. A testing context C is a tuple (£, U, (S), A) where £ is a first-order language, U is some set, (5) is a family of £(i/)-structures, and A is an £(£/)theory. As an example, consider a program Ρ which computes the square root, i.e., outputs \ f x for any non-negative natural number χ given as input. Then a reasonable testing context C = (£, U, (S), A) for Ρ would be • • • •

£ — {F, ·2, · < · , · < · , · + ·}, where F represents the functional behavior of P , U = 1N+, (S) is a family of £(£/)-structures where the symbols of £ being different from F are assigned their standard meanings and F is computable, A = {\/x (F(x)2 L of L is denoted by i.e., if L = A with an atomic formula A then — -~A then ~ L = A. The resolution calculus has no axioms and its only rule (scheme) is {Lu ..., Lm} U Cx {Lm+1,..., (CiUC 2 )/i

}uc2

where {L\,..., Lm}UCi and {Lm+1,..., Lm+n}UG2 do not contain common variables and μ is a most general unifier of { L i , . . . , Lm, ~ L m + 1 , . . . , ~L m +„} (m > l , n > 1). The clause (Gi U is called a resolvent of the clauses { L i , . . . , Lm} U C\ and {Lm+i,..., Lm+n} U C2 via substitution μ. The requirement that the two premises in the resolution rule must not contain common variables is no restriction in practice because the clauses can always be renamed using a variable renaming, i.e., using variants of the clauses instead of the clauses themselves. The resolution calculus for clauses is sound, i.e, if for a clause C and a set Μ of clauses Μ l· C then Μ (= C. The resolution calculus for clauses is also complete as a refutation calculus, i.e., if Μ l· • then Μ does not possess a model. Remember that Horn clauses possess at most one positive literal. Hence the resolution calculus applied to Horn clauses has the property that when building a resolvent from a goal clause and a definite clause, the resolvent is always a goal clause. Remember also that if Ρ is a program and and G is a goal, then Ρ U G} |= • if and only if Ρ |= G. Using the resolution calculus, a refutation of Ρ U {«— G} can be tried in order to compute an answer. Start with a goal clause G and choose some appropriate variant Go of a program clause in order to build a resolvent Gi/ii of G and Go if such a Go exists. Then proceed in the same way with the goal Gi/zi. There are three possibilities for the sequence produced: 1. The end is the empty clause • . Then, with Go := G, the sequence {(Go, 0), (Gi, μι), · · ·, (G n _i,/z„_i), ( • , μη)), where each (Gi, μ*) pair is an immediate derivand of goal G{_ 1, i = 1 i.e., there exists some literal L 6 Gj_ 1 and a (suitable variant

3.4. Resolution Calculus and Operational Semantics

47

of) a program clause C = (H Β) £ Ρ such that the mgu of L and Η is ßi and Gi — ((Gj-i \ {£}) U Β)μι. The pair {G^ßi) is called a derivation step and η is the length of the derivation. In this case (G n — • ) the derivation is called successful. For a substitution σ let σ| G be the restriction of σ to the variables occurring in G, i.e., σ\α := { X/t: X/t € σ and X occurs in G }. The substitution ( μ ι . . . μη)\α is called a computed answer for Ρ U {Sß

ifT = f { r l , . . . , T if Τ = φ(τΧ, i f r = ߀ ΤΒ·

n

)JeF

by

n

,

...,Τη),φβΤη,

Definitions 4.2.7 and 4.2.9 imply that for a v?(ri> · · · > r n) € C{J·, T) Μ τ ι > . . . Ι τ η ) ] Γ = (J Mt» υ&Φσ

where φ ( ΐ Ί , . . . , ν η ) Φ € Γ, σ = {ι/χ / η , . . . ,i/„/r„}, and r i , . . . , r n are defined in Τ. Bodies of type rules represent unions of types, hence this type scheme allows additive polymorphism. Type semantics is defined in sensible way. It handles circular type definitions correctly. Consider the type rule a { f ( a ) } which generates infinite terms. The associated program PQT would be a(f(X)) {a, b}. evenlist(v\, v2) —> {[], [v\\oddlist(vi, f2)]}· oddlist{uχ, v2) —> {[u2\evenlist{v\, 1/2)]}· Then \evenlist{ct, β)\τ = {[], [l,a], [2,a], [l,b], [2,b], [l,a,l,a], [l,a,2,a], [l,a,l,b], [l,a,2,b],...}. [evenlist(a, /?)]T consists of all lists of the form [ίχ,..., ί2»ι], such that i2t € {1,2} and i 2 i-1 £ {a, b}, i = 1 , . . . , n. A type btree(v) representing binary trees of type ν is defined as follows: Let Τ be α

{0, 1}.

btree(u) —> {nil, t{btree(v), v, btree(u))}. Then [.btree(a)]T = {nil, t(nil,0,nil), t(nil,l,nil), t(t(nil,0,nil),0,nil), . . . }. Δ Parametric or polymorphic types [111] can be considered regular in the sense that they are equivalent to regular types which will be defined as above without having type variables and only having type symbols [37]. But parametric types are a notational convenience. They allow for the definition of a generic a type btree(v), and if there is a need, define types btree(integer), btree(float), etc. Concluding this section, the following, concerning variables and type variables in types logic programs should be mentioned. • • • • • •

Variables will be substituted by terms, type variables will be substituted by pure type terms, variables occur only in predicate definitions, type variables occur only in type definitions, type functors occur only in type definitions and type declarations, neither variables nor type variables occur in type declarations.

64

Chapter 4. Program Instrumentation

4.3

Type Relations

For analyzing typed programs, it is necessary to determine certain properties of types. In the previous section, a language for describing types, notably regular parametric types, has been established. Types are represented by ground pure type terms. This section deals with basic properties of regular parametric types. First, emptyness of types is investigated and then subset properties are described. Algorithms for determining if a r G C{T, T) represents the empty type, if for τχ,τ2 G C(T,T), the corresponding types are related by subset, i.e.,[ri] T C [ r 2 ] r , are developed. The algorithm for determining the subset relation uses several properties of types which are represented by ground pure type terms. These properties will also be investigated.

Type Emptyness It is evident from Definition 4.2.9 that for determining whether the type represented by a r = φ{τ\,... , τ„) where ψ G Τ , is empty, the right hand side of the underlying type rule φ{ν\,..., v n ) —• Φ must be investigated. In this case all types represented by the type terms in Φ must be empty. This is the case when (recursively) no type terms can be found which have no type functors. For a τ = / ( r i , . . . , τ η ) where / G Τ, [ r ] r is empty if for one of the r t , [r t ] T is empty. This is stated formally by the following Proposition (cf. [38]).

Proposition 4.3.1 Let τ be a ground pure type term defined by a set Τ of type definitions. Then [rj T = 0 if and only if either of the following holds. 1. τ = φ(τι,..., τη) 6 C(F,T) where φ{ι>χ,... ,νη) —> Φ € Τ and σ = {vi/n,..., f n / r n } , such that Vv € Φσ [υ] Γ = 0, or 2. τ = /(η,..., τη) G C{T, Τ) with f Ε Τ and 3i G { 1 , . . . , η } [τ { ] τ = 0. Proof: 1. If τ is a type structure, φ(τι,..., τ η ) G C(J-, Τ) and φ{ν\,..., Τ, then [ τ ] τ = υυεΦ ^n)

if and only if Vi € [ n ] r [ ( r 2 > . . . , r n ) l r C { ( t 2 , . . . , tn) : f(t, t2,...,

tn) € 5 } .

Since all subset relations are established through the sequence and the original inequality was found, it is established that [ a ( 0 J r ^ IßMJr· following, this approach to type comparison will be developed formally. Definition 4 . 3 . 3 A sequence of ground pure type terms is denoted by ( τ χ , . . . , τη), where η € IN. The concatenation of two sequences will be denoted as follows. ,p m ) : = (τχ,. . . ,Τ η ,Ρχ, . .

(n, • • ·,τη)^(ρι,... The concatenation

of sequences φι,...

.,pm).

,tpk is

Σ ψί : = ψι^· • · · ^ipk. ie{l,. ..,*:} Let Τ be a set of type definitions type terms. [(ΐ"ι,···,Τη)1 τ m

T

:= ··=

and Φ be a set of sequences

of ground pure

{ < ί | , . . . , ί η ) 'ti e [ri] r , i = Ι , , . , , η } , U

Φ

Mr·

For a sequence φ = ( τ χ , . . . , τη), the head τχ of φ is denoted by car(φ) and the tail ( r 2 , . . . , r n ) of φ is denoted by cdr(φ). For a set of sequences Ψ, the set of its heads, respectively its tails are denoted by cars(Ψ) := { car(φ) : φ G Φ }, cdrs( Φ) : = { cdr (φ) : φ G Φ }.

68

Chapter 4. Program Instrumentation

Concatenation is well-defined because finite sequences of ground pure type terms are considered, i.e., ( τ ι , . . . , τη) is a tuple. The three steps of type comparison mentioned above can be stated by the following Proposition (cf. [38] in which a simpler version for non-parametric types has been presented). Proposition 4.3.4 Let (τχ,... , r n ) be a sequence where Tj G C(T,T),

i =

1,..., n. Let Φ be a set of n-ary sequences of ground pure type terms where each ψ G cars(Φ) is not a type structure. Then [ ( τ ι , . . . , τ η ) ] τ C [ Φ ] τ if and only if one of the following holds. 1. τι is a type structure

φ(αι,...,

α η ) , φ{ν\,...,

νη) —V Φ G Τ, σ — { ι>ijoLi :

% = 1 , . . . , η } and Υυ G Φσ [(υ, τ 2 , . . . , τ η )] τ C [Φ] Γ , 2. Τ\ is a ground pure type term / ( α χ , . . . , am)

with f G Tn

and

[(n, · • •, τ η )] τ C [{ (ρχ,..., pn) e Φ : pi has functor fjm }J r , 3. τι is a ground pure type term

/ ( α χ , . . . , a m ) with f

G Tn,

for

each

sequence (pu ..., pn) G Φ, pi = / ( ω χ , . . . ,um), and l(oti,...,am,T2,...,Tn)]T C [{ (uu...,um,p2,...,pn)

: {f(ui,...,Um),p2,...,pn)

Proof : Follows from set theory and Definition 4.2.9.

€ Φ }]Γ·



Proposition 4.3.4 shows two things which must be considered with type comparison. Part 1 is the observation that when comparing a type term τ G C(!F, T) with a set of ground pure type terms, the type terms in the right hand side of the type definition for r must be examined. The term τ must be expanded. In parts 2 and 3 the first type term in each sequence in Φ must be expanded and then the subset of Φ whose sequences start with terms having functor / f m must be selected. Proposition 4.3.4 provides entirely the concept for developing an algorithm for type comparison. This algorithm is divided in several parts. One for expanding a single sequence and one for expanding a set of sequences. Of course expanding a set of sequences is simply the union of its expanded sequences. Figure 4.4 shows these algorithms. The functions defined in this algorithm depend on a set Τ of type definitions, hence they are subscribed with T. Expanding ground pure type terms according to a set of type definitions must not change the type semantics. The following lemma states that type semantics is preserved by type expansion. L e m m a 4 . 3 . 5 Let ψ be a sequence, Φ a set of sequences, and Τ a set of type definitions. Then [ip]T = [expandT(ip)]T and [ Φ ] τ = [expandsetT(Φ)]τ.

69

4.3. Type Relations

function expandT{ip)'· Σ; input: φ: sequence of ground pure type terms output: Σ: set of sequences of ground pure type terms begin if car(^) = φ(τ\,..., r„) and φ{νι,..., un) —>• Φ € Τ then begin (φ is a type function) σ := {ui/n : i = 1 , . . . , η }; Λ := { expandT(r) : τ € Φσ }; ?.·={ΥιΤ>^τ·~αΐΓ{φ)·.ρΙ else Σ := {φ} (φ is not a type function) end end; function expandsetr(Ψ): Σ; input: Ψ: set of sequences of ground pure type terms output: Σ: set of sequences of ground pure type terms begin Σ := Uv>e* exραηά τ (φ) end; Figure 4.4: Type expansion algorithm

Proof: First consider expandr, let ψ — (φι,..., φ™). If Vi is n °t a type structure, the result follows immediately. If φι is a type function ψ(τ\,..., r n ), with φ{ν!,..., vn) —> Φ € Τ, and σ = {i/f/τ* : i = 1 , . . . , η } then [βχραηάτ(φ)]τ

= |J [(ν)€Φσ

Since \φ{τ\,... ,τ η )] Γ = υυεΦσ M r For expandsetx, [Ψ]τ = U^e* ΪΨΐτ result follows.

^m)]T-

Definition 4.2.9), the result follows. Mr = [εχραηάτ(φ)]τ, hence the •

Example 4.3.6 Let κ be a type symbol denoting the base type constant, Τ the set of type definitions { a —» {void, f(a, /?,)}}, and Φ the set of sequences { { η , «), (a, g(α, α)), (κ, a), (f(/c, a), a)}. Then expandsetT( Ψ) = {( r i>«). ( v o i d . g(a> α))> ( f ( a > V), g(a, α)), (κ, α), (ί(κ, a), a)}. Δ

70

Chapter 4. Program Instrumentation

function selectset(r, Φ): Σ; input: r: ground pure type term Φ: set of sequences of ground pure type terms output: Σ: set of sequences of ground pure type terms begin if τ = η then Σ : = { φ e Φ : car (ψ) = 7/}; if τ is a base type symbol then Σ : = { ψ € Φ : car(V>) = η or car(ip) = r }; if r is a constant c then Σ : = { ψ G Φ : οατ{φ) = η or car(ip) = τ or οατ{ψ) = base type β and c G β } ; i f r = / ( n , . . . , r n ) then Σ : = { ψ ξ Φ : car(V') = η or car (φ) has functor / / n } ; end; Figure 4.5: Type selection algorithm

The selection of subsets of Φ in parts 2 and 3 of Proposition 4.3.4 is based on the top-level functor of the first type term in each sequence. An algorithm for selection thus takes a ground pure type term and an expanded set of sequences of ground pure type terms as input and outputs a set of ground pure type terms. The details are depicted in Figure 4.5. Selection is sound with respect to type inclusion, due to the following lemma. L e m m a 4.3.7 Let Τ be a set of type definitions. For a sequence φ of ground pure type terms and a set Φ of sequences of ground pure type terms [Vir — [Ψ]Γ if and only if {ψ]τ C \selectset(car($), expandsetT(^))}T. Proof:

Because of Lemma 4.3.5 \selectset(car{%l>), expandsetT(Φ))]τ

= selectset(car(tjj),

[Φ] Γ ).

By part 2 of Proposition 4.3.4 the result follows.



Example 4.3.8 Consider the type definition Τ = {α -4 {void,f(a;, τ?,)}} and set Φ {(τϊ, κ), (a, g(a, α)), (κ, a), (f(tc, a), a)} of sequences in Example 4.3.6. Then selectset(void, expandsetT(Φ))

= {(void, g(a, α)), (κ, a ) }

and selectset(f(b,d),

expandsetT{^l))

= {(f(a, η), g(a, a)), (f(/c, a), a)} Δ

71

4.3. Type Relations

function movein(r,ip)·. σ; input: r: ground pure type term ψ: sequence of ground pure type terms output: σ: sequence of ground pure type terms begin if r is a constant or a base type symbol or η then σ := cdr(ip); if τ = / ( τ ι , . . . , τ η ) and car(V>) = η then ν ' η—times

if r = / ( τ ι , . . . , τη) and car (ψ) = /(pi, ...,pn) σ := {pi,..., pn)^cdr(^); end;

then

function moveinset(r, Φ): Σ; input: τ: ground pure type term Φ: set of sequences of ground pure type terms output: Σ: set of sequences of ground pure type terms begin Σ : = { movein(r, φ) : φ G Φ } end; Figure 4.6: Move in type terms algorithm

In part 3 of Proposition 4.3.4, the first type term of each sequence in Φ has the functor f/n. So the function symbol can be discarded, and the type term can be replaced by a sequence of its arguments. This is a move in the type term. There is one special case with moving in η. The subset of [77]T having functor f/n is of course 1/(77,..., η)}τ, hence η must be replaced by (η,...,η). The algorithm shown in Figure 4.6 for moving in type terms takes a ground pure type term and an expanded selected sequence of ground pure type terms as input and outputs a sequence of ground pure type terms. There is also a need for a version for moving in sets of sequences. This is simply the set of its moved in sequences. L e m m a 4.3.9 Let ψ be a sequence of ground pure type terms, Φ a set of sequences of ground pure type terms, and Τ a set of type definitions. Then ΙΨίτ — M r if and

ty tf

on

lmovein(car(ip),

ψ)]r

C \moveinset(car(ij)),

selectset(car(ip),

expandsetT(Φ)))]Γ.

72

Chapter 4. Program Instrumentation

Proof : By Lemma 4.3.7, it is [ψ]τ C [ ψ ] τ if and only if [φ]τ C lselectset(car(ij))i

expandsetT(Φ))]τ,

and by part 3 of Proposition 4.3.4 the result follows.



Example 4.3.10 Consider again the type definition T = {a-»{void,f(a,77,)}} and set Φ {(ti,/c), {a, g(a, α)), (/c, a), (£(«, a), a)} of sequences in Example 4.3.6. Then moveinset(void, selectset(void, expandsetT(Ψ)))

= {{g(o;, a)), (a)}

and moveinset(f(b,d),

selectset(f(b,d),expandsetT(ty))) Δ

Proposition 4.3.4 has pointed out a way for constructing an algorithm for type comparison. With the type expansion algorithm (cf. Figure 4.4), the type selection algorithm (cf. Figure 4.5), and the move in type terms algorithm (cf. Figure 4.6), all parts for constructing the entire algorithm for type comparison are available. The algorithm is shown in Figure 4.7 and consists of two functions. The first is the top-level subsetT function which takes two ground pure type terms as input and outputs a boolean value. The output is true if the first argument represents a type being a subset of the type represented by the second argument and false otherwise. The second function is the subsetseqT function which is called from the subsetT function. The input to this function is an n-ary sequence of ground pure type terms, a set of n-ary sequences of ground pure type terms, and a set of tuples (a, Φ), where α is a type structure and Φ is a set of already compared ground pure type terms. The output is a boolean value. The type comparison algorithm in Figure 4.7 represents the formal treatment of the approach sketched in the beginning of this section on page 66. For illustration, consider a more elaborate example. Example 4.3.11 Let κ denote the base type atom, i.e., the set of non numeric constants, and consider the set Τ of type definitions list{u) -4 {[], [u\list(v)]}. evenlist(vi, u2) —» {[], [v>i\oddlist(i>i, ^2)]}· oddlist{vχ, ν2) —> {[i/2\evenlist(ui, i^)]}·

73

4.3. Type Relations

f u n c t i o n suöseir(ri,r 2 ): 6; input: Τχ, r 2 : ground pure type terms output: b: boolean value begin if Tj = e then b := true·, if τ 2 = e then 6 : = /a/se; if η ^ e and τ-χφ t then b := subsetseqT{(T\), end;

{ ( r 2 ) } , 0)

f u n c t i o n sui>se£segT(V>, Φ, A): 6; input: sequence of ground pure type terms Ψ: set of sequences of ground pure type terms A: set of tuples (α, Φ), where a is a ground pure type term and Φ is a set of ground pure type terms output: b: boolean value begin if Φ = 0 then b := false; if ψ — () then b :— true; if (car (φ), Φ) € A and cars (Φ) C Φ then b := subsetseqT(cdr(ip), cdrsfä), A); if car(tp) is a type structure . . . , r „ ) with y? € Tn then begin Σ : = expandT(·φ)\ b := Awes subsetseqT(uj, Φ, A U {(car(ip), cars(\I>))}); end; else ( τ is a base type symbol, η, or /(τχ,..., r n ) with f G J-n) begin r := car (ψ) χ := moveinset(r, selectset(r, expandsetT(Φ))); 6 := subsetseqT(movein(r, φ), x, A) end end; Figure 4.7: Type comparison algorithm

74

Chapter 4. Program Instrumentation

It is clear by inspection, that [evenlist(K,

k)]t

C [fo£(/c)] T

and levenlist(n,

K)]t

% loddlist(n,

κ)]τ.

These two examples will now be demonstrated by simulation of the Type comparison algorithm. The evaluation of subsetT(evenlist(K,

κ),

list(K)),

depicted in Figure 4.8, comes up with the result true. Thus, the algorithm computes the result as desired. The second example, where subsetT(Ti,T2) should b e false is T\ — evenlist(n,

κ),

subsetT(evenlist(K,

= oddlist(K, κ), oddlist(n,

κ). T h e e v a l u a t i o n of /c))

involves the following. subsetseqT((evenlist(K, κ)), {(oddlist(K, κ ) ) } , 0) expandT({evenlist(n, κ))) = {([]), ([K\oddlist(n, /c)])} subsetseqT((\\), {(oddlist(n, κ))}, {(evenlist(K, κ), {oddlist(n, expandsetT({(oddlist(K, «))}) = {([K\evenlist(n,«)])} selectset(\\, {([K,\evenlist(n, κ)])}) = 0 moveinset(\\, 0) = 0

«)})})

movem([],{[])) = () subsetseqT((),

0, {(etienlist(K, κ), {oddlist(K,

κ)})}) =

false

Notice that the right hand side of the statement b := ΛωεΣ subsetseqT(ω, Φ, Λ U {(car(ip), cars^))}); in the type comparison algorithm (cf. Figure 4.7) is evaluated only until one subsetseqT(uJ, Ψ, Λ U {(car(^), οαΓί(Φ))}) is false. Again, the result is as desired. Δ Type Intersection There is often more than one occurrence of a variable in a clause or even in a literal of a clause. Since any argument of a predicate has a type associated with it, in this case it must be checked if the intersection of two types is empty or not. It also is desirable, that in case the intersection is not empty, a set of type rules can be determined, defining this intersection. Such an intersection type can then be used to generate appropriate ground terms for

4.3. Type Relations

subsetseqT

75

((oddlist

(κ, κ)),

expandT((oddlist(K,

{(list

κ,)))

=

( κ ) ) } , 0)

{([K,\evenlist(K,

subsetseqT(([K\evenlist(K,«)]),

{(&£(«))},

expandsetT({(list(n))})

{(oddlist(K,

— {([]),

selectset([n\evenlist(K,

([/c|foi(/c)])})

movein([K\evenlist(K,«)],

([K\evenlist(n,

subsetseqT((K,

κ)),

κ), {(/c,

moveinset(K, movein(K,

{(/c,

=

{(κ,

κ)]))

=

list(K,))}

(κ, evenlist(K,

κ))

foi(/c))},

{foi(/c)})})

expandsetT({(n, selectset(K,

{Esi(/c)})})

=

κ)}, {([K\list(K)])})

evenlist(K,

κ),

(W^Ct)])}

κ)], {([]),

moveinset([n\evenlist(K,

{(oddlist(K,

/c)])}

list(K))})

— {(κ,

list(n))})

=

{(«,

{(κ,

foi(/c))})

(«, evenlist(n,

κ)),

expandT((evenlist(K,

{(list(K))} =

(evenlist(n,

κ))

{(/zsi(/c))}, {(oddlist(n, κ),

κ)))

subsetseqT((\\),

list(K))}

= κ)))

subsetseqT((evenlist(n,

list(K))}

=

{{[]),

([K\oddlist(n,

{list(K)})})

«)])}

{(list(K))},

{(evenlist(K,

κ), {list(K)}),

expandsetT({(list(K))}) selectset([],

{{[]),

(oddlist(κ,

κ),

{Zisi(«)})})

— {([]), ([«|iisi(«;)])}

})

=

{}

moueinsef([], { ( [ ] ) } ) = { ( ) } movein([],

{[]))

=

subsetseqT((),

()

{()},

{list(K,)}),

{(evenlist(n,

(oddlist(K,

subsetseqT(([n\oddlist(K, {(evenlist(K,

κ)]),

expandsetT({(list(K))})

=

subsetseqT((K,

{(«,

moveinset(K,

{(κ,

=

([κ\oddlist

(κ, κ)]))

κ)),

list(K,))},

{(κ,

list(K))})

=

list(n))})



list(K,))})

(κ, oddlist(n,,

subsetseqT((oddlist(K, {(evenlist(K, subsetseqT((),

(&ί(κ)})})

([/c|fof(/c)])}

κ), { Z i s i ( / c ) } ) , (oddlist(K,

expandsetT({(K,

movein(K,

{([]),

κ),

«)], {([/c|foi(/c)])}) κ)],

oddlist(n,

{(evenlist(tc,

(oddlist(K,

κ)], {{[]), {[/c|&i(/t)])}) = {([/c|izsi(ac)])}

moveinset([K\oddlist(K,, movein([K\oddlist(K,

true

{(list(n))},

κ), {/zsf (κ)}),

selectset([K,\oddlist(K,

selectset(n,

κ),

κ), { / z s i ( / c ) } ) } ) =

{(κ,

κ),

{&£(«)})})

{(κ, =

κ)),

{{list(K,))} (oddlist(K,

κ))

{(/isi(«))},

κ), {list(n)}),

(oddlist(tc,

/c), { i i s i ( / « ) } ) } )

{()},

{(evenlist(n, (oddlist(κ,

κ),

list(K))}

(κ, oddlist(K,

list(n))}

=

κ)))

{{κ,

=

{fo£(/c)}), κ), {list(κ)})})

Figure 4.8: Evaluation of subsetT(evenlist(K,K),list(ii))

=

true

κ,))

76

Chapter 4. Program Instrumentation

the construction of test inputs. Suppose, a test input goal for a predicate pj3, where t y p e ( p / 3 ) = p ( a , ß , y ) ,

should be generated. If m o d e { p / 3 ) = p ( + , - , - ) ) ,

then, apart from p ( t , X , Y ) , also p ( t , X , X ) is a meaningful test input in case [β]τ Π [7] Γ φ 0. The question ahead is to determine for two ground pure type terms ΰ and ti7 defined by a set Τ of type definitions a ground pure type term τ and a set TT D Τ of type definitions such that τ is defined by TT and [r| T = [i9JT η [w]T. As a basis for the construction of the type intersection algorithm, first two set properties of regular parametric types are established. These properties refer to set intersection for regular parametric types.

Proposition 4.3.12 β

—• Φ

type β.



Τ .

terms Then

1.

Let

defined the

by

T ,

following

[ f ( T

1

=

2.

Let

Τ

be

a

set

Τι,..., τη, ν \ , . . . , ν where

σ

of η

,

and

type

ΰ =

definitions, ασ,

ρ are

and

a

π

ground

=

type

β ρ



—> Φ be

ground

replacings

for a

and pure and

holds.

, . . . , T

n

) ]

T

n [ f ( v

{ f { t i , . . . , t

n

1

) : ti

[tf]TnMT=

, . . . , V e

fo]r

n

) ] η

T

[vi]

T

,

i =

l , . . . , n }

U (J ( [ r ] r D [υ] τ ). τεΦσ ι>εΦ/>

Proof:

1. Follows immediately from Definition 4.2.9. 2. By Definition 4.2.9, [0] T = UTe

the

U U (Μτ η Mr). reΦσ υ^Φρ

• Part 2 of Proposition 4.3.12 has the consequence that, by the distributivity of set union and intersection, new type rules must be generated, when computing the intersection of two types. For this, the cross-product of every two combinations of ground pure type terms of the right hand sides of two

77

4.3. Type Relations

function intersection(ri,T2,T): Θ; input: Τχ, τ^. ground pure type terms T: set of type rules output: θ : tuple (τ, T"), where r is a of ground pure type term defined by a set T' D Τ of type rules begin θ := intersectionx(ri,T2,T, 0) end; Figure 4.9: Type intersection algorithm

type rules to be intersected must be computed. The set of these crossproducts comprise the right hand side of the new type rule. This means, that the intersection [ι9]Γ Π [τσ]τ will be represented by a new type rule Ψ^

U U (WrnMr)· r€Φσ

Because only ground pure type terms are considered, the new type rules to be generated are not parametric. Along the lines of Proposition 4.3.12 the type intersection algorithm can be constructed. Due to the above discussion, the algorithm will be developed in three parts. 1. The main algorithm shown in Figure 4.9 which inputs are two ground pure type terms τχ, r 2 and a set Τ of type rules defining Τχ and r 2 uses an auxiliary algorithm. The output is (τ, TT), where r is a pure type term defined by the set TT D Τ of type rules, τ represents the intersection of τχ and r 2 , i.e., [τ] Ττ = [τχ] τ Π [τ 2 ] Γ . 2. The auxiliary algorithm intersectionx(ri,T2,T, A) shown in Figure 4.10 implements the two parts of Proposition 4.3.12. It also considers the cases where constants and base types are involved. The only interesting case is that one where the input type terms are instances of type functions defined in T. 3. Proposition 4.3.12, part 2, shows that the cross-product of types is needed. The cross-product of two left hand sides of two type rules from Τ is computed in a subsidiary function depicted in Figure 4.11. The cross-product generates new type symbols a, representing the intersection of two types vj\ and vü2· Because such intersection types are local to the computation performed by the type intersection algorithm, the type functions generated are not parametric. These triples (zu\, τυ2, α) are collected in the argument A of intersectionχ to keep track of already computed intersections.

78

Chapter 4. Program Instrumentation

function input:

Λ): Θ; τχ,τ 2 : ground pure type terms T: set of type rules A: set of triples (τσχ, w2, a) where [τσχ]τ Π [£t72JT = a. output: Θ: tuple (τ, Τ'), where τ is a of ground pure type term defined by a set T" D Τ of type rules begin if Π = r 2 t h e n θ := (τχ,Γ); if 3α (η, τ 2 , α) € Λ t h e n Θ := (α, Γ); if τι =77 t h e n Θ := (τ 2 ,Τ); if τ2 = η t h e n Θ := (τχ,Τ); if Τχ = base type symbol β and τ2 = constant c t h e n if r 2 € T\ t h e n Θ := (r 2 ,T) else Θ := (0,T); if r 2 = base type symbol β and τχ = constant c t h e n if η G T2 t h e n θ := (η, Γ) else Θ := (0,T); if τι or r 2 is defined by Τ t h e n if τχ = ψισι,ψι —> Φι € Τ t h e n Φι := Φχσχ else Φι := {η}; if τ = ψ2n) t h e n begin for i := 1 t o η do (u)i,T)

:=

intersectionx(r\j,

Θ := ( / ( ω χ , . . . , end else θ := (0,T) end;

ω

η

r2,i, T , A ) ;

) , Τ )

Figure 4.10: Type intersection algorithm, auxiliary

type

symbol)

79

4.3. Type Relations

function crossproduct(Φι,Φ2, Τ, A)\ θ; input: Φι, Φ2: sets of ground pure type terms T: set of type rules A: set of triples (τσχ, τσ2, a) where [tci] T Π [τσ2]Γ = a. output: Θ: tuple (Φ,T'), where Φ is a set of ground pure type terms defined by a set V D Τ of type rules begin Φ := 0; for each τ* € Φι do for each V{ G Φ2 do begin (σ, Τ) \= intersectionx(ri,Vi,T, A); Φ : = Φ υ {σ} end; θ : = (Φ, Γ) end; Figure 4.11: Cross-product algorithm

Example 4.3.13 Consider the following set Τ of type definitions. W-MO.M/W]}. 7 - > {l.a}, 5 {a, 2}. For better readability, in the evaluation of intersection(a( 7), β(δ),Τ), depicted in Figure 4.12, the set Τ is not written down, it is simply depicted as T. Notice that for any two ground pure type terms T\ and 72, intersection(ri,T2,T) immediately calls intersectionx(Ti,T2,T,ty). The returned type term is ω\ and the set of type rules is augmented by ω

ι ->· {O'M^i]}, ω2 {a}. Δ Example 4.3.14 Let κ denote the base type atom and Τ be the set of type definitions evenlist{v z/2) —> {[], [v\\oddlist(y\, ^2)]}· oddlist(ui, u2) —> {[i>2\evenlist(vi, v2)}}·

80

Chapter 4. Program Instrumentation

intersectionx(a(7),

β(δ),Τ, 0)

crossproductaU^mAUmm^milßiö)^)}) intersection!(W, [], T, {(«(7), β(δ), α>ι)}) intersectionx(\\, [δ\β(δ)],Τ, {(α(7),/3(ωι)}) intersection!(a, 2, Τ, {(7,^,^2), (α(7),/?(^),ωι)}) intersectionx(a(iy), β(δ),Τ U {ω2 —{a}}, {(01(7),/?(£), ωι)}) Figure 4.12: Evaluation of intersection^^),ß(S),

Τ)

Consider the evaluation of intersection(evenlist(n,

κ), oddlist(n, κ))

shown in Figure 4.13. As in the previous example, the set Τ is not written down, it is simply depicted as T. The returned type term then is u>i and the type rules returned are Φι = φ2 = Now emptyτυ^,^}^)

=

^Tue

{ωι ω2

{[«|ω2]}, {[/c|u>i]}}

^ is expected to.

Δ

The basic type operations of type emptyness, type comparison and type intersection are needed and used for test input generation. But types are not all what is needed for this task. As logic programming has, compared to other programming paradigms, a specific concept of inputs and outputs stemming form the relational nature, modes of a program must also be considered. This will be done in the next section.

4.4

Modes and Data Flow

The declaration of the intended input-output constellations of the arguments of a predicate will be done by a mode scheme, similar to mode declarations used by some Prolog compilers, such as Quintus Prolog [119], for object code

81

4.4. Modes and Data Flow

intersectionx(evenlist(n,

κ), oddlist(K,

crossproduct({\\,

[K\oddlist(n,

{(evenlist(K,

κ), oddli$t(n,

intersectionx(\\,

[n\evenlist(K,

{(evenlist(n,

κ),

intersectionx([K\oddlist(K,

κ),ωι)})

/c)], [K\evenlist(K,«)],

κ,Τ,

κ), oddlist(K,

κ), evenlist(K,

κ),

{(evenlist(n,

«)]},

{(oddlist(n,

κ), evenlist(K,

(evenlist(n,

/c), oddlist(K,

intersection!([n\evenlist(K,

κ),

κ), evenlist(K, κ), oddlist(K,

([n\evenlist(n,

/c)]},

Τ,

ω2),

u>i)})

κ)], [], Τ,

(evenlist(K,

κ),ω2), k),u>i)})

κ)], [K,\oddlist(K,

{(oddlist(K,

κ), evenlist(n,

(evenlist(K,

κ), oddlist(K,

intersectionx(n,

ωι)})

κ), Τ, {[], [K\oddlist(n,

κ),

{(oddlist(n, intersection!

κ),

κ), u>i)})

oddlist(tc,

crossproduct({[n\evenlist(n,

T,

κ),ωι)})

{(evenlist(K,

intersectionx(oddlist(K,

T,

u;i)})

κ), oddlist(n,

intersectionx(n,

/c)]},

κ)], Τ,

/c), oddlist(ic,

{(evenlist(n,

0)

κ),Τ,

κ)]}, {[n\evenlist(K,

κ),

/c)], T,

ω2),

κ),ωι)})

κ, Τ,

{(oddlist(K,

κ), evenlist(n,

(evenlist(K,

κ ) , oddlist(K,

intersectionx(evenlist(n,

ω2),

κ),α;ι)})

κ), oddlist(n,

{(oddlist(n,

κ), evenlist(K,

(evenlist(K,

κ), oddlist(K,

Figure 4.13: Evaluation of intersection(evenlist(K,

κ),

κ), κ),

κ), Τ,

ω2), ωχ)})

κ), oddlist(n, κ))

optimization purposes. Modes are states of instantiation of the arguments of a predicate. Before the mode declaration scheme is defined, some general notions about modes will be given. These are, in contrast to types, concerned with the refutation process of resolving goals from program clauses. From the operational view of logic programs, a program clause causes a data flow. Consider a clause C = Η