exercism/go/simple-cipher/simple_cipher_test.go

271 lines
6.8 KiB
Go
Raw Normal View History

2016-09-12 22:41:46 +00:00
// For Step 1, implement the Caesar cipher, which seems clear enough except
// maybe for figuring out whether to add or subtract.
//
// For Step 2, implement a shift cipher, like Caesar except the shift amount
// is specified as an int. (Each letter of "ddddddddddddddddd", is 'd';
// 'd'-'a' == 3, so the corresponding shift amount is 3.)
//
// Steps 2 and 3 seem to be describing the Vigenère cipher (Google it)
// so let's do that too. The random thing, don't worry about. There is
// no test for that.
//
// API:
//
// type Cipher interface {
// Encode(string) string
// Decode(string) string
// }
// NewCaesar() Cipher
// NewShift(int) Cipher
// NewVigenere(string) Cipher
//
// Interface Cipher is in file cipher.go.
//
// Argument for NewShift must be in the range 1 to 25 or -1 to -25.
// Zero is disallowed. For invalid arguments NewShift returns nil.
//
// Argument for NewVigenere must consist of lower case letters a-z only.
// Values consisting entirely of the letter 'a' are disallowed.
// For invalid arguments NewVigenere returns nil.
package cipher
import (
"fmt"
"testing"
)
// type for testing cipher encoding alone, without requiring any text prep.
type prepped struct {
pt string // prepped text == decoded plain text
ct string // cipher text
}
// +3? -3? Positions vary. Your code just has to pass the tests.
var caesarPrepped = []prepped{
{"iamapandabear", "ldpdsdqgdehdu"},
{"programmingisawesome", "surjudpplqjlvdzhvrph"},
{"todayisholiday", "wrgdblvkrolgdb"},
{"venividivici", "yhqlylglylfl"},
}
func TestCaesarPrepped(t *testing.T) {
c := NewCaesar()
for _, test := range caesarPrepped {
if enc := c.Encode(test.pt); enc != test.ct {
t.Fatalf("Caesar Encode(%q) = %q, want %q.", test.pt, enc, test.ct)
}
}
}
// type for testing implementations of the Cipher interface
type cipherTest struct {
source string // source text, any UTF-8
cipher string // cipher text, result of Encode(st)
plain string // decoded plain text, result of Decode(ct)
}
var caesarTests = []cipherTest{
{"Go, go, gophers", "jrjrjrskhuv", "gogogophers"},
{"I am a panda bear.", "ldpdsdqgdehdu", "iamapandabear"},
{"Programming is AWESOME!", "surjudpplqjlvdzhvrph", "programmingisawesome"},
{"today is holiday", "wrgdblvkrolgdb", "todayisholiday"},
{"Twas the night before Christmas",
"wzdvwkhqljkwehiruhfkulvwpdv",
"twasthenightbeforechristmas"},
{"venividivici", "yhqlylglylfl", "venividivici"},
{" -- @#!", "", ""},
{"", "", ""},
}
func TestCaesar(t *testing.T) {
testCipher("Caesar", NewCaesar(), caesarTests, t)
}
func testCipher(name string, c Cipher, tests []cipherTest, t *testing.T) {
for _, test := range tests {
if enc := c.Encode(test.source); enc != test.cipher {
t.Fatalf("%s Encode(%q) = %q, want %q.",
name, test.plain, enc, test.cipher)
}
if dec := c.Decode(test.cipher); dec != test.plain {
t.Fatalf("%s Decode(%q) = %q, want %q.",
name, test.cipher, dec, test.plain)
}
}
}
var NSATests = []cipherTest{
{"THE ENEMY IS NEAR", "qebbkbjvfpkbxo", "theenemyisnear"},
{"SPIES SEND SECRET MESSAGES",
"pmfbppbkapbzobqjbppxdbp",
"spiessendsecretmessages"},
{"THOMAS JEFFERSON DESIGNED A SUBSTITUTION CIPHER",
"qeljxpgbccboplkabpfdkbaxprypqfqrqflkzfmebo",
"thomasjeffersondesignedasubstitutioncipher"},
{"the quick brown fox jumps over the lazy dog",
"qebnrfzhyoltkclugrjmplsboqebixwvald",
"thequickbrownfoxjumpsoverthelazydog"},
}
func TestShift(t *testing.T) {
// test shift(3) against Caesar cases.
c := NewShift(3)
if c == nil {
t.Fatal("NewShift(3) returned nil, want non-nil Cipher")
}
testCipher("Shift(3)", c, caesarTests, t)
// NSA and WP say Caesar uses shift of -3
c = NewShift(-3)
if c == nil {
t.Fatal("NewShift(-3) returned nil, want non-nil Cipher")
}
testCipher("Shift(-3)", c, NSATests, t)
// invalid shifts
for _, s := range []int{-27, -26, 0, 26, 27} {
if NewShift(s) != nil {
t.Fatal("NewShift(%d) returned non-nil, "+
"Want nil return for invalid argument.", s)
}
}
}
var vtests = []struct {
key string
tests []cipherTest
}{
{"lemon", []cipherTest{{"ATTACKATDAWN", "lxfopvefrnhr", "attackatdawn"}}},
{"abcdefghij", []cipherTest{
{"aaaaaaaaaa", "abcdefghij", "aaaaaaaaaa"},
{"zzzzzzzzzz", "zabcdefghi", "zzzzzzzzzz"},
}},
{"iamapandabear", []cipherTest{
{"I am a panda bear.", "qayaeaagaciai", "iamapandabear"},
}},
{"duxrceqyaimciuucnelkeoxjhdyduu", []cipherTest{
{"Diffie Hellman", "gccwkixcltycv", "diffiehellman"},
}},
{"qgbvno", []cipherTest{
{"cof-FEE, 123!", "sugars", "coffee"},
}},
}
func TestVigenere(t *testing.T) {
for _, test := range vtests {
v := NewVigenere(test.key)
if v == nil {
t.Fatalf("NewVigenere(%q) returned nil, want non-nil Cipher",
test.key)
}
testCipher(fmt.Sprintf("Vigenere(%q)", test.key), v, test.tests, t)
}
// invalid keys
for _, k := range []string{"", "a", "aa", "no way", "CAT", "3", "and,"} {
if NewVigenere(k) != nil {
t.Fatalf("NewVigenere(%q) returned non-nil, "+
"Want nil return for invalid argument.", k)
}
}
}
// Benchmark combined time to run all tests.
// Note other ciphers test different data; times cannot be compared.
func BenchmarkEncodeCaesar(b *testing.B) {
c := NewCaesar()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range caesarTests {
c.Encode(test.source)
}
}
}
func BenchmarkDecodeCaesar(b *testing.B) {
c := NewCaesar()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range caesarTests {
c.Decode(test.cipher)
}
}
}
func BenchmarkNewShift(b *testing.B) {
for i := 0; i < b.N; i++ {
for s := -27; s <= 27; s++ {
NewShift(s)
}
}
}
func BenchmarkEncodeShift(b *testing.B) {
s := NewShift(5)
all := append(caesarTests, NSATests...)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range all {
s.Encode(test.source)
}
}
}
func BenchmarkDecodeShift(b *testing.B) {
s := NewShift(5)
all := append(caesarTests, NSATests...)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range all {
s.Decode(test.cipher)
}
}
}
func BenchmarkNewVigenere(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range vtests {
NewVigenere(test.key)
}
}
}
func BenchmarkEncVigenere(b *testing.B) {
v := make([]Cipher, len(vtests))
for i, test := range vtests {
v[i] = NewVigenere(test.key)
if v[i] == nil {
b.Skip("Benchmark requires valid Vigenere test cases")
}
}
b.ResetTimer()
for j := 0; j < b.N; j++ {
for i, test := range vtests {
vi := v[i]
for _, test := range test.tests {
vi.Encode(test.source)
}
}
}
}
func BenchmarkDecVigenere(b *testing.B) {
v := make([]Cipher, len(vtests))
for i, test := range vtests {
v[i] = NewVigenere(test.key)
if v[i] == nil {
b.Skip("Benchmark requires valid Vigenere test cases")
}
}
b.ResetTimer()
for j := 0; j < b.N; j++ {
for i, test := range vtests {
vi := v[i]
for _, test := range test.tests {
vi.Decode(test.cipher)
}
}
}
}