Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > de.comp.lang.java > #13019

Re: JUnit Test von JButton: Action wird nicht erkannt

From "Christian H. Kuhn" <qno-news@qno.de>
Newsgroups de.comp.lang.java
Subject Re: JUnit Test von JButton: Action wird nicht erkannt
Date 2016-07-19 14:59 +0200
Message-ID <dv6mjhFqq9cU1@mid.individual.net> (permalink)
References <duo028Fb4qaU1@mid.individual.net> <nm8j8b$r9c$1@newsreader4.netcologne.de> <duqgrdFtso5U1@mid.individual.net> <nmbfkk$sk4$1@newsreader4.netcologne.de>

Show all headers | View raw


Version 0.9.2

Am 15.07.2016 um 22:04 schrieb Patrick Roemer:
> Responding to Christian H. Kuhn:

> interface TimeSource {
>   long getTimeInNanoSecs();
> }

Den Gedanken habe ich aufgegriffen und mal implementiert. Die TimeSource
darf sich dabei aussuchen, welche Zeiteinheit sie verwendet; es muss nur
ein ganzzahliger Bruchteil einer Sekunde sein.

public interface QTimeSource {
    long getFactorToSeconds(); // erlaubt versch. time units
    long getNow();
}

public class QSystemMilliTime implements QTimeSource {
    private static final long MILLIS_PER_SECOND = 1000;
    @Override public final long getFactorToSeconds() {
        return MILLIS_PER_SECOND;
    }
    @Override public final long getNow() {
        return System.currentTimeMillis();
    }
}

private static class TestTimeSource implements QTimeSource {
        private static final long SECONDS_TO_SECONDS = 1;
        private long              now;
        @Override public long getFactorToSeconds() {
            return SECONDS_TO_SECONDS;
        }
        @Override public long getNow() {
            synchronized (this) {
                return now;
            }
        }
        public void setNow(final long _now) {
            synchronized (this) {
                now = _now;
            }
        }
}

Dazu habe ich Stefan Rams Timer-Konzept im großen und ganzen übernommen
und mit synchronize(this) (hoffentlich?) thread safe gemacht. Der Timer
arbeitet jetzt mit abstrakten Zeiten. Zur Sicherheit müsste im
Konstruktor die startTime noch auf ein adäquates now gesetzt werden,
falls von start(long) auch Werte <= 0 kommen könnten.

public final class QCountdownTimer {
    private transient long    remainingTime;
    private transient long    additionalFischerTime;
    private transient long    startTime;
    private transient long    stopTime;
    private transient boolean running;

    QCountdownTimer(final long _timeOfReflection) {
        this(_timeOfReflection, 0);
    }
    QCountdownTimer(final long _timeOfReflection, final long
_additionalFischerTime) {
        running = false;
        additionalFischerTime = _additionalFischerTime;
        remainingTime = _timeOfReflection + additionalFischerTime;
    }

    /* default */ long getRemainingTime(final long _now) { // Parameter
has no effect if !running
        long actualRemaningTime;
        synchronized (this) {
            if (running && _now > startTime) {
                actualRemaningTime = remainingTime - _now + startTime;
            } else {
                actualRemaningTime = remainingTime;
            }
            return actualRemaningTime;
        }
    }

    /* default */ void start(final long _startTime) {
        synchronized (this) {
            if (!running && _startTime >= stopTime) {
                running = true;
                startTime = _startTime;
            }
        }
    }

    /* default */ void stop(final long _stopTime) {
        synchronized (this) {
            if (running && _stopTime >= startTime) {
                running = false;
                stopTime = _stopTime;
                remainingTime = remainingTime - (stopTime - startTime) +
additionalFischerTime;
            }
        }
    }

    /* default */ boolean isRunning() {
        synchronized (this) {
            return running;
        }
    }
}

QChessClock kann jetzt im Konstruktor eine QTimeSource mitbekommen,
ansonsten nimmt sie QSystemMilliTime.

> ...und für Tests dann sowas:

Genau.

> Das ist aber nur die halbe Miete. Man würde dann auch den konkreten
> j.u.Timer in der Kernklasse wegabstrahieren wollen. 

