Debugging help with Java for #OW9

I’m stuck on Stage #ow9.

The test case is weird, it tries to use a file that it isn’t there but expects my solution to magically know the metainfo file announce key vaue

Here I’ll show you the logs:

remote: [tester::#OW9] Running tests for Stage #OW9 (Parse torrent file)
remote: [tester::#OW9] Running ./your_bittorrent.sh info /tmp/torrents4252367964/codercat.gif.torrent
remote: [tester::#OW9] Checking for tracker URL (Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce)
remote: [tester::#OW9] Expected stdout to contain "Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce", got: ""
remote: [tester::#OW9] Test failed

And here’s a snippet of my code:

public class Main {
    private static final Gson gson = new Gson();

    private static final Bencode bencode = new Bencode();

    public static void main(String[] args) throws Exception {

        String command = args[0];

        if ("decode".equals(command)) {
            String bencodedValue = args[1];
            Object decoded;
            try {
                decoded = decodeBencode(bencodedValue);
            } catch (RuntimeException e) {
                System.out.println(e.getMessage());
                return;
            }
            System.out.println(gson.toJson(decoded));
        } else if ("info".equals(args[0])) {
            readInfoFile(args[1]);
        } else {
            System.out.println("Unknown command: " + command);
        }
    }

    private static void readInfoFile(String pathString) {
        try {
            Path path = Paths.get(pathString);

            if (!Files.exists(path)) {
                return;
            }

            byte[] torrentBytesArray = Files.readAllBytes(path);

            final Map<String, Object> dict = bencode.decode(torrentBytesArray, Type.DICTIONARY);

            final Object url = dict.get("announce");
            if (url == null) {
                System.out.println("Error: 'announce' field not found in torrent file.");
            } else {
                System.out.printf("Tracker URL: %s\n", url);
            }

            final Map<String, Object> info = (Map<String, Object>) dict.get("info");

            if (info != null) {
                System.out.printf("Length: %s\n", info.get("length"));
            } else {
                System.out.println("Error: 'info' field not found in torrent file.");
            }

        } catch (Exception e) {
            e.printStackTrace(); // Print the exception for debugging
        }
    }


    // I could use a stack or dequeue for when I decide to implement my own bencode solution.
    static Object decodeBencode(String bencodedString) {
        if (Character.isDigit(bencodedString.charAt(0))) {
            return bencode.decode(bencodedString.getBytes(), Type.STRING);
        } else if (bencodedString.startsWith("i")) {
            return bencode.decode(bencodedString.getBytes(), Type.NUMBER);
        } else if (bencodedString.startsWith("l")) {
            return bencode.decode(bencodedString.getBytes(), Type.LIST);
        } else if (bencodedString.startsWith("d")) {
            return bencode.decode(bencodedString.getBytes(), Type.DICTIONARY);
        } else {
            throw new RuntimeException("Only strings are supported at the moment");
        }
    }
    
}

well, the test will run in their environment, so you didn’t need worry about file don’t exist in your environment.try output more info to see what’s not working well

2 Likes

Apparently it isn’t there because I get a “java.nio.file.NoSuchFileException”.

@JanInquisitor looks like you’ve got a !Files.exists(path), shouldn’t that have triggered an early return if the file doesn’t exist? Why are you getting an exception instead?

I’d also echo @rgbygv’s suggestion - try printing out the path value to make sure that args are being parsed correctly.

(Pretty sure the file exists btw, 1000s of users have passed this stage, and we haven’t changed the tester recently)

I changed the code and made it print the exception, I get a “java.nio.file.NoSuchFileException”. I’ve also printed the arguments goings into the program and I got the path file string, here it is:

remote: [tester::#OW9] Running tests for Stage #OW9 (Parse torrent file)
remote: [tester::#OW9] Running ./your_bittorrent.sh info /tmp/torrents770425335/itsworking.gif.torrent
remote: [your_program] /tmp/torrents770425335/itsworking.gif.torrent.
remote: [your_program] java.nio.file.NoSuchFileException: /tmp/torrents770425335/itsworking.gif.torrent.
remote: [your_program]  at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
remote: [your_program]  at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106)
remote: [your_program]  at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
remote: [your_program]  at java.base/sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:261)
remote: [your_program]  at java.base/java.nio.file.Files.newByteChannel(Files.java:379)
remote: [your_program]  at java.base/java.nio.file.Files.newByteChannel(Files.java:431)
remote: [your_program]  at java.base/java.nio.file.Files.readAllBytes(Files.java:3268)
remote: [your_program]  at Main.readInfoFile(Main.java:40)
remote: [your_program]  at Main.main(Main.java:28)
remote: [tester::#OW9] Checking for tracker URL (Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce)
remote: [tester::#OW9] Expected stdout to contain "Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce", got: "/tmp/torrents770425335/itsworking.gif.torrent.\n"
remote: [tester::#OW9] Test failed
remote: [your_program] [info, /tmp/torrents3409021111/congratulations.gif.torrent.]
remote: [your_program] File path doesn't exist.
remote: [tester::#OW9] Checking for tracker URL (Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce)
remote: [tester::#OW9] Expected stdout to contain "Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce", got: "[info, /tmp/torrents3409021111/congratulations.gif.torrent.]\nFile path doesn't exist.\n"
remote: [tester::#OW9] Test failed
/tmp/torrents770425335/itsworking.gif.torrent.

@JanInquisitor is the “.” at the end of the filename part of the filename? Or was that added in your log? Seems a bit suspicious that the same dot would also be present in the “NoSuchFileException” log:

java.nio.file.NoSuchFileException: /tmp/torrents770425335/itsworking.gif.torrent.

If that’s not the issue here, feel free to share your updated code and we’ll help figure this out!

I have no idea where that “.” is coming from.

Here is my code:

public class Main {
    private static final Gson gson = new Gson();

    private static final Bencode bencode = new Bencode();

    public static void main(String[] args) throws Exception {
        String command = args[0];

        if ("decode".equals(command)) {
            String bencodedValue = args[1];
            Object decoded;
            try {
                decoded = decodeBencode(bencodedValue);
            } catch (RuntimeException e) {
                System.out.println(e.getMessage());
                return;
            }
            System.out.println(gson.toJson(decoded));
        } else if ("info".equals(args[0])) {
            readInfoFile(args[1]);
        } else {
            System.out.println("Unknown command: " + command);
        }
    }

    private static void readInfoFile(String pathString) {
        try {
            Path path = Paths.get(pathString);

            byte[] torrentBytesArray = Files.readAllBytes(path);

            Map<String, Object> dict = bencode.decode(torrentBytesArray, Type.DICTIONARY);

            Object url = dict.get("announce");
            System.out.printf("Tracker URL: %s\n", url);

            Map<String, Object> info = (Map<String, Object>) dict.get("info");
            System.out.printf("Length: %s\n", info.get("length"));

        } catch (Exception e) {
            System.out.println(e);
        }
    }


    // I could use a stack or dequeue for when I decide to implement my own bencode solution.
    static Object decodeBencode(String bencodedString) {

        if (Character.isDigit(bencodedString.charAt(0))) {
            return bencode.decode(bencodedString.getBytes(), Type.STRING);
        } else if (bencodedString.startsWith("i")) {
            return bencode.decode(bencodedString.getBytes(), Type.NUMBER);
        } else if (bencodedString.startsWith("l")) {
            return bencode.decode(bencodedString.getBytes(), Type.LIST);
        } else if (bencodedString.startsWith("d")) {
            return bencode.decode(bencodedString.getBytes(), Type.DICTIONARY);
        } else {
            throw new RuntimeException("Only strings are supported at the moment");
        }

    }
}

Gotcha. So the problem definitely seems to be related to the extra dot. @andy1li should be able to take a closer look at this for you on Monday. @JanInquisitor If you figure out what’s wrong in the meantime, please do let us know!

The only way I could pass the test was modifying the method like this:

    private static void readInfoFile(String pathString) {
        if (pathString.endsWith(".")) {
            StringBuilder sb = new StringBuilder(pathString);
            pathString = sb.substring(0, sb.length() - 1);
        }

        Path path = Paths.get(pathString);

        try {
            byte[] torrentBytesArray = Files.readAllBytes(path);

            Map<String, Object> dict = bencode.decode(torrentBytesArray, Type.DICTIONARY);

            Object url = dict.get("announce");
            System.out.printf("Tracker URL: %s\n", url);

            Map<String, Object> info = (Map<String, Object>) dict.get("info");
            System.out.printf("Length: %s\n", info.get("length"));

        } catch (Exception e) {
            System.out.println(e);
        }
    }

@JanInquisitor Turns out there’s an extra dot at the end of the command in your_bittorrent.sh:

Removing it should resolve the issue with the trailing dot in the arguments.