Skip to content

Latest commit

 

History

History
280 lines (203 loc) · 13.6 KB

README.md

File metadata and controls

280 lines (203 loc) · 13.6 KB

ECSE 429 Software Validation Project, Fall 2015

TouchCORE tool project (subset) for the ECSE429 Software Validation, Fall 2015 course at McGill University. TouchCORE is a multitouch-enabled tool for concern-oriented software design modeling aimed at developing scalable and reusable software design concern models. The version provided here only contains functionality to create Reusable Aspect Models (RAM), or Aspects, which is a multi-view modelling notation supporting structure and behaviour using class, sequence and state diagrams (here called structure, message and state views). The goal of this project is to test parts of the message views. The full version can be found at touchcore.cs.mcgill.ca.

How to get started

To get started, all you need is the code contained in this repository and your development environment.

  • Getting the projects/code: To get the code, you can
    • fork this repository, or
    • import the projects from this repository into your own.
  • Set up the development environment: Please follow all the instructions on How To Setup.

Running TouchCORE

To run TouchCORE, run the class ca.mcgill.sel.ram.ui.TouchCORE.

The Settings.txt in ca.mcgill.sel.ram.gui allows you to adjust the window settings (fullscreen and window size). Also, you might need to adjust the VM arguments to grant TouchCORE more memory in case of performance issues.

Note: TouchCORE requires Java 7 or higher and a graphics card supporting OpenGL 2 (for the UI).

Viewing the Metamodel

To view the different parts of the metamodel the Sirius editor is used. Do the following:

  • Open the Model Explorer view in Eclipse via Window > Show View > Other > Sirius > Model Explorer
    • Note: This view is also available in the Modeling perspective.
  • This view shows you the projects and allows to open an .aird file.
  • Navigate to ca.mcgill.sel.ram/model/ and double-click on RAM.aird.
  • Expand RAM.aird and navigate to Representations per category > Design > Entities
  • Open any of the entries starting with RAM_.

Interacting with the User Interface

In general, TouchCORE supports multitouch, but also keyboard and mouse.

In the instructions below, tap refers to both, a tap using a touchscreen and a click using the mouse. Tap-and-hold refers to clicking the left mouse button and keeping it pressed until the circle on the screen fills up completely.

Generally, elements (or properties of a model) in the user interface allow the following gestures:

  • Double-tap allows editing of that property by either entering/changing text or using a selector shown to choose.
  • Tap-and-hold on an element provides a menu with additional options for that element (see below for more details).
  • Some UI elements allow a unistroke gesture, which is indicated by a yellow line. Tap and hold, then move while still holding down. For example, drawing a rectangle on the background of the class diagram allows to create a class. See below for more information (sometimes referred to as draw a line).

Creating and Modifying Models

First, you need to create concern, which can contain several Aspects. You can view the concern like a project. Each concern has its own folder and a .core file. Each Aspect of that concern should be located in that folder and is serialized into a .ram file.

To create a concern

  • Tap the + button.
  • Create a folder for the concern (the name of the folder will be the name of the concern).
  • Select the folder.
  • The concern will then be opened.

To create an aspect

  • Tap the A+ button.
  • Rename the aspect by double-tapping on Untitled in the top left corner.
  • Save.
To create classes
  • Tap-and-hold on the background of an aspect (alternatively, you can draw a rectangle).
  • Select Class and type in a name.
  • Double-tap on the name to rename the class afterwards.
To create an association between two classes
  • Select one class
  • Double-tap the other class
  • Alternatively, you can draw a line from the edge of one class on top of the other class.
  • To modify, double-tap on specific properties or tap-and-hold on them to choose advanced options.
    • For example, tap-and-holding on the end allows to toggle navigability.
To create attributes and operations:
  • Tap-and-hold on the class
    • Hint: If you want to know what the icons mean, tap-and-hold on the inner circle to toggle showing the labels.
  • Select one of the following options:
    • C+ to create a constructor (named create),
    • O+ to create an operation (type in the signature in one line as shown by the placeholder),
      • visibility: +, -, ~ or # (public, private, package or protected)
      • return type: One of the primitive types or one of your class names
      • parameter: type (see return type) and name
    • A+ to create an attribute (type declaration in one line without the visibility).
  • To modify attributes and operations, double-tap on specific properties or tap-and-hold on them to choose advanced options.
To create or open a message view
  • Tap-and-hold on an operation.
  • Select the Go to Message View icon (the one without the asterisk).
To modify a message view
  • To create a message
    • draw a line from a grey placeholder to either an existing lifeline or into empty space and then
    • select the property and/or the operation to call.
    • Note: Selecting a metaclass allows you to create new instances or call static operations.
  • To create a self or reply message tap-and-hold on the grey placeholder.
  • The available menu will also give you other options to create fragments.
  • To delete a message, tap-and-hold on the message signature (the operation).
    • Note: If a lifeline will become empty due to deleting messages, it will be removed.
  • The same applies to fragments.
  • To modify Combined Fragments (loop, alt, opt etc.)
    • Double-tap on the kind (top left corner) to choose another
    • Tap-and-hold on the constraint to add more operands.

To navigate

  • To go back to the concern, tap on the < button.
  • To go back to the main screen, tap on the home icon.

To open existing models

  • In the main screen, select the folder icon and choose the concern (.core file) to load.
  • The RAM Models list shows all aspects.
  • Double-tap on the one you want to open or create a new one.

Test Code Hints

The following code snippets will be required or helpful for your test setup. For example, models you created with TouchCORE can be loaded programmatically.

Initializing

In order to be able to load existing models, the following code is necessary:

// Initialize ResourceManager.
ResourceManager.initialize();
// Initialize packages.
RamPackage.eINSTANCE.eClass();

