0

I'm currently unit testing some code to save and restore data for a game. My current save files are just text files. My unit tests save files, then load them and compare the results to the original.

Right now I'm having issues on several of the tests where Java's Scanner class, which I'm using to read the save files, will return the empty string when the line of the file its reading is not empty. After returning "", the scanner doesn't move to the next line, it instead reads the line it should have read when it returned "". My code expects an int, but it reads a string from the file instead, and throws an exception.

The example is an ItemContainer class (like a backpack for the player). The code calls a loop to load each item contained in the ItemContainer, and it successfully loads the first item, then this bug shows itself on the second iteration of the loop.

public class UnitTest {
    @Test
    public void testItemContainerContainsCorrectItemsAfterSaveLoad(){
        try {

            ItemContainer saved = Inventory.getStartingBackpack();
            Weapon wpn1 = new Weapon(1, 1, 1, 1);
            wpn1.description = "this is weapon number one";
            wpn1.name = "weapon one";
            Weapon wpn2 = new Weapon(2, 2, 2, 2);
            wpn2.name = "weapon two";
            wpn2.description = "this is weapon number two";
            WornItem clothing = new WornItem(10, 10, 10, 2, 0);
            clothing.name = "clothing";
            clothing.description = "this is clothing";

            saved.addItem(wpn1);
            saved.addItem(wpn2);
            saved.addItem(clothing);

            PrintWriter writer = new PrintWriter(filePath);
            saved.saveToFile(writer);
            writer.close();

            Scanner scanner = new Scanner(new File(filePath));
            ItemContainer loaded = ItemContainer.loadAlpha0_1(scanner);
            scanner.close();

            Field field = ItemContainer.class.getDeclaredField("items");
            field.setAccessible(true);
            ArrayList<Item> savedItems = ((ArrayList<Item>) field.get(saved));
            ArrayList<Item> loadedItems = ((ArrayList<Item>) field.get(loaded));

            for (Item item : savedItems){
                assertTrue(loadedItems.contains(item));
            }

        } catch (FileNotFoundException e) {
            fail("threw exception");
        } catch (NoSuchFieldException e) {
            fail("threw exception");
        } catch (SecurityException e) {
            fail("threw exception");
        } catch (IllegalArgumentException e) {
            fail("threw exception");
        } catch (IllegalAccessException e) {
            fail("threw exception");
        }   
    }
}
public class ItemContainer {
    public static ItemContainer loadAlpha0_1(Scanner scanner){
        int concealmentHits, concealability, spaceUsed, weightContained, baseSpace, baseWeight;
        String name = scanner.nextLine();
        concealmentHits = scanner.nextInt();
        concealability = scanner.nextInt();
        spaceUsed = scanner.nextInt();
        weightContained = scanner.nextInt();
        baseSpace = scanner.nextInt();
        baseWeight = scanner.nextInt();
        ArrayList<Item> items = loadItemsAlpha0_1(scanner);
        return new ItemContainer(baseWeight, baseSpace, name, items);
    }
    private static ArrayList<Item> loadItemsAlpha0_1(Scanner scanner) {
        ArrayList<Item> items = new ArrayList<Item>();
//      scanner.nextLine();
        // This is my bandaid fix for an unexplained blank line "" that keeps being read
        // at certain points in the code.
        scanner.nextLine();
        String itemType = scanner.nextLine();
        while(!itemType.equals("end item container")) {
            Item item = Item.loadAlpha0_1(scanner, itemType);
            items.add(item);
            itemType = scanner.nextLine(); // this line is when the bug mentioned shows up
        }
        return items;
    }
}
public class Weapon extends Item {
    public static Weapon loadAlpha0_1(Scanner scanner) {
        int size, weight, durability, hardness, damage, weaponType, parry, accuracy, might;
        String name = scanner.nextLine();
        size = scanner.nextInt();
        weight = scanner.nextInt();
        durability = scanner.nextInt();
        hardness = scanner.nextInt();
        damage = scanner.nextInt();
        String description = Item.loadItemDescriptionAlpha0_1(scanner);
        weaponType = scanner.nextInt();
        parry = scanner.nextInt();
        accuracy = scanner.nextInt();
        might = scanner.nextInt();
        Weapon weapon =  new Weapon(size, weight, durability, hardness, damage,
                weaponType, parry, accuracy, might);
        weapon.name = name;
        weapon.description = description;
        return weapon;
    }
}

this is the file that is being read during the test

backpack
0
0
50
50
1000
10
weapon
weapon one
15
15
125
5
0
this is weapon number one
end description
1
1
1
1
weapon
weapon two
25
25
150
5
0
this is weapon number two
end description
2
2
2
2
worn item
clothing
10
10
10
2
0
end item container

I know it's a lot of code to share, but I've been trying to figure this out for a very long time and I haven't been able to.

Edit: I found a bug that I created while messing around and trying to debug this. I've corrected it.

icaughtfireonce
  • 171
  • 1
  • 10
  • 1
    *"I know it's a lot of code to share, but (...)"* - Consider posting a [mcve] instead, then. – XenoRo Dec 25 '15 at 21:53
  • I only posted the code that seemed necessary to me. The unit test, and the code it's testing, and the file. I'm not really sure what could be taken out. If I understood what was causing this problem better, maybe I could take more out...but if I understood why this was happening I wouldn't be here. – icaughtfireonce Dec 25 '15 at 21:59
  • But you can understand better why this is happening by using a debugger, something that is included in most all modern IDE's and which are pretty easy to learn to use. I do suggest that you consider doing this, for one it will likely help you solve this puzzle, or two, if you don't solve it, you will be able to ask a much more specific question with less code, but more relevant code. – Hovercraft Full Of Eels Dec 25 '15 at 22:01
  • I have used the Eclipse debugger. I know the line of code where the bug is showing up right now (I've changed a few things and the spot where the bug shows up has moved, but it's still the same bug. Scanner.nextLine() returns "" instead of the line that it should return, and doesn't move to the next line. I just don't understand why it should do that. This isn't the only place this bug has manifested either, this is just the example I'm working on now. (All having to do with saving/loading different classes from files). – icaughtfireonce Dec 25 '15 at 22:06
  • I think I've just figured a sort-of fix for this. I think scanner.next() would ignore the erroneous new lines, but it would also mean that names could not contain spaces (or would have to use stand-in characters for names). That would not be ideal, because names are something that would be visible within the game by the user (ie. a weapon called "Flaming Sword" would be invalid if I used this 'fix'). – icaughtfireonce Dec 25 '15 at 22:13
  • Your problem looks to be that you're not adequately handling the end-of-line token. Please see the [duplicate](http://stackoverflow.com/questions/13102045/skipping-nextline-after-using-next-nextint-or-other-nextfoo-methods) used to close this question. Also, have a look at [my answer to this similar question](http://stackoverflow.com/questions/27141183/scanner-nextline-occasionally-skips-input). – Hovercraft Full Of Eels Dec 25 '15 at 22:15
  • So, what I'm getting from this is that you need to call Scanner.nextLine() after calling Scanner.nextInt() in order to move to the next line? So whenever I'm expecting a string and the last thing I called was Scanner.nextInt(), I need to call Scanner.nextLine and ignore the result to get it set back up for reading strings again? – icaughtfireonce Dec 26 '15 at 01:54

0 Answers0