13brane's holidazed cavalcade of code shenanigans: the flavors of FizzBuzz - Day 0

 

(Full source code for the entire series will be progressively published here, and the other days of the cavalcade are here: 1-8, 9-16, 17-24)

Hello intertubes! It’s that time of the year again. The time of ugly sweaters, sun washed decorations you forgot to remove last year (after February 1st there is no excuse, really), and that awkward hibernation HR departments and LinkedIn recruiters alike go into because of some thing which relates to fiscal years and budgets or what is it.

Nothing fundamentally wrong with that, but then you don’t get to berate us unprofessionals for not being available to start a new position in say, July or August. Just sayin’.

The hibernation period will eventually, like all things periodical, come to an end and the hordes of talent seekers will come out of the dungeons, ready to ask you to complete a 40h+ coding assignment or worse, trying to assess your programming might with FizzBuzzian techniques.

So how do you prepare for this apocalypse?

In the case of the forty hour idiocy, best is to ignore or tell the recruiters to get some sense. Especially if you have a full time job already. If you are currently in between jobs, you may opt for a different approach: perform the coding assignment if you feel like it, but make the code available under the following license:

Copyright © YEAR AUTHOR. All rights reserved.

A non-transferable, non-exclusive license is hereby being granted to COMPANY, hereafter referred to as THE LICENSEE, to peruse and scrutinize the code but only for the porpoises (LOL) of assessing the viability of a potential work partnership opportunity, which is, at this point, pretty low, since you guys have no respect for anyone’s time. THE LICENSEE must not attempt run the code in any way, including, but not limited to, feeding the code into an interpreter, compiling into an executable or otherwise producing object or runnable machine code. This includes static and dynamic linking. In other words, you are going to read this, fuckers. Tests and everything.

For the twist after the stab, add:

For all other entities, the conditions of the MIT License apply.

IANAL, you do this at your own risk. This approach is totally untested, so don’t be surprised if you can’t reenter the industry after you hit send. You’ve been warned.

What about The Fizz? What do we do with that?

We double down. You ask for “an implementation of FizzBuzz” and I say “which variant”?

Of course, there is no point in solving the same problem over and over again. So my obviously flawless plan is as follows:

  1. Implement as many versions of FizzBuzz as I can preemptively, for shits and giggles.
  2. Receive request to implement FizzBuzz.
  3. Deflect request Matrix style by directing requester to code repository.
  4. Be a smug prick.

And this is the whole point of the cavalcade. Is it possible to do 24 versions of The Fizz and turn it into a sort of deranged advent calendar thing? Or will my meager software developing capacity falter before the third sunset?

Day 0: spec and test harness

The uninitiated to the software development practice would probably commit some or all of the following three unforgivable offenses:

  • Not having a clear spec before starting development;
  • Not having acceptance tests reflecting the spec;
  • Starting at 1. We do not allow heresy here.

So, without further ado, here they are. The common version of the spec:

Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.

C2Wiki

A revised, more formal, but functionally equivalent and test friendly spec:

The function FizzBuzz
takes in a positive integer n
and returns a string s
where s is

  • the string “FizzBuzz”, when n is both a multiple of 3 and a multiple of 5
  • the string “Fizz”, when n is a multiple of 3 but not a multiple of 5
  • the string “Buzz”, when n is a multiple of 5 but not a multiple of 3
  • the decimal representation of the number n in all other cases

Instead of doing the normal iterate and print in the main program, as that would be trivial anyway, we are going to test if our implementations produce correct outputs for all input values in the range 1-100 instead.

In this case, the acceptance test is the following:

package day00_test

import (
	"fmt"
	"testing"

	// we'll add the implementations here...
	"gitlab.com/carlosdavidepto/cavalcade-2018/day01"
)

func TestAllImplementations(t *testing.T) {
	implementations := []func(int) string{
		// ... and here.
		day01.FizzBuzz,
	}

	for _, FizzBuzzImpl := range implementations {
		testMultiplesOf3and5OutputFizzBuzz(t, FizzBuzzImpl)
		testMultiplesOf3OutputFizz(t, FizzBuzzImpl)
		testMultiplesOf5OutputBuzz(t, FizzBuzzImpl)
		testEveryOtherValueOutputsInputConvertedToString(t, FizzBuzzImpl)
	}
}

func testMultiplesOf3and5OutputFizzBuzz(t *testing.T, FizzBuzz func(int) string) {
	for i := 15; i <= 100; i = i + 15 {
		// 3 and 5 are the only prime factors of 15
		// therefore, all multiples of 3 and 5 are also multiples of 15
		if FizzBuzz(i) != "fizzbuzz" {
			t.Errorf("Expected %v, got %v in FizzBuzz(%v)", "fizzbuzz", FizzBuzz(i), i)
		}
	}
}

func testMultiplesOf3OutputFizz(t *testing.T, FizzBuzz func(int) string) {
	for i := 3; i <= 100; i = i + 3 {
		if i%5 != 0 {
			// ignore multiples of 3 which are also multiples of 5 here
			// they are tested in the first case
			if FizzBuzz(i) != "fizz" {
				t.Errorf("Expected %v, got %v in FizzBuzz(%v)", "fizz", FizzBuzz(i), i)
			}
		}
	}
}

func testMultiplesOf5OutputBuzz(t *testing.T, FizzBuzz func(int) string) {
	for i := 5; i <= 100; i = i + 5 {
		if i%3 != 0 {
			// ignore multiples of 5 which are also multiples of 3 here
			// they are tested in the first case
			if FizzBuzz(i) != "buzz" {
				t.Errorf("Expected %v, got %v in FizzBuzz(%v)", "buzz", FizzBuzz(i), i)
			}
		}
	}
}

func testEveryOtherValueOutputsInputConvertedToString(t *testing.T, FizzBuzz func(int) string) {
	for i := 1; i <= 100; i++ {
		if i%3 != 0 && i%5 != 0 {
			// ignore multiples of 5 and multiples of 3 here
			// they are tested in the other cases
			if FizzBuzz(i) != fmt.Sprint(i) {
				t.Errorf("Expected %v, got %v in FizzBuzz(%v)", fmt.Sprint(i), FizzBuzz(i), i)
			}
		}
	}
}

Tune in in a few days for more Fizz, this time with actual working Buzzes. Tomorrow is day 1.