// Register resource factories.
ResourceManager.registerExtensionFactory("ram", new RamResourceFactoryImpl());

// Initialize adapter factories.
AdapterFactoryRegistry.INSTANCE.addAdapterFactory(RamItemProviderAdapterFactory.class);

Loading an Aspect

Aspect aspect = (Aspect) ResourceManager.loadModel("path/to/the/concern/Aspect.ram");

Navigating a model

Once you loaded a model, you can navigate it to access its properties and children. Each property and reference in the metamodel has a corresponding getter method. Make use of the code completion provided by Eclipse or use debugging to look at the properties of a loaded model.

Getting a Message View

You can either access them directly by calling aspect.getMessageViews() to retrieve the list. Or, you can request the message view for a specific operation by calling RAMModelUtil.getMessageViewFor(Aspect, Operation).

Calling a controller

Use the ca.mcgill.sel.ram.controller.ram.ControllerFactory class to get the singleton instance of a controller.

Initializing the GUI

In addition to the initializing code provided above, the following is required to run the GUI of TouchCORE.

RamApp.initialize(new Runnable() {
    
    @Override
    public void run() {
        final RamApp app = RamApp.getApplication();        
        app.loadAspect(aspect);
        app.invokeLater(new Runnable() {
            
            @Override
            public void run() {
                // TODO: Your test code.
            }
        });
    }
});

The code inside run() will be executed once TouchCORE is fully set up. Since in some cases the aspect needs to be loaded in the GUI, it is loaded first. The aspect will then be shown when the next frame is drawn.

Your test code should execute after, i.e., it needs to be executed in the frame after that. Therefore, it is invoked later. Inside there, you could then for example create a MessageViewView. The following code snippets also shows you how to access the layout of a message view:

MessageView messageView = (MessageView) aspect.getMessageViews().get(0);
ContainerMapImpl layout = EMFModelUtil.getEntryFromMap(aspect.getLayout().getContainers(), messageView);

MessageViewView messageViewView = new MessageViewView(messageView, layout, 1024, 768);

To access a handler for a view, use the ca.mcgill.sel.ram.ui.views.message.handler.MessageViewHandlerFactory class.

Testing with multiple threads

As you can see in the example code above there is code that can be invoked later. The reason is that the GUI is executed within its own UI thread. So all modifications on the UI need to be performed in this thread. Otherwise you might get an exception during runtime.

This means that your test case within a test method can reach the end, but UI modifications have not been executed yet. When JUnit executed all tests, it shuts down the process and all threads within. So depending on the time it takes you might not see the GUI showing up at all.

To overcome this, you can use a semaphore which forces the main thread to be blocked until another thread is done. A very pragmatic solution could involve sleeping the main thread for a certain number of seconds. However, this requires to "play around" with the time it takes for certain operations. A more flexible solution can be the use of a helper library like concurrentunit. The following code shows an example on how this would be used within one test class. Please see the README of concurrentunit for more details on how it can be used.

public class TestSomeClass {
    
    private static Waiter waiter = new Waiter();

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        // Initialize ResourceManager.
        ResourceManager.initialize();
        // Initialize packages.
        RamPackage.eINSTANCE.eClass();
        
        // Register resource factories.
        ResourceManager.registerExtensionFactory("ram", new RamResourceFactoryImpl());
    
        // Initialize adapter factories.
        AdapterFactoryRegistry.INSTANCE.addAdapterFactory(RamItemProviderAdapterFactory.class);
        
        RamApp.initialize(new Runnable() {
            
            @Override
            public void run() {
                waiter.resume();
            }
        });
        
        // Wait for RamApp to be initialized.
        waiter.await();
    }

    /**
     * Assumes that each test case uses the same test model.
     * Closes and reloads the aspect scene.
     */
    @Before
    public void setUp() throws Exception {
        // Close current aspect.
        if (aspect != null) {
            RamApp.getApplication().invokeLater(new Runnable() {
                @Override
                public void run() {
                    RamApp.getApplication().closeAspectScene(RamApp.getActiveAspectScene());                    
                }
            });
        }
        
        // Load model to use in test.
        aspect = (Aspect) ResourceManager.loadModel("/path/to/my/Model.ram");
        
        RamApp.getApplication().addSceneChangeListener(new ISceneChangeListener() {
            
            @Override
            public void processSceneChangeEvent(SceneChangeEvent event) {
                // Resume once the new aspect scene is loaded (switched to).
                if (event.getNewScene() instanceof DisplayAspectScene) {
                    RamApp.getApplication().removeSceneChangeListener(this);
                    waiter.resume();
                }
            }
        });
        
        RamApp.getApplication().loadAspect(aspect);
        
        // Wait for UI to be updated.
        waiter.await();
    }

    // TODO: Your tests.
}.

Simulating mouse events

To simulate that a mouse button was pressed (and released) somewhere on the screen, use the following code:

app.dispatchEvent(new MouseEvent(app, MouseEvent.MOUSE_PRESSED, 0, MouseEvent.BUTTON1_MASK, x, x, x, y, 1, false, MouseEvent.BUTTON1));
app.dispatchEvent(new MouseEvent(app, MouseEvent.MOUSE_RELEASED, 0, MouseEvent.BUTTON1_MASK, x, y, x, y, 1, false, MouseEvent.BUTTON1));

You need to find out the correct x and y to tap the right location on the screen. You can either debug into MouseInputSource.mousePressed(...) to find out the x and y when you tap somewhere or only dispatch the MOUSE_PRESSED event, which will then show the tap point on the screen.

Background

TouchCORE. is developed by the Software Engineering Lab at the School of Computer Science, McGill University. It's predecessor was TouchRAM.

TouchCORE is built using the Eclipse Modeling Framework for the backend and Multitouch for Java (MT4j) for the touch-based user interface.