hr.fer.spocc.automaton.fsm.DefaultNondeterministicFiniteAutomatonTest.java Source code

Java tutorial

Introduction

Here is the source code for hr.fer.spocc.automaton.fsm.DefaultNondeterministicFiniteAutomatonTest.java

Source

/*
 * DefaultNondeterministicFiniteAutomatonTest.java
 *
 * Copyright (C) 2010 Leo Osvald <leo.osvald@gmail.com>
 * Copyright (C) 2010 Matko Raguz <m.raguz@gmail.com>
 *
 * This file is part of SPoCC.
 *
 * SPoCC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SPoCC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SPoCC. If not, see <http://www.gnu.org/licenses/>.
 */
package hr.fer.spocc.automaton.fsm;

import hr.fer.spocc.automaton.fsm.FiniteStateMachine.Transition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.junit.Assert;
import org.junit.Test;

/*
 * @author Leo Osvald
 * @author Matko Raguz
 *
 */
public class DefaultNondeterministicFiniteAutomatonTest {

    @Test
    public void testAddState() {
        NondeterministicFiniteAutomaton<Character> nfa = createNfa();

        nfa.addState(new State(1, false));

        // provjeri da li se stanje zapamtilo pod id-em 1
        State state = nfa.getState(1);
        Assert.assertNotNull(state);

        // provjeri da li to zapamceno stanje stvarno ima id 1
        Assert.assertEquals(1, state.getId());
    }