Siehe den langen Subthread „Countdown Timer Design“ mit vielen Beiträgen
von Stefan Ram. Dem bin ich in der ersten Stufe gefolgt: QCountdownTimer
weiss nichts von Zeitquellen und hat auch keinen TimerTask mehr. Der
konkrete Zeitbegriff und die Überprüfung auf Zeitablauf erfolgt im
Verwender.

In einem Punkt stimme ich mit Stefan nicht überein. Er hat auch die
Schachuhr von TimeSource und TimerTask befreit. In der einfachen
Blitzschachuhr, die er benutzt, ist das ok. Da wird bei Blättchenfall
einfach nur eine Anzeige gesetzt und gut.

Abweichend vom sauberen TDD, siehe Beiträge von Wanja Gayk im Thread
„Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle“, weiss ich
aber jetzt schon, dass in der nächsten Ausbaustufe beim Blättchenfall
zusätzliche Funktionalität dazukommt. Die ist in der Begrifflichkeit des
Kano-Modells kein Begeisterungs- oder auch nur Leistungs-, sondern
Basis-Merkmal und wird definitiv im ersten Release enthalten sein. Also
darf ich auch ohne Test in die Richtung planen ;-)

Es geht um mehrere Bedenkzeitperioden. Bislang implementiert sind
Bedenkzeitmodelle mit einer Periode. Ist die Zeit für diese Periode
abgelaufen, ist die Partie im Prinzip vorbei. Die Zeitüberschreitung
wird angezeigt, mehr Funktionalität ist nicht nötig. Das wird in der
Praxis bei sogenannten Blitz- und Rapid-Partien angewandt und betrifft
typischerweise kurze Bedenkzeiten von 1 bis 90 min pro Spieler und
Partie, mit Zeitzuschlägen von 1 bis 20 s.

In „langen“, „ernsten“, „Turnier-“ Partien werden üblicherweise zwei
oder mehr Zeitperioden benutzt. In den unteren Amateurklassen ist sehr
verbreitet: 2 h für die ersten 40 Züge, danach ein Zuschlag von einer
oder einer halben Stunde. Die Bundesliga spielte von 2009 bis 2014 mit
100 min für die ersten 40 Züge, 50 min für die nächsten 20 Züge, 15 min
für den Rest, 30s akkumulierender Zuschlag pro Zug.

Die letzte Zeitperiode wird gehandhabt wie bisher: Bei Ablauf Anzeige
und gut. Die Zeitperioden davor machen mehr Arbeit. Bei Ablauf der
Periode (in der Regel durch Bedenkzeitüberschreitung, gelegentlich auch
durch Erreichen der Zügezahl, wofür noch ein Zugzähler zu implementieren
ist) wird die Zeitüberschreitung beim jeweiligen Spieler so angezeigt,
dass sie über einen längeren Zeitraum festgestellt werden kann.
Gleichzeitig wird die erlaubte Bedenkzeit der nächsten Periode bei
beiden Spielern zur aktuellen Bedenkzeit addiert.

Ich bin ja der Ansicht, dass diese Umstellung der verbleibenden
Bedenkzeiten ein Wissen über Implementationsdetails der Schachuhr
verlangt. Das ist vor anderen Klassen zu verbergen und gehört damit in
die Schachuhr. Auch die regelmäßige Überprüfung auf Zeitüberschreitung
darf nicht anderen Klassen überlassen werden. Sie ist von der Schachuhr
selbst zu leisten; entsprechende Ereignisse sind an Oberflächen zu
senden. Damit benötige ich einen TimerTask in der Schachuhr, ein
ScheduledThreadPoolExecutor wäre ne Nummer zu dick. Der TimerTask
überprüft in sinnvollen Abständen auf ZÜ. Bei Anzeigengenauigkeit von 1s
wäre ein Takt von 1s zu langsam; der durchschnittliche Fehler von einer
halben Sekunde ist beim Blitzen bereits relevant. Der Millisekunden-Takt
der Standard-Zeitquelle würde sehr genau anzeigen, aber eine nicht mehr
wahrnehmbare Genauigkeit produzieren. Der Mensch kann Bilder in Folgen
von 24/s wahrnehmen, Sicherheitsfaktor 2, runden, sind wir bei 1/50 s
oder 20 ms. Und wenn dieser Thread schon mal existiert, kann er auch
veränderte Zeiten an die GUI mitteilen.

