In the first part, the JSN library was presented, and also the basic concepts of the Petri Nets and the JSN library were explained. Here, some information about about the internals of the JSN library will be discussed, so you can understand how it works, and, perhaps, develop it further.
I think that the essential skill is testing, so I'll start with explanation, how the JSN library is tested. Once I'll explain some tests, I'm going to show you the code.
At the moment of writing this, the JSN library wasn't ready to be published. Well, I'm rather demanding when it comes to code (not that I never write any shitty one!), so I always try to ace it before publish, but I think I have to make an exception here. Just remember, that the code of the JSN library is far from being perfect, and some parts need to be rewritten.
Before we'll dive into any code, let's try to clone the JSN repository and build the library:
$ git clone https://bitbucket.org/tprimke/jsn.git $ cd jsn $ npm install $ npm run build
Please note, how easy it is: all you have to do is to clone the repository, go to the directory, and then let the NPM do the rest.
Now, we can find the library in the 'build' directory.
For all the tasks like building and testing, gulp is used. But you don't have to call gulp directly, since all the important commands can be executed as npm scripts.
Now, let's see, how we can use that built JSN library in other projects.
Let's create a separate project:
$ cd .. $ mkdir test-project $ cd test-project $ npm init
After the command
npm init, you have to answer many questions. Don't bother with them, you can go on with the default values - it doesn't matter here.
Right now, we have no access to the JSN library:
$ node > var jsn = require('jsn'); Error: cannot find module 'jsn'
We can get the access in two different ways:
If we are going to work on the JSN library - develop new features, perhaps fix bugs - the best solution is to use our own version of the JSN library.
Let's get back to the JSN library directory and link the library to the local node.js/npm ecosystem:
$ cd ../jsn $ npm link
Now, we have to get back to our project's directory and tell npm to use the linked library:
$ cd ../test-project $ npm link jsn $ node > var jsn = require('jsn');
The advantage of such a way of development is that any change made to our local JSN library will be available in our project. And that's what we need, since we are going to use the JSN library in our projects, and develop the library at the same time.
OK, let's get back to the JSN library, and try to test it.
$ cd ../jsn $ npm run test
As we can see, most tests are passing, and some of them are pending / skipped. Well, in fact, the test code needs some cleaning. Let's take a look at the tests.
There are some basic tests (empty net, tokens, evaluating arc expressions). There are also some validation tests, but I'm not going to explain them, since I think all the validation code should be rewritten from scratch. It simply doesn't work as it should. Then we have tests for simple nets (or models), like the counters. The tests for place and transition events are also very important, since using events is a way to automatically control the net. Please don't look at the automatic transition tests, since the concept of the automatic transition has been abandoned in favour of place and transition events. And the last group, so far, is update token tests. Those tests check the results of updating token values, instead of producing a brand new ones.
As I have already said, the test code needs some serious cleaning. There are 47 tests passing, and all the pending tests should be removed. As you can see, the number of tests is not so big, and yet the library works very well. Of course, I expect the number of tests to increase, since the library will be developed in order to make your master's thesis possible to complete.
(By the way, I expect the most changes will be caused by the introduction of the shared places and shared transitions, needed in order to control the simulation using the internet. This "little" change can affect the evaluation of the arc expressions, which are the very heart of the JSN library. What was synchronous before, will become asynchronous, and it can even cause all the code that works today to simply break. So, the very first change made to the library will be the introduction of the shared places and transitions.)
Let's take a look at the packages.json file and see, what is done, when we run the command
npm run test.
As we can see, this command simply runs the gulp's task called "test". So, let's take a look at this task, in the gulpfile.
So, the JSN library is written in ES6, and the tests are in the 'test' directory, and they are standard JS files. Let's take a look at them.
This is only one file in the directory so far. I think it should be split into separate modules, in order to improve readability. Let's take a look at this file.
At the beginning, some modules are imported. I think that mocha is not needed, but the should library is. We also use the sinon library (for mocks), the lodash library, and, of course, the JSN library itself.
After the modules are loaded, the tests begin. Let's take a look at the first test, for the empty net.
At the beginning, we create a new, empty net. The net is empty (or SHOULD be empty), since no specification for the net is given. Such an empty net is not useful at all, but it was a good start for this first test.
Then, we check the time of our net, and we try to tick the timer, and check the net time once again. The net may be empty, but its internal timer should work even without any places, transitions and arcs.
There are tests for the global token and errors, but don't bother with them. I think that the global token feature will be removed, since it wasn't needed while working on the prototype simulation. Right now I think it's not needed at all, and I see no reason for maintaining this feature.
As for errors, the whole concept of handling errors should be rethought, and after that the code will be probably rewritten from scratch.
So, let's focus on the tests for transitions and places. We check the number of ready transitions (which should equal to 0, since there are no transitions in our model), we check the number of places. Then, we check the existence of some methods, like
fire, and we check, whether they throw exceptions. They should throw exceptions, since there are no places that could be got, nor transitions that could be fired.
As for usage of the 'should' assertion library, please refer to its homepage.
Well, before we dive into the JSN library code, let's examine one more test. I think that the tests for the 'count up' counter are a good example.
The tests are defined in a separate suite. The suite provides the
net variable, which is used for storing our counter model.
beforeEach function is called before each test case is run. The function simply creates our counter model.
And the model is quite simple: it consists of one place (which holds the state of the counter), one transition (used to increase the counter), and one arc to connect them. The initial value of the counter is 0, the arc's ready expressions is always 'true', so the transition is always ready, and the arc's place expression is simply increasing the counter value.
In this function, the ES5 syntax is used. It will be probably rewritten in ES6.
beforeEach function, there are two auxiliary functions:
checkReady. The first one examinates the given
place object: it checks the
token properties. The second one checks, whether this is exactly one ready transition in the model, and whether the transition's name is 'count'.
Now, let's take a look at the tests.
The first test checks the result of the
getPlaces function. This function should return the array of all available places in our model. So, we check whether only one place is available, and whether the token value in this place is equal to 0.
These are the basic tests we can perform. Let's ask an interesting question, though: what should happen, when we CHANGED the returned objects? Well, my idea was that changing the returned objects shouldn't affect the net model at all. (If we want to affect the tokens in our places, we should fire some transitions instead.) The
getPlaces function should merely return a kind of 'view' on the real model places.
This behaviour is tested by CHANGING the name and the token values of the returned place object. After such a change, we get the array of places once again, and check, whether the returned place object has the properties set after the net model initialisation. In other words, the fact, that we had changed those values, shouldn't affect the values returned by the
getPlaces function in any way.
getPlace test is very similar, so we won't analyse it here.
And here are the transition tests.
The first test simply checks, whether after initialisation the only transition is ready. Nothing interesting, so far.
More interesting is the
counting up test. After the
count transition is fired, the transition should be ready (since it should be always ready), and the token in the place should has the value of 1.
The last test checks the
reset function. First, we fire the transition, and then we reset our model. After the reset, the transition should be ready and the token in the place should has the value of 0, because reset should cause the model to have the same state as just after the initialisation.
Now, it's time to take a look at the JSN library code.
In the first line, we import the 'lodash' library. Well, it's possible, that in the future I'll prepare a custom build of this library just for the JSN library, in order to squeeze the JSN size a little.
Then, we have our
Net function. This function should return out net model, according to the specification (the
conf argument, which is an empty object by default).
In this place, let's note that:
confargument is an empty object. So, calling the
Netfunction without any arguments should return an empty net model - just as we supposed in our first test.
As we can see, the module provides us some functions. There are functions for time management, for getting information about places and transitions, for firing transitions, and for resetting the model.
There are also other functions, for the global token and for errors handling, but this part of the library will be probably deprecated, so I won't focus on them.
Copyright (C) Tomasz Primke 2015-2016