Thread overview
Problem with assertThrown
Sep 09
kookman
Sep 10
kookman
Sep 10
kookman
September 09

I'm having trouble understanding why the assertThrown in unit test 5 is not behaving in the code below:

ubyte[] decodeBase32(string encoded) {
    import std.string: indexOf, stripRight;

    // Remove padding if present
    encoded = encoded.stripRight("=");

    ubyte[] result;
    size_t bitBuffer = 0;
    int bitBufferLen = 0;

    foreach (char c; encoded) {
        auto index = base32Alphabet.indexOf(c);
        if (index == -1)
            throw new Exception("Invalid character in base32 string");

        bitBuffer = (bitBuffer << 5) | index;
        bitBufferLen += 5;

        while (bitBufferLen >= 8) {
            bitBufferLen -= 8;
            result ~= cast(ubyte)((bitBuffer >> bitBufferLen) & 0xFF);
        }
    }

    return result;
}

unittest {
    import std.algorithm.comparison: equal;
    import std.string: representation;
    import std.stdio: writeln;
    writeln("Testing new implementation:");
    // Test case 1: Basic "Hello world" example
    string encoded = "JBSWY3DPEB3W64TMMQ======";
    auto expected = "Hello world".representation;
    ubyte[] result = decodeBase32(encoded);
    assert(result.equal(expected), "Test case 1 failed: 'Hello World' decoding");

    // Test case 2: Empty string should return an empty array
    writeln("Test case 2: Empty string should return an empty array");
    encoded = "";
    expected = [];
    result = decodeBase32(encoded);
    assert(result == expected, "Test case 2 failed: Empty string decoding");

    // Test case 3: "foobar" in Base32
    writeln("Test case 3: 'foobar' in Base32");
    encoded = "MZXW6YTBOI======";
    expected = [102, 111, 111, 98, 97, 114]; // "foobar"
    result = decodeBase32(encoded);
    assert(result == expected, "Test case 3 failed: 'foobar' decoding");

    import std.exception: assertThrown;
    // Test case 4: Test with padding in the middle (invalid)
    writeln("Test case 4: Test with padding in the middle (invalid)");
    assertThrown(decodeBase32("JBSWY=3DPEB======"),
        "Test case 4 failed: Invalid input with padding in the middle should throw");

    // Test case 5: Invalid character in input string
    writeln("Test case 5: Invalid character in input string");
    // '@' is not a valid Base32 character
    try {
        result = decodeBase32("JBSWY3DP@B3W64TMMQ");
    } catch (Exception e) {
        writeln("case 5 passed really..., exception msg was: ", e.msg);
    }
    // for some reason the below fails, giving an assert error (ie, no exception thrown)
    assertThrown(decodeBase32("JBSWY3DP@B3W64TMMQ"),
        "Test case 5 failed: Invalid character should throw an exception");
}

When I compile with unit tests on, I get this output:

Testing new implementation:
Test case 2: Empty string should return an empty array
Test case 3: 'foobar' in Base32
Test case 4: Test with padding in the middle (invalid)
Test case 5: Invalid character in input string
case 5 passed really..., exception msg was: Invalid character in base32 string
core.exception.AssertError@src/encoding.d(283): Assertion failure

It seems like assertThrown works as expected for case 4, but mysteriously not working for case 5 - despite the code under test raising the same exception. Am I missing something stupid here?

September 09
On Monday, September 9, 2024 5:46:18 PM MDT kookman via Digitalmars-d-learn wrote:
> It seems like assertThrown works as expected for case 4, but mysteriously not working for case 5 - despite the code under test raising the same exception. Am I missing something stupid here?

At a glance, it looks like the part of case 5 which explicitly catches the Exception and the part that uses assertThrown should behave the same, but your example doesn't actually compile (you didn't include a definition for base32Alphabet), so it's not actually possible to reproduce your problem.

- Jonathan M Davis



September 10
On Tuesday, 10 September 2024 at 00:27:43 UTC, Jonathan M Davis wrote:
> On Monday, September 9, 2024 5:46:18 PM MDT kookman via Digitalmars-d-learn wrote:
>> It seems like assertThrown works as expected for case 4, but mysteriously not working for case 5 - despite the code under test raising the same exception. Am I missing something stupid here?
>
> At a glance, it looks like the part of case 5 which explicitly catches the Exception and the part that uses assertThrown should behave the same, but your example doesn't actually compile (you didn't include a definition for base32Alphabet), so it's not actually possible to reproduce your problem.
>
> - Jonathan M Davis

Thanks Jonathan. Sorry I missed the excerpt for base32Alphabet - included below:

```
enum base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
```

September 09
On Monday, September 9, 2024 6:40:07 PM MDT kookman via Digitalmars-d-learn wrote:
> On Tuesday, 10 September 2024 at 00:27:43 UTC, Jonathan M Davis
>
> wrote:
> > On Monday, September 9, 2024 5:46:18 PM MDT kookman via
> >
> > Digitalmars-d-learn wrote:
> >> It seems like assertThrown works as expected for case 4, but mysteriously not working for case 5 - despite the code under test raising the same exception. Am I missing something stupid here?
> >
> > At a glance, it looks like the part of case 5 which explicitly catches the Exception and the part that uses assertThrown should behave the same, but your example doesn't actually compile (you didn't include a definition for base32Alphabet), so it's not actually possible to reproduce your problem.
> >
> > - Jonathan M Davis
>
> Thanks Jonathan. Sorry I missed the excerpt for base32Alphabet - included below:
>
> ```
> enum base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
> ```

When I run it locally, assertThrown passes as expected for test case 5, and the same happens on run.dlang.io, so nothing in my specific setup is making it pass when it normally wouldn't.

So, unless you verified that your example failed (and since you forgot to include that enum, I suspect you didn't), I would guess that whatever your problem is went away when you reduced the code to provide the example for your question.

I would note however, that the message for the AssertError that you provided
did not come from assertThrown, since while assertThrown does take the file
and line number from the caller, its message starts with
"assertThrown failed:", whereas your error message was the generic message
that you get from an assertion failure. So, whatever is going wrong in your
code, assertThrown is not determining that your code didn't throw and then
throwing an AssertError. It looks like an assertion on line #283 of whatever
code you actually ran (which is clearly longer than the example you
provided, since it's not that long) is failing.

- Jonathan M Davis



September 10
On Tuesday, 10 September 2024 at 01:04:15 UTC, Jonathan M Davis wrote:
>
> When I run it locally, assertThrown passes as expected for test case 5, and the same happens on run.dlang.io, so nothing in my specific setup is making it pass when it normally wouldn't.
>
> So, unless you verified that your example failed (and since you forgot to include that enum, I suspect you didn't), I would guess that whatever your problem is went away when you reduced the code to provide the example for your question.
>
> I would note however, that the message for the AssertError that you provided
> did not come from assertThrown, since while assertThrown does take the file
> and line number from the caller, its message starts with
> "assertThrown failed:", whereas your error message was the generic message
> that you get from an assertion failure. So, whatever is going wrong in your
> code, assertThrown is not determining that your code didn't throw and then
> throwing an AssertError. It looks like an assertion on line #283 of whatever
> code you actually ran (which is clearly longer than the example you
> provided, since it's not that long) is failing.
>
> - Jonathan M Davis

Ah! *(forehead slap)* Thank you! It was actually an assert in the immediately following unittest block failing (which I hadn't added a message to). I should have tried my reduced version before posting.

Thanks,
Kookman