> Über SwingUtilities#invokeLater().

Mit den beiden invoke bin ich auch am Kämpfen. Beide sorgen auf dem EDT
für eine klare Abfolge von Events. Nur invokeAndWait() sorgt aber auch
für eine happens-before-Beziehung zwischen ausgeführten Befehlen auf dem
aufrufenden Thread und dem EDT. Für manche Tests nicht unwichtig, die
darauf warten müssen, dass das EDT-Ereignis auch ausgeführt ist, bevor
der Test funktionieren kann. Dafür wirft invokeAndWait() eine Exception.
In normalem Code kann die problemlos abgefangen und verarbeitet werden;
in Runnables und actionPerformed muss man entweder eine try-catch mit
leerem Catch-Block machen, was alle statischen Codechecker anmeckern,
oder man macht im Catch-Block so Dinge wie

final Thread thread = Thread.currentThread();
thread.getUncaughtExceptionHandler().uncaughtException(thread, e);

die auch keinen Schönheitspreis gewinnen.

lg
QNo

Back to de.comp.lang.java | Previous | NextPrevious in thread | Next in thread | Find similar


Thread

JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-14 01:09 +0200
  Re: JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-14 18:26 +0200
  Re: JUnit Test von JButton: Action wird nicht erkannt Patrick Roemer <sangamon@netcologne.de> - 2016-07-14 19:47 +0200
    Re: JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-15 00:08 +0200
      Re: JUnit Test von JButton: Action wird nicht erkannt Patrick Roemer <sangamon@netcologne.de> - 2016-07-15 22:04 +0200
        Re: JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-15 22:53 +0200
        Re: JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-15 23:09 +0200
          Re: JUnit Test von JButton: Action wird nicht erkannt Patrick Roemer <sangamon@netcologne.de> - 2016-07-15 23:44 +0200
            Countdown Timer Design (was: JUnit Test von JButton: Action wird nicht erkannt) "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-16 23:44 +0200
              Re: Countdown Timer Design "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-17 12:44 +0200
              Re: Countdown Timer Design "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-17 15:49 +0200
                Re: Countdown Timer Design "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-17 17:03 +0200
                Re: Countdown Timer Design "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-19 15:59 +0200
        Re: JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-19 14:59 +0200
          Re: JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-19 16:06 +0200
          Re: JUnit Test von JButton: Action wird nicht erkannt Patrick Roemer <sangamon@netcologne.de> - 2016-07-19 18:59 +0200
            Re: JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-19 22:35 +0200
              Re: JUnit Test von JButton: Action wird nicht erkannt Patrick Roemer <sangamon@netcologne.de> - 2016-07-20 13:00 +0200
                Re: JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-23 20:36 +0200
                Re: JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-23 23:15 +0200
          Re: JUnit Test von JButton: Action wird nicht erkannt Wanja Gayk <brixomatic@yahoo.com> - 2016-07-19 23:02 +0200
            Re: JUnit Test von JButton: Action wird nicht erkannt "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-20 12:33 +0200
    GUI-Update über Swing-EDT (was: JUnit Test von JButton: Action wird nicht erkannt) "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-15 12:03 +0200
      Re: GUI-Update über Swing-EDT Patrick Roemer <sangamon@netcologne.de> - 2016-07-15 22:43 +0200
        Re: GUI-Update über Swing-EDT "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-15 23:18 +0200
        Re: GUI-Update über Swing-EDT "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-16 15:24 +0200
          Re: GUI-Update über Swing-EDT Patrick Roemer <sangamon@netcologne.de> - 2016-07-16 16:42 +0200
            Re: GUI-Update über Swing-EDT "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-16 23:05 +0200
              Re: GUI-Update über Swing-EDT "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-17 16:02 +0200

csiph-web