    @Test
    public void testAddTransition1() {
        NondeterministicFiniteAutomaton<Character> nfa = createNfa();

        // dodaj dva stanja s id-evima 1 i 2
        nfa.addState(new State(1, false));
        nfa.addState(new State(2, false));

        // dodaj prijelaz izmedju ta dva stanja
        nfa.addTransition(1, 2, 'x');

        // vrati kolekciju svih prijelaza
        Collection<Transition<Character>> transitions = nfa.getTransitions();

        // testiraj da li kolekcija sadrzi tocno jedan prijelaz
        Assert.assertEquals(1, transitions.size());

        // testiraj da li kolekcija sadrzi pravi prijelaz
        Transition<Character>[] tranArray = toTransitionArray(transitions);
        Transition<Character> singleTransition = tranArray[0];
        State state1 = nfa.getState(1), state2 = nfa.getState(2);
        // assertSame ti testira da li se radi o istom objektu
        // (ekvivalentno sa operatorom ==)
        Assert.assertSame(state1, singleTransition.getFrom());
        Assert.assertSame(state2, singleTransition.getTo());
        // assertEquals testira da li su objekti (kopije) jednaki
        // a to se ustanovljuje pozivom .equals() metode
        Assert.assertEquals(Character.valueOf('x'), // mogli smo i castati 
                singleTransition.getSymbol());

        // Character je objekt koji sadrzi char
        // on moze biti null (to je nama epsilon prijelaz onda)

        // jos jednom test da li je prijelaz isti (na drugi nacin)
        // ovo za rezliku od proslog usporedjuje da li je prijelaz
        // jednak, a ne da li je isti (da li ima reference na ista stanja)
        checkTransition(nfa, 1, 2, 'x', singleTransition);
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testAddTransition2() {
        NondeterministicFiniteAutomaton<Character> nfa = createNfa();

        // dodaj dva stanja s id-evima 11, 22 i 33
        nfa.addState(new State(11, false));
        nfa.addState(new State(22, false));
        nfa.addState(new State(33, false));

        // dodaj prijelaze
        nfa.addTransition(11, 33, 'c');
        nfa.addTransition(22, 11, null);
        nfa.addTransition(22, 22, 'a');
        nfa.addTransition(11, 22, 'b');

        // testiraj da li sadrzi sve ove prijelaze automat
        checkTransitions(nfa, createTransition(nfa, 11, 22, 'b'), createTransition(nfa, 22, 11, null),
                createTransition(nfa, 11, 33, 'c'), createTransition(nfa, 22, 22, 'a'));
    }

    @Test
    public void testNoStartStateProcess() {
        NondeterministicFiniteAutomaton<Character> nfa = createNfa();

        // dodaj dva stanja s id-evima 1 i 2 i povezi ih sa 'a'
        nfa.addState(new State(1, false));
        nfa.addState(new State(2, false));
        nfa.addTransition(1, 2, 'a');

        boolean ok = false;
        // probaj pokrenuti automat - daj mu na ulaz znak 'a'
        try {
            nfa.process('a'); // ocekujemo da ce tu puknuti program
            // naime u dokumentaciji ove metode pise da baca exception
            // ako nije definirano pocetno stanje a automat se pokrene
            // (Ctrl + left click na metodu pa procitaj)
        } catch (IllegalStateException e) {
            // ALI cekaj, pa uopce nismo definirali poceetno stanje
            // pa bi bilo logicno da automat baci nekakvu izniku i na 
            // taj nacin nas upozori da smo napravili logicku gresku
            ok = true;
            // da vidimo o cem se zapravo radi
        }

        Assert.assertTrue(ok); // ako je false onda nije doslo do exceptiona
        // a trebalo je - dakle ne valja
    }

    @Test
    public void testProcess1() {
        NondeterministicFiniteAutomaton<Character> nfa = createNfa();
        nfa.addState(new State(1, false), true); // ovo je pocetno stanje
        nfa.addState(new State(2, false));
        nfa.addState(new State(3, false));
        nfa.addState(new State(4, false));
        nfa.addState(new State(5, false));
        nfa.addTransition(1, 2, 'a');
        nfa.addTransition(1, 3, null); // ovo je epsilon prijelaz
        nfa.addTransition(3, 5, null); // ovo je epsilon prijelaz
        nfa.addTransition(5, 4, 'b');
        nfa.addTransition(4, 1, 'a');
        nfa.addTransition(2, 4, 'b');
        nfa.addTransition(2, 1, 'a');

        nfa.reset();

        // provjeri dal je 1 u skupu trenutnih stanja (mora bit jel je to pocetno stanje)
        Assert.assertTrue(nfa.getCurrentStates().contains(nfa.getState(1)));

        // posto je epsilon okolina od 1 skup {3, 5}, 
        // onda bi i ona trebala biti u skupu trenutnik
        Assert.assertTrue(nfa.getCurrentStates().contains(nfa.getState(3)));
        Assert.assertTrue(nfa.getCurrentStates().contains(nfa.getState(5)));

        // sad ucitaj znak 'a'
        nfa.process('a');

        // sad bi automat trebao biti u stanjima {2}

        nfa.process(toList("ab"));

        // restiraj automat - dovedi ga u pocetno stanje
        nfa.reset();

        // sad ucitaj niz "aaaa"
        nfa.process(toList("aba"));

        // automat bi trebao biti u {1, 3, 5}
        assertCurrentStates(nfa, 1, 3, 5);
    }

    @Test
    public void testInfiniteLoop() {
        // gradimo automat koji se stalno moze saltati izmedju stanja 1 i 2
        NondeterministicFiniteAutomaton<Character> nfa = createNfa();
        nfa.addState(new State(1, false), true); // ovo je pocetno stanje
        nfa.addState(new State(2, false));
        nfa.addState(new State(3, false));
        nfa.addTransition(1, 2, null);
        nfa.addTransition(2, 1, null);
        nfa.addTransition(1, 1, 'x');
        nfa.addTransition(2, 2, 'x');
        nfa.addTransition(2, 3, 'x');
        nfa.addTransition(3, 2, 'x');

        nfa.reset();

        assertCurrentStates(nfa, 1, 2); // skup pocetnih stanja je {1, 2}

        nfa.process('x'); // sad je neparan broj x-eva, znaci {1, 2, 3}
        assertCurrentStates(nfa, 1, 2, 3);

        nfa.process(toList("x"));
        assertCurrentStates(nfa, 1, 2, 3);

        nfa.process(toList("xxxx"));
        assertCurrentStates(nfa, 1, 2, 3);

        nfa.process('x');
        assertCurrentStates(nfa, 1, 3, 2); // nije bitan redoslijed

        nfa.process('f'); // ovo je nedefiniran prijelaz, pa je trenutni skup stanja {}
        assertCurrentStates(nfa); // predajemo prazan skup (nista)
        // mogli smo i Assert.assertTrue(nfa.getCurrentStates().isEmpty());
    }

    // NASTAVAK TESTOVA
    // OVDJE

    @Test
    public void test1() {
        NondeterministicFiniteAutomaton<Character> nfa = createNfa();

        // dodavanje stanja
        nfa.addState(new State(0, false));
        nfa.addState(new State(1, false));
        nfa.addState(new State(2, false));
        nfa.addState(new State(3, false));
        nfa.addState(new State(4, false));
        nfa.addState(new State(5, false));
        nfa.addState(new State(6, false));
        nfa.addState(new State(7, true));
        nfa.setStartState(0);

        // dodaj prijelaze
        nfa.addTransition(0, 1, 'a');
        nfa.addTransition(0, 4, 'b');
        nfa.addTransition(0, 4, 'c');
        nfa.addTransition(1, 4, 'a');
        nfa.addTransition(1, 4, 'c');
        nfa.addTransition(1, 2, 'b');
        nfa.addTransition(2, 3, 'c');
        nfa.addTransition(2, 4, 'b');
        nfa.addTransition(2, 4, 'a');
        nfa.addTransition(3, 4, 'b');
        nfa.addTransition(3, 4, 'c');
        nfa.addTransition(3, 5, 'a');
        nfa.addTransition(5, 6, 'a');
        nfa.addTransition(6, 7, 'b');
        nfa.addTransition(5, 4, 'c');
        nfa.addTransition(4, 4, 'c');
        nfa.addTransition(4, 4, 'a');
        nfa.addTransition(4, 4, 'b');
        nfa.addTransition(6, 4, 'c');
        nfa.addTransition(6, 4, 'a');
        nfa.addTransition(5, 2, 'b');

        nfa.reset();

        //provjera 1

        nfa.process('a');
        assertCurrentStates(nfa, 1);

        //provjera 2

        nfa.process('b');
        assertCurrentStates(nfa, 2);

        //provjera 3

        nfa.process('c');
        assertCurrentStates(nfa, 3);

        //provjera 4

        nfa.process('a');
        assertCurrentStates(nfa, 5);

        //provjera 5

        nfa.process('b');
        assertCurrentStates(nfa, 2);

        //provjera 6

        nfa.process('c');
        assertCurrentStates(nfa, 3);

        //provjera 7

        nfa.process('a');
        assertCurrentStates(nfa, 5);

        //provjera 8

        nfa.process('a');
        assertCurrentStates(nfa, 6);

        //provjera 9

        nfa.process('b');
        assertCurrentStates(nfa, 7);

        //kraj provjera jednog po jednog znaka za niz "abcabcaab"

        nfa.reset();

        //provjera istog automata sa nizovima znakova
        //provjera 10 - niz "abc"

        nfa.process(toList("abc"));
        assertCurrentStates(nfa, 3);

        nfa.reset();

        //provjera 11 - niz "abcaa"

        nfa.process(toList("abcaa"));
        assertCurrentStates(nfa, 6);

        nfa.reset();

        //provjera 12 - niz "abcabc"

        nfa.process(toList("abcabc"));
        assertCurrentStates(nfa, 3);

        nfa.reset();

        //provjera 13 - niz "abcabcaab"

        nfa.process(toList("abcabcaab"));

        assertCurrentStates(nfa, 7);
    }

    @Test
    public void test2() {
        NondeterministicFiniteAutomaton<Character> nfa = createNfa();

        // dodavanje stanja
        nfa.addState(new State(0, false));
        nfa.addState(new State(1, false));
        nfa.addState(new State(2, true));
        nfa.addState(new State(3, false));
        nfa.setStartState(0);

        //prijelazi

        nfa.addTransition(0, 1, 'a');
        nfa.addTransition(0, 2, 'b');
        nfa.addTransition(0, 1, 'c');
        nfa.addTransition(0, 1, null);
        nfa.addTransition(0, 3, null);
        nfa.addTransition(1, 2, 'a');
        nfa.addTransition(1, 1, 'a');
        nfa.addTransition(1, 1, 'b');
        nfa.addTransition(1, 3, 'c');
        nfa.addTransition(1, 2, null);
        nfa.addTransition(2, 2, 'a');
        nfa.addTransition(2, 1, 'b');
        nfa.addTransition(2, 3, 'c');
        nfa.addTransition(3, 2, 'a');

        nfa.reset();

        //test 1 - znak "c"

        nfa.process('c');
        assertCurrentStates(nfa, 1, 2, 3);

        nfa.reset();

        //test 2 - niz "ab"

        nfa.process(toList("ab"));
        assertCurrentStates(nfa, 1, 2);

        nfa.reset();

        //test 3 - niz "aca"

        nfa.process(toList("aca"));
        assertCurrentStates(nfa, 2);

        nfa.reset();

        //test 4 - niz "ca"

        nfa.process(toList("ca"));
        assertCurrentStates(nfa, 1, 2);

        nfa.reset();

        //test 5 - niz "ab"

        Assert.assertTrue(nfa.process(toList("ab")));
        assertCurrentStates(nfa, 1, 2);

        nfa.reset();

        //test 6 - niz "bcab"

        nfa.process(toList("bcab"));
        assertCurrentStates(nfa, 1, 2);

        nfa.reset();

        //test 7 - niz "ababacaabba"

        nfa.process(toList("abacaabba"));
        assertCurrentStates(nfa, 1, 2);

        nfa.reset();

        //test 8 - niz "abbabbabcac"

        Assert.assertTrue(nfa.process(toList("abbabbabcac")));
        assertCurrentStates(nfa, 3);

        nfa.reset();

        //test 9 - niz "abcc"
        //ovdje je prijelaz u nedefinirano stanje

        Assert.assertFalse(nfa.process(toList("abcc")));
        assertCurrentStates(nfa);

        nfa.reset();

        //test 10 - niz "abcbaa"
        //ovdje je prijelaz u nedefinirano stanje
        //nakon abcb je vec nedefinirano, a aa sam jos dodo, valjda sam smio - u svakom slucaju ostaje u nedefiniranom

        Assert.assertFalse(nfa.process(toList("abcbaa")));
        assertCurrentStates(nfa);

        nfa.reset();

        //test 11 - niz "ccc"
        //ovdje je prijelaz u nedefinirano stanje

        Assert.assertFalse(nfa.process(toList("ccc")));
        assertCurrentStates(nfa);

        nfa.reset();

        //test 12 - niz "babacabacb"
        //ovdje je prijelaz u nedefinirano stanje

        Assert.assertFalse(nfa.process(toList("babacabacb")));
        assertCurrentStates(nfa);

        nfa.reset();

    }

    @Test
    public void test3() {
        NondeterministicFiniteAutomaton<Character> nfa = createNfa();

        // dodavanje stanja
        nfa.addState(new State(0, false));
        nfa.addState(new State(1, false));
        nfa.addState(new State(2, true));
        nfa.addState(new State(3, false));
        nfa.addState(new State(4, true));
        nfa.setStartState(0);

        //prijelazi

        nfa.addTransition(0, 0, 'a');
        nfa.addTransition(0, 2, 'b');
        nfa.addTransition(0, 1, 'b');
        nfa.addTransition(0, 3, 'c');
        nfa.addTransition(0, 4, 'c');
        nfa.addTransition(0, 2, null);
        nfa.addTransition(1, 3, 'a');
        nfa.addTransition(1, 1, 'b');
        nfa.addTransition(1, 1, 'c');
        nfa.addTransition(1, 2, 'd');
        nfa.addTransition(1, 2, null);
        nfa.addTransition(1, 1, null);
        nfa.addTransition(2, 4, 'a');
        nfa.addTransition(2, 0, 'b');
        nfa.addTransition(2, 2, 'c');
        nfa.addTransition(2, 4, null);
        nfa.addTransition(3, 1, 'a');
        nfa.addTransition(3, 3, 'a');
        nfa.addTransition(3, 3, null);
        nfa.addTransition(3, 0, null);
        nfa.addTransition(4, 0, 'a');
        nfa.addTransition(4, 4, 'a');

        nfa.reset();

        //test 1 - niz "ab"

        nfa.process(toList("ab"));
        assertCurrentStates(nfa, 1, 2, 0, 4);

        nfa.reset();

        //test 2 - niz "bca"

        nfa.process(toList("bca"));
        assertCurrentStates(nfa, 1, 2, 3, 4, 0);

        nfa.reset();

        //test 3 - niz "bcab"

        nfa.process(toList("bcab"));
        assertCurrentStates(nfa, 1, 0, 2, 4);

        nfa.reset();

        //test 4 - niz "acaba"

        nfa.process(toList("acaba"));
        assertCurrentStates(nfa, 2, 3, 4, 0);

        nfa.reset();

        //test 5 - niz "abdacab"

        nfa.process(toList("abdacab"));
        assertCurrentStates(nfa, 0, 1, 2, 4);

        nfa.reset();

        //test 6 - niz "abbaaa"

        Assert.assertTrue(nfa.process(toList("abaaa")));
        assertCurrentStates(nfa, 0, 1, 2, 4, 3);

        nfa.reset();

        //test 7 - niz "cacaa"
        //ovdje je u nedefiniranom stanju

        Assert.assertTrue(nfa.process(toList("cacaa")));
        assertCurrentStates(nfa, 0, 1, 2, 3, 4);

        nfa.reset();

        //test 8 - niz "abdd"
        //ovaj je u nedefiniranom podrucju

        Assert.assertFalse(nfa.process(toList("abdd")));
        assertCurrentStates(nfa);

        nfa.reset();

        //test 9 - niz "abd"
        //ovaj je u nedefiniranom podrucju

        nfa.process(toList("abd"));
        assertCurrentStates(nfa, 2, 4);

        nfa.reset();

    }

    // privatne metode za lakse testiranje

    // ovo ti je da se ne moras mucit s stvaranjem templatiranog polja
    @SuppressWarnings("unchecked")
    private static <T> Transition<T>[] toTransitionArray(Collection<Transition<T>> transitions) {
        return transitions.toArray(new Transition[0]);
    }

    // ovo ti je da se ne moras mucit s stvaranjem prijelaza
    private static <T> Transition<T> createTransition(NondeterministicFiniteAutomaton<T> nfa, int fromId, int toId,
            T inputSymbol) {
        return new Transition<T>(nfa.getState(fromId), nfa.getState(toId), inputSymbol);
    }

    private static <T> void checkTransition(NondeterministicFiniteAutomaton<T> nfa, int fromId, int toId,
            T inputSymbol, Transition<T> actual) {
        Assert.assertEquals(createTransition(nfa, fromId, toId, inputSymbol), actual);
    }

    // ovo ti je da se ne moras mucit s assertanjem jednog po jednog prijelaza
    // - samo dodas kao zadnji parametar proizvoljan broj prijelaza
    private static <T> void checkTransitions(NondeterministicFiniteAutomaton<T> nfa,
            Transition<T>... expectedTransitions) {
        Validate.isTrue(expectedTransitions.length > 0, "Moras dat bar jedan prijelaz, inace nema smisla");
        Collection<Transition<T>> transitions = nfa.getTransitions();
        for (Transition<T> transition : expectedTransitions) {
            Assert.assertTrue(transitions.contains(transition));
        }
    }

    private static <T> void assertCurrentStates(NondeterministicFiniteAutomaton<T> nfa, Integer... ids) {
        Set<State> expectedCurrentStates = new HashSet<State>(ids.length);
        for (Integer i : ids)
            expectedCurrentStates.add(nfa.getState(i));
        Assert.assertEquals(expectedCurrentStates, nfa.getCurrentStates());
    }

    @SuppressWarnings("unused")
    private static <T> void assertCurrentStatesContains(NondeterministicFiniteAutomaton<T> nfa, Integer... ids) {
        Set<State> expectedCurrentStatesSubset = new HashSet<State>(ids.length);
        for (Integer i : ids)
            expectedCurrentStatesSubset.add(nfa.getState(i));
        Assert.assertTrue(nfa.getCurrentStates().containsAll(expectedCurrentStatesSubset));
    }

    /**
     * Pretvara string u listu znakova.
     * 
     * @param s niz znakova (string)
     * @return lista znakova
     */
    private static List<Character> toList(String s) {
        List<Character> list = new ArrayList<Character>();
        for (int i = 0; i < s.length(); ++i)
            list.add(s.charAt(i));
        return list;
    }

    private static <T> NondeterministicFiniteAutomaton<T> createNfa() {
        return new DefaultNondeterministicFiniteAutomaton<T>();
    }

}