Merge branch 'master' into story_61

Also got client-server handshake working. Multiple clients can connect and control their own boat.
The client's boat has a larger black triangle drawn around it. Probably needs tidying up.
Added isClientBoat boolean to VisualiserBoat.

WindGenerator is now an interface, implemented by ConstantWindGenerator (useful for testing) and RandomWindGenerator. Added ConstantWindGeneratorTest.
RandomWindGenerator was formerly WindGenerator.
The WindGenerator is passed in to MockRace.

CommandFactory throws a CommandConstructionException if it cannot create a command.

MessageSerialiser and MessageDeserialiser correctly terminate on error.

Readded VisualiserInput's switch statement in the main loop, pending a refactor.

Removed the sleep statement from LobbyController - it was blocking javaFX thread.

Lobby.fxml: moved buttons and text input into grid panes, so they don't break when you resize screen.

Added some test object creation functions in several test classes (MockRaceTest.createMockRace(), BoatXMLReaderTest.createBoatDataSource(), etc...).

#story[1095]
This commit is contained in:
fjc40
2017-08-10 23:49:21 +12:00
45 changed files with 1320 additions and 556 deletions
@@ -14,6 +14,13 @@ import static org.junit.Assert.fail;
public class PolarParserTest {
public static Polars createPolars() {
Polars polars = PolarParser.parse("mock/polars/acc_polars.csv");
return polars;
}
/**
* Tests if we can parse a valid polar data file (stored in a string), and create a polar table.
*/
@@ -0,0 +1,72 @@
package mock.model;
import org.junit.Before;
import org.junit.Test;
import shared.model.Bearing;
import shared.model.Wind;
import static org.junit.Assert.*;
/**
* Tests the {@link ConstantWindGenerator}.
*/
public class ConstantWindGeneratorTest {
WindGenerator windGenerator;
Bearing windBearing;
double windSpeedKnots;
@Before
public void setUp() throws Exception {
windBearing = Bearing.fromDegrees(78.5);
windSpeedKnots = 18.54;
windGenerator = new ConstantWindGenerator(windBearing, windSpeedKnots);
}
/**
* Tests if the {@link WindGenerator#generateBaselineWind()} function works.
*/
@Test
public void generateBaselineTest() {
int repetitions = 100;
for (int i = 0; i < repetitions; i++) {
Wind wind = windGenerator.generateBaselineWind();
assertEquals(windBearing.degrees(), wind.getWindDirection().degrees(), 0.01);
assertEquals(windSpeedKnots, wind.getWindSpeed(), 0.01);
}
}
/**
* Tests if the {@link WindGenerator#generateNextWind(Wind)} ()} function works.
*/
@Test
public void generateNextWindTest() {
int repetitions = 100;
Wind wind = windGenerator.generateBaselineWind();
for (int i = 0; i < repetitions; i++) {
wind = windGenerator.generateNextWind(wind);
assertEquals(windBearing.degrees(), wind.getWindDirection().degrees(), 0.01);
assertEquals(windSpeedKnots, wind.getWindSpeed(), 0.01);
}
}
}
@@ -1,7 +1,44 @@
package mock.model;
import mock.dataInput.PolarParserTest;
import network.Messages.LatestMessages;
import shared.dataInput.*;
import shared.exceptions.InvalidBoatDataException;
import shared.exceptions.InvalidRaceDataException;
import shared.exceptions.InvalidRegattaDataException;
import shared.model.Bearing;
import shared.model.Constants;
import static org.junit.Assert.*;
public class MockRaceTest {
//TODO
/**
* Creates a MockRace for use in testing.
* Has a constant wind generator.
* @return MockRace for testing.
* @throws InvalidBoatDataException
* @throws InvalidRaceDataException
* @throws InvalidRegattaDataException
*/
public static MockRace createMockRace() throws InvalidBoatDataException, InvalidRaceDataException, InvalidRegattaDataException {
BoatDataSource boatDataSource = BoatXMLReaderTest.createBoatDataSource();
RaceDataSource raceDataSource = RaceXMLReaderTest.createRaceDataSource();
RegattaDataSource regattaDataSource = RegattaXMLReaderTest.createRegattaDataSource();
LatestMessages latestMessages = new LatestMessages();
Polars polars = PolarParserTest.createPolars();
WindGenerator windGenerator = new ConstantWindGenerator(Bearing.fromDegrees(230), 10);
MockRace mockRace = new MockRace(boatDataSource, raceDataSource, regattaDataSource, latestMessages, polars, Constants.RaceTimeScale, windGenerator);
return mockRace;
}
}
@@ -8,10 +8,10 @@ import shared.model.Wind;
import static org.junit.Assert.*;
public class WindGeneratorTest {
public class RandomWindGeneratorTest {
private WindGenerator windGenerator;
private RandomWindGenerator randomWindGenerator;
private Bearing windBaselineBearing;
private Bearing windBearingLowerBound;
@@ -36,7 +36,7 @@ public class WindGeneratorTest {
this.windSpeedLowerBound = 7;
this.windSpeedUpperBound = 20;
this.windGenerator = new WindGenerator(
this.randomWindGenerator = new RandomWindGenerator(
windBaselineBearing,
windBearingLowerBound,
windBearingUpperBound,
@@ -55,7 +55,7 @@ public class WindGeneratorTest {
@Test
public void generateBaselineWindTest() {
Wind wind = windGenerator.generateBaselineWind();
Wind wind = randomWindGenerator.generateBaselineWind();
assertEquals(windBaselineSpeed, wind.getWindSpeed(), speedKnotsEpsilon);
assertEquals(windBaselineBearing.degrees(), wind.getWindDirection().degrees(), bearingDegreeEpsilon);
@@ -72,7 +72,7 @@ public class WindGeneratorTest {
for (int i = 0; i < randomWindCount; i++) {
Wind wind = windGenerator.generateRandomWind();
Wind wind = randomWindGenerator.generateRandomWind();
assertTrue(wind.getWindSpeed() >= windSpeedLowerBound);
assertTrue(wind.getWindSpeed() <= windSpeedUpperBound);
@@ -91,13 +91,13 @@ public class WindGeneratorTest {
@Test
public void generateNextWindTest() {
Wind wind = windGenerator.generateBaselineWind();
Wind wind = randomWindGenerator.generateBaselineWind();
int randomWindCount = 1000;
for (int i = 0; i < randomWindCount; i++) {
wind = windGenerator.generateNextWind(wind);
wind = randomWindGenerator.generateNextWind(wind);
assertTrue(wind.getWindSpeed() >= windSpeedLowerBound);
assertTrue(wind.getWindSpeed() <= windSpeedUpperBound);
@@ -1,31 +1,72 @@
package mock.model.commandFactory;
import mock.exceptions.CommandConstructionException;
import mock.model.MockBoat;
import mock.model.MockRace;
import mock.model.MockRaceTest;
import network.Messages.BoatAction;
import network.Messages.Enums.BoatActionEnum;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import shared.exceptions.InvalidBoatDataException;
import shared.exceptions.InvalidRaceDataException;
import shared.exceptions.InvalidRegattaDataException;
import shared.model.Bearing;
import shared.model.Boat;
import shared.model.Race;
import visualiser.model.VisualiserRace;
import static org.mockito.Mockito.when;
import static org.testng.Assert.*;
import static org.mockito.Mockito.mock;
/**
* Created by connortaylorbrown on 4/08/17.
*/
public class WindCommandTest {
private Race race;
private Boat boat;
private MockRace race;
private MockBoat boat;
private Command upwind;
private Command downwind;
private double initial;
private double offset = 3.0;
@Before
public void setUp() {
boat = new Boat(0, "Bob", "NZ");
public void setUp() throws CommandConstructionException, InvalidBoatDataException, InvalidRegattaDataException, InvalidRaceDataException {
race = MockRaceTest.createMockRace();
boat = race.getBoats().get(0);
//when(race.getWindDirection()).thenReturn(Bearing.fromDegrees(0.0));
boat.setBearing(Bearing.fromDegrees(45.0));
BoatAction upwindAction = new BoatAction(BoatActionEnum.UPWIND);
upwindAction.setSourceID(boat.getSourceID());
BoatAction downwindAction = new BoatAction(BoatActionEnum.DOWNWIND);
downwindAction.setSourceID(boat.getSourceID());
upwind = CommandFactory.createCommand(race, upwindAction);
downwind = CommandFactory.createCommand(race, downwindAction);
initial = boat.getBearing().degrees();
}
/**
* Ensure the difference between initial and final angle is 3 degrees
*/
@Test
public void upwindCommandDecreasesAngle() {
upwind.execute();
assertEquals(initial - boat.getBearing().degrees(), -offset, 1e-5);
}
@Test
public void upwindCommandDecreasesAngle() {
public void downwindCommandIncreasesAngle() {
downwind.execute();
assertEquals(initial - boat.getBearing().degrees(), offset, 1e-5);
}
}
}
@@ -31,31 +31,40 @@ public class BoatXMLReaderTest {
private List<Mark> marks;
@Before
public void setUp() {
public static BoatDataSource createBoatDataSource() throws InvalidBoatDataException {
String boatXMLString = null;
try {
boatXMLString = XMLReader.readXMLFileToString("mock/mockXML/boatTest.xml", StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
fail("could not read boat xml file into a string");
} catch (TransformerException | XMLReaderException e) {
throw new InvalidBoatDataException("Could not read boat XML file into a string.", e);
}
try {
boatData = new BoatXMLReader(boatXMLString, XMLFileType.Contents);
BoatDataSource boatData = new BoatXMLReader(boatXMLString, XMLFileType.Contents);
return boatData;
boats = new ArrayList<>(boatData.getBoats().values());
marks = new ArrayList<>(boatData.getMarkerBoats().values());
} catch (XMLReaderException | InvalidBoatDataException e) {
e.printStackTrace();
assertTrue(false);
} catch (XMLReaderException e) {
throw new InvalidBoatDataException("Could not parse boat XML file.", e);
}
}
@Before
public void setUp() throws InvalidBoatDataException {
boatData = createBoatDataSource();
boats = new ArrayList<>(boatData.getBoats().values());
marks = new ArrayList<>(boatData.getMarkerBoats().values());
}
@Test
@@ -1,5 +1,39 @@
package shared.dataInput;
import shared.enums.XMLFileType;
import shared.exceptions.InvalidRaceDataException;
import shared.exceptions.XMLReaderException;
import javax.xml.transform.TransformerException;
import java.nio.charset.StandardCharsets;
public class RaceXMLReaderTest {
//TODO
public static RaceDataSource createRaceDataSource() throws InvalidRaceDataException {
String raceXMLString = null;
try {
raceXMLString = XMLReader.readXMLFileToString("mock/mockXML/raceTest.xml", StandardCharsets.UTF_8);
} catch (TransformerException | XMLReaderException e) {
throw new InvalidRaceDataException("Could not read race XML file into a string.", e);
}
try {
RaceXMLReader raceData = new RaceXMLReader(raceXMLString, XMLFileType.Contents);
return raceData;
} catch (XMLReaderException e) {
throw new InvalidRaceDataException("Could not parse race XML file.", e);
}
}
}
@@ -3,6 +3,11 @@ package shared.dataInput;
import org.junit.Before;
import org.junit.Test;
import shared.enums.XMLFileType;
import shared.exceptions.InvalidRegattaDataException;
import shared.exceptions.XMLReaderException;
import javax.xml.transform.TransformerException;
import java.nio.charset.StandardCharsets;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -14,6 +19,34 @@ import static org.junit.Assert.fail;
public class RegattaXMLReaderTest {
private RegattaXMLReader regatta;
public static RegattaDataSource createRegattaDataSource() throws InvalidRegattaDataException {
String regattaXMLString = null;
try {
regattaXMLString = XMLReader.readXMLFileToString("mock/mockXML/regattaTest.xml", StandardCharsets.UTF_8);
} catch (TransformerException | XMLReaderException e) {
throw new InvalidRegattaDataException("Could not read regatta XML file into a string.", e);
}
try {
RegattaDataSource raceData = new RegattaXMLReader(regattaXMLString, XMLFileType.Contents);
return raceData;
} catch (XMLReaderException e) {
throw new InvalidRegattaDataException("Could not parse regatta XML file.", e);
}
}
@Before
public void findFile() {
try {