What is the problem with Atbash tests?

Well, I’ve written the program to solve it, and all tests look like the following:

=== RUN   TestAtbash/encode_yes

    atbash_cipher_test.go:10: Atbash('yes'): expected 'bvh', actual 'bvh'

--- FAIL: TestAtbash/encode_yes (0.00s)
=== RUN   TestAtbash/encode_no

    atbash_cipher_test.go:10: Atbash('no'): expected 'ml', actual 'ml'

--- FAIL: TestAtbash/encode_no (0.00s)
=== RUN   TestAtbash/encode_all_the_letters

    atbash_cipher_test.go:10: Atbash('The quick brown fox jumps over the lazy dog.'): expected 'gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt', actual 'gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt'

--- FAIL: TestAtbash/encode_all_the_letters (0.00s)

What the hell is going here? I have “expected” the same as “actual” in all cases, and yet every test fails. The code is:

package atbash

func Atbash(s string) string {
    var ob = make([]byte, len(s)*6/5)
    var group int
	for _, b := range []byte(s) {
        if (b>='a') && (b<='z') {
            ob = append(ob, 25+'a'-b+'a')
        } else if (b>='A') && (b<='Z') {
        	ob = append(ob, 25+'A'-b+'a')
        } else if (b>='0') && (b<='9') {
        	ob = append(ob, b)
        } else {
        	group -= 1
        }

        group += 1
    	if group == 5 {
            group = 0
            ob = append(ob, ' ')
        }
    }
	if ob[len(ob)-1] == ' ' {
        ob = ob[:len(ob)-1]
    }
	return string(ob)
}

types - How to convert byte array to string in Go - Stack Overflow suggests you might need

return string(ob[:])

No, that didn’t helped.

The test portion in the tests looks like this:

actual := Atbash(tc.phrase)
if actual != tc.expected {
    t.Errorf("Atbash('%s'): expected '%s', actual '%s'", tc.phrase, tc.expected, actual)
}

I suspected that the comparison in the test (!=) is probably “too deep” and inspects the underlying arrays (no matter how stupid is the idea to compare strings this way). But no way I was able to manipulate my string before returning it so it works. I even tried to implement this string(ob[:]) myself, the dumb way:

	var q = make([]byte, len(ob))
    for i, c := range ob {
        q[i] = c
    }
	return string(q)

Also imported fmt:

	return fmt.Sprintf("%v", string(ob))

None of that helped either. The test output is the same: visually the expected and actual are the same, yet tests fail.

I think it might be important to note I’m using web version of Exercism.

Changing L4 to this fixes it:

var ob = []rune{}

I wonder if you have some extra bytes left over that are not visible in the test output.

Well, even []byte{} works. make([]byte, len(s)) and make([]rune, len(s)) does not. And now I get why.

Yes, there is a whole lot of extra 0-bytes in the beginning. Totally invisible. I wanted to allocate array big enough so it doesn’t need to do reallocation, but accidentally set length of the slice to that size, instead of zero. What I was intended to write is make([]byte, 0, 6*len(s)/5); notice the extra 0 argument in the middle.

Solved!

Yeah, byte and rune are identical.

Glad you solved it :slight_smile: