Table of Contents

Lab0: Calendar

If you get stuck, be sure to view the Instructional Videos.

For Lab0 below (Calendar example) view accompanying Video here

First Project Using the IDE

Precondition: Study how to to use the EiffelStudio IDE, add the ESpec library, change the root class, add clusters and classes, and write tests.

If you have met the above preconditions, you should be able to start a new Eiffel project from scratch. Start Eiffelstudio, and setup a new project “calendar” as shown in the steps below:

Step1: Create a new project

Create a new calendar project at the terminal: Eifflel 18.11 Void Safe Starter Project:

red> eiffel-new 
New Eiffel void-safe project name: calendar
red> ls
calendar

Check your directory structure to see that the file system mirrors the cluster hierarchy.

calendar
├── calendar.ecf
├── model
│   └── calendar.e
├── root
│   └── application.e
└── tests
    └── test.e

The new project is in the folder calendar.

estudio18.11 calendar/calendar.ecf&

Also, take a look at the Settings and the XML/ECF file to see the Void safe settings.

To add a new cluster or class see FAQ: Adding a new cluster/class

Calendar Class

The start of a Calendar class might be as follows:

Note that the query has a precondition, and calls another query to calculate the leap year:

The leap year calculation also has a postcondition.

Also note the indexing clause. Apply the self-documentation principle by giving meaningful names, preconditions, postconditions, invariants etc.

Note: the implementation of the leap year query may not be correct. Does it satisfy the postcondition?

In the Gregorian calendar (established in 1582) we add a Leap Day on February 29, almost every four years. Three criteria must be taken into account: The year can be evenly divided by 4; If the year can be evenly divided by 100, it is NOT a leap year, unless, the year is also evenly divisible by 400. Then it is a leap year. This means that in the Gregorian calendar, the years 2000 and 2400 are leap years, while 1800, 1900, 2100, 2200, 2300 and 2500 are NOT leap years.

Instead of the informal English description, the postcondition is a precise mathematical specification of the leap-year query.

ESpec Unit Tests

Try the following tests

Test t0 is a Boolean query whereas test t1 is a command (a violation case). Make sure you know the difference.

Note that a boolean test must terminate with Result true, but you can also add some asserts along the way. In addition to the comment, you can also apply additional documentation via sub_comment.

The Root Class APPLICATION

The Root class should look as follows

The root class inherits from ES_SUITE. (You can also let the root class inherit from ES_TEST, and add the tests in the root class). We can then add further classes with other tests.

Testing feedback in the browser

Ensure that you get the above feedback.

Question: How many tests must you write to ensure than you are calculating the leap year correctly?

Here is another question. Suppose wee add another violation test t2:

This results in a red bar

Why?

Additional features, design and class invariant

We wish to add additional features such as advance the date to tomorrow and next week, and change the date to yesterday and last week. We may also want to calculate the number of days between two dates.

The user will have to keep adding arguments to commands tomorrow(a_year, a_month, a_day:INTEGER), yesterday (a_year, a_month, a_day:INTEGER) etc.

Is there a better design? One that avoids all those arguments evert time we wish to change the date?

How about the following:

Note that the invariant constraints dates to the potentially allowable range, but a feature is_valid_date is still needed to check that the actual date is allowable given leap years etc.

Using the debugger

When tests fail, it is essential to be able to use the debugger to track down the error.

Using Estudio Debugger

This is part of a series of instructional videos: Eiffel instructional videos

Abstraction

Wikipedia defines abstraction as follows: “In software engineering and computer science, abstraction is a technique for arranging complexity of computer systems. It works by establishing a level of complexity on which a person interacts with the system, suppressing the more complex details below the current level. The programmer works with an idealized interface (usually well defined) and can add additional levels of functionality that would otherwise be too complex to handle. For example, a programmer writing code that involves numerical operations may not be interested in the way numbers are represented in the underlying hardware (e.g. whether they're 16 bit or 32 bit integers), and where those details have been suppressed it can be said that they were abstracted away, leaving simply numbers with which the programmer can work. In addition, a task of sending an email message across continents would be extremely complex if the programmer had to start with a piece of fiber optic cable and basic hardware components. By using layers of complexity that have been created to abstract away the physical cables and network layout, and presenting the programmer with a virtual data channel, this task is manageable.”

Beck's map of the London underground is a wonderful example of abstraction, in which irrelevant details have been suppressed and only the relevant information to use the underground is described:

Abstraction in the Calendar program

How can we write the contracts for additional features such as tomorrow, yesterday and the number of days between any two dates? What about if awe want to convert one calendar date (say the Gregorian) to a Julian date, or a Persian date?

This is where a good abstraction can greatly simplify the code and its specification.

In Calendrical Calculations, Nachum Dershowitz et. al (Cambridge), provide an abstract model of dates as a sequence of integers

..., -3, -2, -1, 0,, 1, 2, 3 ,..

When introducing any new calendar (say the ISO calendar) all that we need is a function to convert from that calendar to the fixed days and vice versa. Here is the abstraction function for the Georgian calendar:

g2fd is an abstraction function. We can now write the specification for the command tomorrow as

year: INTEGER
month: INTEGER
day: INTEGER
 
tomorrow
	do
		...
	ensure
		g2fd(year, month, day) = old g2fd(year, month, day) + 1
	end

Here is a test case:

t3: BOOLEAN
	local
		c1, c2: CALENDAR
	do
		comment("t3: test tomorrow")
		create c1.make (2016, 12, 31)
		create c2.make (2017, 1, 1)
		Result := c1.is_valid_date
		check Result end
		c1.tomorrow
		Result := c1.year=2017 and c1.month=1 and c1.day =1
		check Result end
		Result := not (c1 = c2) and (c1 ~ c2)
		create c1.make (1900, 2, 28)
		c1.tomorrow
		Result := c1.year=1900 and c1.month=3 and c1.day =1
		check Result end
		c1.yesterday
		Result := c1.year=1900 and c1.month=2 and c1.day =28
	end

With a sequence of integers as a data abstraction, converting from one calendar to another and specifying calendar features becomes much simpler.

Building Maintainable Software

See “Building Maintainable Software: Ten Guidelines for Future-Proof Code”, by Joost Visser, O-Reilley, 2016.

Difficult-to-maintain source code is a big problem in software development leading to costly delays and defects. In addition, it is usually frustrating working with someone else's code. The mark of a professional is the ability to develop code that others can understand and maintain. The authors provide the following guidelines for building maintainable software: