Technology definitions loader

* Added "dummy" data file for technologies (for now it simply copies the
old, line-based technologies) and corresponding XML schema

* Added missing SQL stored procedure to clear all dependencies and
reverse dependencies from a technology

* Added import classes, loader and import tool for the technology graph

* Added tech graph import tool to post-build tests
This commit is contained in:
Emmanuel BENOîT 2012-02-27 20:04:02 +01:00
parent c5464212bc
commit 1f3c7a9202
24 changed files with 1731 additions and 43 deletions

View file

@ -1,7 +1,11 @@
package com.deepclone.lw.cli.xmlimport;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
@ -11,7 +15,6 @@ import org.junit.Test;
import com.deepclone.lw.cli.xmlimport.data.DataImportException;
import com.deepclone.lw.cli.xmlimport.data.i18n.I18NText;
import com.deepclone.lw.cli.xmlimport.data.i18n.LanguageDefinition;
import com.deepclone.lw.cli.xmlimport.data.i18n.StringDefinition;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.io.StreamException;
@ -129,22 +132,8 @@ public class TestI18NLoader
assertNotNull( text );
int lCount = 0;
for ( LanguageDefinition ld : text ) {
assertEquals( "test" , ld.getId( ) );
assertEquals( "test" , ld.getName( ) );
int tCount = 0;
for ( StringDefinition sd : ld ) {
assertEquals( "test" , sd.getId( ) );
try {
assertEquals( "test" , sd.getString( ).trim( ) );
} catch ( DataImportException e ) {
fail( "could not load string: " + e.getMessage( ) );
}
tCount++;
}
assertEquals( 1 , tCount );
for ( @SuppressWarnings( "unused" )
LanguageDefinition ld : text ) {
lCount++;
}
assertEquals( 1 , lCount );

View file

@ -14,7 +14,6 @@ import org.junit.Test;
import com.deepclone.lw.cli.xmlimport.data.DataImportException;
import com.deepclone.lw.cli.xmlimport.data.resources.BasicResource;
import com.deepclone.lw.cli.xmlimport.data.resources.NaturalResource;
import com.deepclone.lw.cli.xmlimport.data.resources.Resources;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.io.StreamException;
@ -127,27 +126,8 @@ public class TestResourceLoader
// Not sure if this is the best way to code for the two different resource types...
int rCount = 0;
for ( BasicResource br : resources ) {
if ( rCount == 0 ) {
assertEquals( "money" , br.getName( ) );
assertEquals( "moneyDescription" , br.getDescription( ) );
assertEquals( new Integer( 0 ) , br.getWeight( ) );
assertEquals( null , br.getCategory( ) );
} else if ( rCount == 1 ) {
// This isn't retarded is it?
NaturalResource nr = (NaturalResource) br;
assertEquals( "titanium" , nr.getName( ) );
assertEquals( "titaniumDescription" , nr.getDescription( ) );
assertEquals( new Integer( 1 ) , nr.getWeight( ) );
assertEquals( "minerals" , nr.getCategory( ) );
assertEquals( new Double( 0.8 ) , nr.getPresenceProbability( ) );
assertEquals( new Double( 5000 ) , nr.getQuantity( ).getAverage( ) );
assertEquals( new Double( 1500 ) , nr.getQuantity( ).getDeviation( ) );
assertEquals( new Double( 0.1 ) , nr.getDifficulty( ).getAverage( ) );
assertEquals( new Double( 0.05 ) , nr.getDifficulty( ).getDeviation( ) );
assertEquals( new Double( 0.4 ) , nr.getRecovery( ).getAverage( ) );
assertEquals( new Double( 0.05 ) , nr.getRecovery( ).getDeviation( ) );
}
for ( @SuppressWarnings( "unused" )
BasicResource br : resources ) {
rCount++;
}
assertEquals( 2 , rCount );

View file

@ -0,0 +1,132 @@
package com.deepclone.lw.cli.xmlimport;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import org.junit.Test;
import com.deepclone.lw.cli.xmlimport.data.DataImportException;
import com.deepclone.lw.cli.xmlimport.data.techs.Technologies;
import com.deepclone.lw.cli.xmlimport.data.techs.Technology;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.io.StreamException;
/**
* Unit tests for the {@link TechnologyLoader} class.
*
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
*/
public class TestTechnologyLoader
{
/**
* Try initialising the loader with a <code>null</code> file instance.
*
* @throws NullPointerException
* when the constructor is executed
*/
@Test( expected = NullPointerException.class )
public void testNullFile( )
throws NullPointerException
{
new TechnologyLoader( null );
}
/** Try loading a file that does not exist */
@Test
public void testMissingFile( )
{
TechnologyLoader loader = new TechnologyLoader( new File( "does-not-exist" ) );
try {
loader.load( );
} catch ( DataImportException e ) {
assertTrue( "cause is a" + e.getCause( ).getClass( ).getName( ) , e.getCause( ) instanceof IOException );
return;
}
fail( "no exception after trying to load a missing file" );
}
/** Try loading a file that contains something that is not XML. */
@Test
public void testBadXML( )
{
TechnologyLoader loader = new TechnologyLoader( new File( "TestFiles/technology-loader/bad-xml.xml" ) );
try {
loader.load( );
} catch ( DataImportException e ) {
assertTrue( "cause is a " + e.getCause( ).getClass( ).getName( ) , e.getCause( ) instanceof StreamException );
return;
}
fail( "no exception after loading stuff that isn't XML" );
}
/**
* Test loading a file that contains XML but which cannot be deserialised to a
* {@link Technologies} instance.
*/
@Test
public void testBadContents( )
{
TechnologyLoader loader = new TechnologyLoader( new File( "TestFiles/technology-loader/bad-contents.xml" ) );
try {
loader.load( );
} catch ( DataImportException e ) {
assertTrue( "cause is a " + e.getCause( ).getClass( ).getName( ) ,
e.getCause( ) instanceof ConversionException );
return;
}
fail( "no exception after loading bad XML" );
}
/**
* Try loading a file that contains valid XML for a {@link Technologies} instance with semantic
* errors.
*/
@Test
public void testBadData( )
{
TechnologyLoader loader = new TechnologyLoader( new File( "TestFiles/technology-loader/bad-data.xml" ) );
try {
loader.load( );
} catch ( DataImportException e ) {
assertNull( e.getCause( ) );
return;
}
fail( "no exception after loading bad data" );
}
/** Try loading valid data, make sure that it contains one record */
@Test
public void testGoodData( )
{
TechnologyLoader loader = new TechnologyLoader( new File( "TestFiles/technology-loader/good-data.xml" ) );
Technologies technologies;
try {
technologies = loader.load( );
} catch ( DataImportException e ) {
fail( "could not load valid file" );
return;
}
assertNotNull( technologies );
int rCount = 0;
for ( @SuppressWarnings( "unused" )
Technology tech : technologies ) {
rCount++;
}
assertEquals( 1 , rCount );
}
}

View file

@ -22,7 +22,7 @@ import com.thoughtworks.xstream.XStreamException;
*
*/
abstract public class BaseTest
abstract class BaseTest
{
/**
* Escape &amp;, &lt; and &gt; in XML strings

View file

@ -0,0 +1,175 @@
package com.deepclone.lw.cli.xmlimport.data.techs;
import com.deepclone.lw.cli.xmlimport.data.ImportableData;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.XStreamException;
/**
* Base class for testing technology importation
*
* <p>
* This class is used as a parent for tests of the technology import structures. It includes the
* code used to actually create these technology definitions, as this can normally only be done
* through XStream.
*
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
*
*/
abstract class BaseTest
{
/**
* Escape &amp;, &lt; and &gt; in XML strings
*
* @param string
* the string to escape
*
* @return the escaped string
*/
private String xmlString( String string )
{
return string.replace( "&" , "&amp;" ).replace( "<" , "&lt;" ).replace( ">" , "&gt;" );
}
/**
* Escape &amp;, &lt; and &gt;, ' and " in XML strings
*
* @param string
* the string to escape
*
* @return the escaped string
*/
private String quoteString( String string )
{
return "\"" + this.xmlString( string ).replace( "\"" , "&quot;" ).replace( "'" , "&apos;" ) + "\"";
}
/**
* Create the XML code for a technology definition
*
* <p>
* This method generates the XML code for a technology definition, which can then be imported
* using XStream into a {@link Technology} instance. It is capable of generating instances with
* various fields set to <code>null</code>.
*
* @param name
* identifier of the technology's name string
* @param category
* identifier of the technology's category string
* @param discovery
* identifier of the technology's discovery string
* @param description
* identifier of the technology's description string
* @param cost
* monetary cost of implementing the technology
* @param points
* amount of research points required
* @param dependencies
* dependencies as an array of strings
* @return
*/
protected String createTechnology( String name , String category , String discovery , String description ,
Long cost , Long points , String... dependencies )
{
StringBuilder builder = new StringBuilder( "<technology" );
if ( name != null ) {
builder.append( " name=" ).append( this.quoteString( name ) );
}
if ( category != null ) {
builder.append( " category=" ).append( this.quoteString( category ) );
}
if ( discovery != null ) {
builder.append( " discovery=" ).append( this.quoteString( discovery ) );
}
if ( description != null ) {
builder.append( " description=" ).append( this.quoteString( description ) );
}
if ( cost != null ) {
builder.append( " cost=\"" ).append( cost ).append( "\"" );
}
if ( points != null ) {
builder.append( " points=\"" ).append( points ).append( "\"" );
}
if ( dependencies.length == 0 ) {
builder.append( " />" );
} else {
builder.append( ">" );
for ( String dep : dependencies ) {
builder.append( "<depends-on>" );
if ( dep != null ) {
builder.append( this.xmlString( dep ) );
}
builder.append( "</depends-on>" );
}
builder.append( "</technology>" );
}
return builder.toString( );
}
/**
* Creates the XML code for a top-level technology definition element
*
* @param resources
* XML definitions of technologies
*
* @return the top-level element's XML code
*/
protected String createTopLevel( String... technologies )
{
StringBuilder str = new StringBuilder( );
str.append( "<lw-tech-graph>" );
for ( String technology : technologies ) {
str.append( technology );
}
str.append( "</lw-tech-graph>" );
return str.toString( );
}
/**
* Create the necessary XStream instance
*
* <p>
* Initialise an XStream instance and set it up so it can process technology data definitions.
* This includes annotation processing, of course, but also generating an alias allowing the
* &lt;technology&gt; tag to be recognised on its own.
*/
private XStream createXStreamInstance( )
{
XStream xstream = new XStream( );
xstream.processAnnotations( Technologies.class );
xstream.alias( "technology" , Technology.class );
return xstream;
}
/**
* Create a resource object from its XML code
*
* @param xml
* the XML code
* @param cls
* the class of the object
*
* @return the object that was created from the code
*
* @throws ClassCastException
* if the code corresponds to some other type of object
* @throws XStreamException
* if some error occurred while deserialising the object
*/
protected < T extends ImportableData > T createObject( String xml , Class< T > cls )
throws ClassCastException , XStreamException
{
return cls.cast( this.createXStreamInstance( ).fromXML( xml ) );
}
}

View file

@ -0,0 +1,96 @@
package com.deepclone.lw.cli.xmlimport.data.techs;
import static org.junit.Assert.*;
import org.junit.Test;
import com.deepclone.lw.cli.xmlimport.data.DataImportException;
/**
* Unit tests for the {@link Technologies} class
*
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
*/
public class TestTechnologies
extends BaseTest
{
/**
* Test loading an empty list of technologies
*
* <p>
* {@link Technologies#iterator()} will throw {@link NullPointerException} when there are no
* definitions. Use that to determine if it is empty.
*/
@Test( expected = NullPointerException.class )
public void testEmpty( )
{
String defs = this.createTopLevel( );
Technologies techs = this.createObject( defs , Technologies.class );
techs.iterator( );
}
/**
* Test loading a list of technologies
*/
@Test
public void testTechs( )
{
String tDef = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L );
String defs = this.createTopLevel( tDef );
Technologies techs = this.createObject( defs , Technologies.class );
int i = 0;
for ( Technology technology : techs ) {
assertNotNull( technology );
assertEquals( 0 , i );
i++;
}
}
/**
* Test verifying an empty list of technologies
*/
@Test( expected = DataImportException.class )
public void testVerifyEmpty( )
throws DataImportException
{
String defs = this.createTopLevel( );
Technologies techs = this.createObject( defs , Technologies.class );
techs.verifyData( );
}
/**
* Test verifying a list of technologies with an invalid technology in it
*/
@Test( expected = DataImportException.class )
public void testVerifyInvalid( )
throws DataImportException
{
String tDef = this.createTechnology( null , "category" , "discovery" , "description" , 12L , 34L );
String defs = this.createTopLevel( tDef );
Technologies techs = this.createObject( defs , Technologies.class );
techs.verifyData( );
}
/**
* Test verifying a valid list of technologies
*/
@Test
public void testVerifyOk( )
throws DataImportException
{
String tDef = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L );
String defs = this.createTopLevel( tDef );
Technologies techs = this.createObject( defs , Technologies.class );
techs.verifyData( );
}
}

View file

@ -0,0 +1,329 @@
package com.deepclone.lw.cli.xmlimport.data.techs;
import static org.junit.Assert.*;
import org.junit.Test;
import com.deepclone.lw.cli.xmlimport.data.DataImportException;
/**
* Test the {@link Technology} class
*
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
*/
public class TestTechnology
extends BaseTest
{
/**
* Test loading a definition with no dependencies
*/
@Test
public void testLoadNoDeps( )
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
assertNotNull( technology );
assertEquals( "name" , technology.getName( ) );
assertEquals( "category" , technology.getCategory( ) );
assertEquals( "discovery" , technology.getDiscovery( ) );
assertEquals( "description" , technology.getDescription( ) );
assertEquals( new Long( 12 ) , technology.getCost( ) );
assertEquals( new Long( 34 ) , technology.getPoints( ) );
assertNotNull( technology.getDependencies( ) );
assertTrue( technology.getDependencies( ).isEmpty( ) );
}
/**
* Test loading a definition with dependencies
*/
@Test
public void testLoadDeps( )
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L ,
"dep1" , "dep2" );
Technology technology = this.createObject( definition , Technology.class );
assertNotNull( technology );
assertEquals( "name" , technology.getName( ) );
assertEquals( "category" , technology.getCategory( ) );
assertEquals( "discovery" , technology.getDiscovery( ) );
assertEquals( "description" , technology.getDescription( ) );
assertEquals( new Long( 12 ) , technology.getCost( ) );
assertEquals( new Long( 34 ) , technology.getPoints( ) );
assertNotNull( technology.getDependencies( ) );
assertEquals( 2 , technology.getDependencies( ).size( ) );
assertEquals( "dep1" , technology.getDependencies( ).get( 0 ) );
assertEquals( "dep2" , technology.getDependencies( ).get( 1 ) );
}
/**
* Test {@link Technology#verifyData()} when the name is null
*/
@Test( expected = DataImportException.class )
public void testVerifyNameNull( )
throws DataImportException
{
String definition = this.createTechnology( null , "category" , "discovery" , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the name is empty
*/
@Test( expected = DataImportException.class )
public void testVerifyNameEmpty( )
throws DataImportException
{
String definition = this.createTechnology( "" , "category" , "discovery" , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the name consists of spaces only
*/
@Test( expected = DataImportException.class )
public void testVerifyNameSpaces( )
throws DataImportException
{
String definition = this.createTechnology( " " , "category" , "discovery" , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the category is null
*/
@Test( expected = DataImportException.class )
public void testVerifyCategoryNull( )
throws DataImportException
{
String definition = this.createTechnology( "name" , null , "discovery" , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the category is empty
*/
@Test( expected = DataImportException.class )
public void testVerifyCategoryEmpty( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "" , "discovery" , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the category consists of spaces only
*/
@Test( expected = DataImportException.class )
public void testVerifyCategorySpaces( )
throws DataImportException
{
String definition = this.createTechnology( "name" , " " , "discovery" , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the discovery string is null
*/
@Test( expected = DataImportException.class )
public void testVerifyDiscoveryNull( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , null , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the discovery string is empty
*/
@Test( expected = DataImportException.class )
public void testVerifyDiscoveryEmpty( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "" , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the discovery string consists of spaces only
*/
@Test( expected = DataImportException.class )
public void testVerifyDiscoverySpaces( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , " " , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the description is null
*/
@Test( expected = DataImportException.class )
public void testVerifyDescriptionNull( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , null , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the description is empty
*/
@Test( expected = DataImportException.class )
public void testVerifyDescriptionEmpty( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the description consists of spaces only
*/
@Test( expected = DataImportException.class )
public void testVerifyDescriptionSpaces( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , " " , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the cost is null
*/
@Test( expected = DataImportException.class )
public void testVerifyCostNull( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , null , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the cost is 0
*/
@Test( expected = DataImportException.class )
public void testVerifyCostZero( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 0L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the amount of points is null
*/
@Test( expected = DataImportException.class )
public void testVerifyPointsNull( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , null );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when the amount of points is 0
*/
@Test( expected = DataImportException.class )
public void testVerifyPointsZero( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 0L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} on a valid definition with no dependencies
*/
@Test
public void testVerifyValidNoDeps( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when a dependency is empty
*/
@Test( expected = DataImportException.class )
public void testVerifyDepsEmpty( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L , "" );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} when a dependency consists of spaces only
*/
@Test( expected = DataImportException.class )
public void testVerifyDepsSpaces( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L ,
" " );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
/**
* Test {@link Technology#verifyData()} on a valid definition with dependencies
*/
@Test
public void testVerifyValidDeps( )
throws DataImportException
{
String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L ,
"dep1" , "dep2" );
Technology technology = this.createObject( definition , Technology.class );
technology.verifyData( );
}
}