added tests to repo
This commit is contained in:
@@ -9,10 +9,6 @@
|
||||
*.dylib
|
||||
binaries/
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
*_test.go
|
||||
|
||||
# Code coverage profiles and other test artifacts
|
||||
*.out
|
||||
coverage.*
|
||||
|
||||
@@ -0,0 +1,517 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestCalculateDice_BasicDiceRoll tests simple dice rolls
|
||||
func TestCalculateDice_BasicDiceRoll(t *testing.T) {
|
||||
result, diceRolls, err := CalculateDice("d20")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result < 1 || result > 20 {
|
||||
t.Errorf("d20 should roll 1-20, got %v", result)
|
||||
}
|
||||
if !strings.Contains(diceRolls, "1d20") {
|
||||
t.Errorf("diceRolls should contain '1d20', got %q", diceRolls)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_MultipleDice tests multiple dice
|
||||
func TestCalculateDice_MultipleDice(t *testing.T) {
|
||||
result, _, err := CalculateDice("2d6")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result < 2 || result > 12 {
|
||||
t.Errorf("2d6 should roll 2-12, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_AdditionWithDice tests dice with addition
|
||||
func TestCalculateDice_AdditionWithDice(t *testing.T) {
|
||||
result, _, err := CalculateDice("1d6+5")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result < 6 || result > 11 {
|
||||
t.Errorf("1d6+5 should roll 6-11, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_SubtractionWithDice tests dice with subtraction
|
||||
func TestCalculateDice_SubtractionWithDice(t *testing.T) {
|
||||
result, _, err := CalculateDice("2d10-5")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result < -3 || result > 15 {
|
||||
t.Errorf("2d10-5 should roll -3 to 15, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_MultiplicationWithDice tests dice with multiplication
|
||||
func TestCalculateDice_MultiplicationWithDice(t *testing.T) {
|
||||
result, _, err := CalculateDice("1d5*2")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result < 2 || result > 10 {
|
||||
t.Errorf("1d5*2 should roll 2-10, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_DivisionWithDice tests dice with division
|
||||
func TestCalculateDice_DivisionWithDice(t *testing.T) {
|
||||
result, _, err := CalculateDice("1d6/2")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result < 0 || result > 3 {
|
||||
t.Errorf("1d6/2 should roll 0-3, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_HighestDiceModifier tests the 'H' modifier
|
||||
func TestCalculateDice_HighestDiceModifier(t *testing.T) {
|
||||
// 2d20H should return only the highest die
|
||||
result, _, err := CalculateDice("2d20H")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result < 1 || result > 20 {
|
||||
t.Errorf("2d20H should roll 1-20 (highest of two d20s), got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_LowestDiceModifier tests the 'L' modifier
|
||||
func TestCalculateDice_LowestDiceModifier(t *testing.T) {
|
||||
// 2d20L should return only the lowest die
|
||||
result, _, err := CalculateDice("2d20L")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result < 1 || result > 20 {
|
||||
t.Errorf("2d20L should roll 1-20 (lowest of two d20s), got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_ComplexExpression tests complex mathematical expressions
|
||||
func TestCalculateDice_ComplexExpression(t *testing.T) {
|
||||
result, _, err := CalculateDice("2d6+3*2")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
// 2d6 (2-12) + 3*2 (6) = 8-18
|
||||
if result < 8 || result > 18 {
|
||||
t.Errorf("2d6+3*2 should roll 8-18, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_ParenthesesExpression tests expressions with parentheses
|
||||
func TestCalculateDice_ParenthesesExpression(t *testing.T) {
|
||||
result, _, err := CalculateDice("(1d4+2)*3")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
// (1d4+2)*3 = (3-6)*3 = 9-18
|
||||
if result < 9 || result > 18 {
|
||||
t.Errorf("(1d4+2)*3 should roll 9-18, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_MultipleAddends tests multiple addends
|
||||
func TestCalculateDice_MultipleAddends(t *testing.T) {
|
||||
result, _, err := CalculateDice("1d4+1d6+1d8")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
// 1d4 (1-4) + 1d6 (1-6) + 1d8 (1-8) = 3-18
|
||||
if result < 3 || result > 18 {
|
||||
t.Errorf("1d4+1d6+1d8 should roll 3-18, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_PrefixModifier tests prefix H/L modifiers
|
||||
func TestCalculateDice_PrefixModifier(t *testing.T) {
|
||||
result, _, err := CalculateDice("H2d20")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result < 1 || result > 20 {
|
||||
t.Errorf("H2d20 should roll 1-20 (highest of two d20s), got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_EmptyExpression tests error handling for empty expression
|
||||
func TestCalculateDice_EmptyExpression(t *testing.T) {
|
||||
_, _, err := CalculateDice("")
|
||||
if err == nil {
|
||||
t.Errorf("Empty expression should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_InvalidDiceCount tests error handling for invalid dice count
|
||||
func TestCalculateDice_InvalidDiceCount(t *testing.T) {
|
||||
_, _, err := CalculateDice("0d6")
|
||||
if err == nil {
|
||||
t.Errorf("0d6 should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_InvalidDiceSides tests error handling for invalid dice sides
|
||||
func TestCalculateDice_InvalidDiceSides(t *testing.T) {
|
||||
_, _, err := CalculateDice("1d0")
|
||||
if err == nil {
|
||||
t.Errorf("1d0 should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_PlaceholderDice tests error handling for placeholder dice
|
||||
func TestCalculateDice_PlaceholderDice(t *testing.T) {
|
||||
_, _, err := CalculateDice("dx")
|
||||
if err == nil {
|
||||
t.Errorf("dx should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_SimpleAddition tests simple addition
|
||||
func TestEvaluateMathExpression_SimpleAddition(t *testing.T) {
|
||||
result, err := evaluateMathExpression("5+3")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 8 {
|
||||
t.Errorf("5+3 should be 8, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_Subtraction tests subtraction
|
||||
func TestEvaluateMathExpression_Subtraction(t *testing.T) {
|
||||
result, err := evaluateMathExpression("10-3")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 7 {
|
||||
t.Errorf("10-3 should be 7, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_Multiplication tests multiplication
|
||||
func TestEvaluateMathExpression_Multiplication(t *testing.T) {
|
||||
result, err := evaluateMathExpression("4*5")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 20 {
|
||||
t.Errorf("4*5 should be 20, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_Division tests division
|
||||
func TestEvaluateMathExpression_Division(t *testing.T) {
|
||||
result, err := evaluateMathExpression("20/4")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 5 {
|
||||
t.Errorf("20/4 should be 5, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_OperatorPrecedence tests operator precedence (multiplication before addition)
|
||||
func TestEvaluateMathExpression_OperatorPrecedence(t *testing.T) {
|
||||
result, err := evaluateMathExpression("2+3*4")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 14 {
|
||||
t.Errorf("2+3*4 should be 14, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_Parentheses tests parentheses override precedence
|
||||
func TestEvaluateMathExpression_Parentheses(t *testing.T) {
|
||||
result, err := evaluateMathExpression("(2+3)*4")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 20 {
|
||||
t.Errorf("(2+3)*4 should be 20, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_Exponentiation tests exponentiation
|
||||
func TestEvaluateMathExpression_Exponentiation(t *testing.T) {
|
||||
result, err := evaluateMathExpression("2^3")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 8 {
|
||||
t.Errorf("2^3 should be 8, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_UnaryMinus tests unary minus
|
||||
func TestEvaluateMathExpression_UnaryMinus(t *testing.T) {
|
||||
result, err := evaluateMathExpression("-5+10")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 5 {
|
||||
t.Errorf("-5+10 should be 5, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_FloatingPoint tests floating point numbers
|
||||
func TestEvaluateMathExpression_FloatingPoint(t *testing.T) {
|
||||
result, err := evaluateMathExpression("3.5+2.5")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 6 {
|
||||
t.Errorf("3.5+2.5 should be 6, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_DivisionByZero tests division by zero error
|
||||
func TestEvaluateMathExpression_DivisionByZero(t *testing.T) {
|
||||
_, err := evaluateMathExpression("5/0")
|
||||
if err == nil {
|
||||
t.Errorf("Division by zero should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_MissingClosingParen tests missing closing parenthesis error
|
||||
func TestEvaluateMathExpression_MissingClosingParen(t *testing.T) {
|
||||
_, err := evaluateMathExpression("(2+3")
|
||||
if err == nil {
|
||||
t.Errorf("Missing closing parenthesis should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_InvalidCharacter tests invalid character error
|
||||
func TestEvaluateMathExpression_InvalidCharacter(t *testing.T) {
|
||||
_, err := evaluateMathExpression("5@3")
|
||||
if err == nil {
|
||||
t.Errorf("Invalid character should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_Whitespace tests whitespace handling
|
||||
func TestEvaluateMathExpression_Whitespace(t *testing.T) {
|
||||
result, err := evaluateMathExpression(" 5 + 3 ")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 8 {
|
||||
t.Errorf("' 5 + 3 ' should be 8, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_ComplexExpression tests complex mathematical expressions
|
||||
func TestEvaluateMathExpression_ComplexExpression(t *testing.T) {
|
||||
result, err := evaluateMathExpression("((2+3)*4-5)/3")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
// ((2+3)*4-5)/3 = (5*4-5)/3 = (20-5)/3 = 15/3 = 5
|
||||
if result != 5 {
|
||||
t.Errorf("((2+3)*4-5)/3 should be 5, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRollDiceSet_Range tests rollDiceSet produces values in correct range
|
||||
func TestRollDiceSet_Range(t *testing.T) {
|
||||
rolls := rollDiceSet(10, 20)
|
||||
if len(rolls) != 10 {
|
||||
t.Errorf("rollDiceSet(10, 20) should return 10 rolls, got %d", len(rolls))
|
||||
}
|
||||
for i, roll := range rolls {
|
||||
if roll < 1 || roll > 20 {
|
||||
t.Errorf("Roll %d is %d, expected 1-20", i, roll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestRollDiceSet_Variability tests rollDiceSet produces different values
|
||||
func TestRollDiceSet_Variability(t *testing.T) {
|
||||
rolls := rollDiceSet(100, 6)
|
||||
seenValues := make(map[int]bool)
|
||||
for _, roll := range rolls {
|
||||
seenValues[roll] = true
|
||||
}
|
||||
// With 100 rolls of 1d6, we should see multiple different values
|
||||
if len(seenValues) < 3 {
|
||||
t.Errorf("100 rolls of 1d6 should see at least 3 different values, got %d", len(seenValues))
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_OutputFormat tests the output format
|
||||
func TestCalculateDice_OutputFormat(t *testing.T) {
|
||||
_, diceRolls, err := CalculateDice("2d6")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
// Should contain information about the rolls
|
||||
if !strings.Contains(diceRolls, "2d6") {
|
||||
t.Errorf("diceRolls should contain '2d6', got %q", diceRolls)
|
||||
}
|
||||
if !strings.Contains(diceRolls, "(") || !strings.Contains(diceRolls, ")") {
|
||||
t.Errorf("diceRolls should be formatted with parentheses, got %q", diceRolls)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_PrefixAndSuffixModifiers tests that suffix modifier takes priority
|
||||
func TestCalculateDice_PrefixAndSuffixModifiers(t *testing.T) {
|
||||
// When both prefix and suffix modifiers are present, suffix should take priority
|
||||
result, _, err := CalculateDice("H2d20L")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
// Should use L (suffix) modifier, not H
|
||||
if result < 1 || result > 20 {
|
||||
t.Errorf("H2d20L should use L modifier and roll 1-20, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_LargeNumbers tests large number handling
|
||||
func TestEvaluateMathExpression_LargeNumbers(t *testing.T) {
|
||||
result, err := evaluateMathExpression("1000000+2000000")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 3000000 {
|
||||
t.Errorf("1000000+2000000 should be 3000000, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_ExponentiationPrecedence tests exponentiation precedence
|
||||
func TestEvaluateMathExpression_ExponentiationPrecedence(t *testing.T) {
|
||||
result, err := evaluateMathExpression("2+3^2")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
// 2 + (3^2) = 2 + 9 = 11
|
||||
if result != 11 {
|
||||
t.Errorf("2+3^2 should be 11, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_NestedParentheses tests nested parentheses
|
||||
func TestEvaluateMathExpression_NestedParentheses(t *testing.T) {
|
||||
result, err := evaluateMathExpression("((10))")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 10 {
|
||||
t.Errorf("((10)) should be 10, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_NegativeResult tests expressions that can produce negative results
|
||||
func TestCalculateDice_NegativeResult(t *testing.T) {
|
||||
result, _, err := CalculateDice("1d4-10")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
// 1d4 (1-4) - 10 = -9 to -6
|
||||
if result < -9 || result > -6 {
|
||||
t.Errorf("1d4-10 should roll -9 to -6, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestExpandDiceNotation_SingleDice tests expansion of single dice notation
|
||||
func TestExpandDiceNotation_SingleDice(t *testing.T) {
|
||||
// This test verifies that dice notation expands correctly
|
||||
// We can't test exact values due to randomness, but we can test the format
|
||||
expanded, diceRolls, err := expandDiceNotation("1d6")
|
||||
if err != nil {
|
||||
t.Fatalf("expandDiceNotation failed: %v", err)
|
||||
}
|
||||
// expanded should be a number between 1 and 6
|
||||
val, err := evaluateMathExpression(expanded)
|
||||
if err != nil {
|
||||
t.Fatalf("expanded result should be valid: %v", err)
|
||||
}
|
||||
if val < 1 || val > 6 {
|
||||
t.Errorf("1d6 should expand to 1-6, got %v", val)
|
||||
}
|
||||
if !strings.Contains(diceRolls, "1d6") {
|
||||
t.Errorf("diceRolls should contain '1d6', got %q", diceRolls)
|
||||
}
|
||||
}
|
||||
|
||||
// TestExpandDiceNotation_WithModifier tests expansion with H/L modifier
|
||||
func TestExpandDiceNotation_WithModifier(t *testing.T) {
|
||||
expanded, _, err := expandDiceNotation("2d20H")
|
||||
if err != nil {
|
||||
t.Fatalf("expandDiceNotation failed: %v", err)
|
||||
}
|
||||
val, err := evaluateMathExpression(expanded)
|
||||
if err != nil {
|
||||
t.Fatalf("expanded result should be valid: %v", err)
|
||||
}
|
||||
if val < 1 || val > 20 {
|
||||
t.Errorf("2d20H should expand to 1-20, got %v", val)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_PowerOperator tests the power/exponentiation operator
|
||||
func TestCalculateDice_PowerOperator(t *testing.T) {
|
||||
result, _, err := CalculateDice("2^3")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result != 8 {
|
||||
t.Errorf("2^3 should be 8, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_ZeroDice tests zero values
|
||||
func TestCalculateDice_ZeroDice(t *testing.T) {
|
||||
result, _, err := CalculateDice("0")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
if result != 0 {
|
||||
t.Errorf("0 should be 0, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_NestedExpression tests deeply nested expressions
|
||||
func TestEvaluateMathExpression_NestedExpression(t *testing.T) {
|
||||
result, err := evaluateMathExpression("(((2+3)))")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
if result != 5 {
|
||||
t.Errorf("(((2+3))) should be 5, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDice_MultipleOperations tests multiple different operations
|
||||
func TestCalculateDice_MultipleOperations(t *testing.T) {
|
||||
result, _, err := CalculateDice("1d4+2-1")
|
||||
if err != nil {
|
||||
t.Fatalf("CalculateDice failed: %v", err)
|
||||
}
|
||||
// 1d4 (1-4) + 2 - 1 = 2-5
|
||||
if result < 2 || result > 5 {
|
||||
t.Errorf("1d4+2-1 should roll 2-5, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateMathExpression_ChainedExponentiation tests chained exponentiation
|
||||
func TestEvaluateMathExpression_ChainedExponentiation(t *testing.T) {
|
||||
result, err := evaluateMathExpression("2^2^2")
|
||||
if err != nil {
|
||||
t.Fatalf("evaluateMathExpression failed: %v", err)
|
||||
}
|
||||
// Left-associative: (2^2)^2 = 4^2 = 16
|
||||
if result != 16 {
|
||||
t.Errorf("2^2^2 (left-associative) should be 16, got %v", result)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,706 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCalculateDiceStatistics_Multiplication(t *testing.T) {
|
||||
expression := "5d10*3"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Errorf("CalculateDiceStatistics(%q) failed: %v", expression, err)
|
||||
return
|
||||
}
|
||||
|
||||
// For 5d10, min is 5, max is 50.
|
||||
// With *3, min should be 15, max should be 150.
|
||||
if stats.MinValue != 15 {
|
||||
t.Errorf("Expected MinValue 15, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 150 {
|
||||
t.Errorf("Expected MaxValue 150, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_Basic(t *testing.T) {
|
||||
// 2d6 -> 2-12
|
||||
expression := "2d6"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 2 || stats.MaxValue != 12 {
|
||||
t.Errorf("Expected 2-12, got %d-%d", stats.MinValue, stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_Constant(t *testing.T) {
|
||||
// 5 + 3 -> 8
|
||||
expression := "5+3"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 8 || stats.MaxValue != 8 {
|
||||
t.Errorf("Expected 8-8, got %d-%d", stats.MinValue, stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_Mixed_ConstantMult(t *testing.T) {
|
||||
// 1d4 + 2 * 3 -> 1d4 + 6 -> 7-10
|
||||
expression := "1d4+2*3"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 7 || stats.MaxValue != 10 {
|
||||
t.Errorf("Expected 7-10, got %d-%d", stats.MinValue, stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_Mixed_DiceMult(t *testing.T) {
|
||||
// 1d4*2 + 3 -> (1..4)*2 + 3 -> {2,4,6,8} + 3 -> {5,7,9,11}
|
||||
expression := "1d4*2+3"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 5 {
|
||||
t.Errorf("Expected MinValue 5, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 11 {
|
||||
t.Errorf("Expected MaxValue 11, got %d", stats.MaxValue)
|
||||
}
|
||||
// Check that 6 is NOT a possible outcome (since outcomes are 5, 7, 9, 11)
|
||||
if _, exists := stats.Results[6]; exists {
|
||||
t.Errorf("Did not expect outcome 6 to exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_ParenthesisAndDiceMult(t *testing.T) {
|
||||
// (5d10+3)*d10
|
||||
// 5d10 ranges 5-50. +3 ranges 8-53.
|
||||
// d10 ranges 1-10.
|
||||
// Min: 8 * 1 = 8.
|
||||
// Max: 53 * 10 = 530.
|
||||
expression := "(5d10+3)*d10"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate %s: %v", expression, err)
|
||||
}
|
||||
if stats.MinValue != 8 {
|
||||
t.Errorf("Expected MinValue 8, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 530 {
|
||||
t.Errorf("Expected MaxValue 530, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_ComplexParentheses(t *testing.T) {
|
||||
// 2 * (1d4 + 1) -> 2 * {2,3,4,5} -> {4,6,8,10}
|
||||
expression := "2 * (1d4 + 1)"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 4 {
|
||||
t.Errorf("Expected MinValue 4, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 10 {
|
||||
t.Errorf("Expected MaxValue 10, got %d", stats.MaxValue)
|
||||
}
|
||||
if _, exists := stats.Results[5]; exists {
|
||||
t.Errorf("Outcome 5 should not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_ImplicitMult_Dice(t *testing.T) {
|
||||
// d203d10 -> 1d203 * 1d10
|
||||
// Min: 1 * 1 = 1
|
||||
// Max: 203 * 10 = 2030
|
||||
expression := "d203d10"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate %s: %v", expression, err)
|
||||
}
|
||||
if stats.MinValue != 1 {
|
||||
t.Errorf("Expected MinValue 1, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 2030 {
|
||||
t.Errorf("Expected MaxValue 2030, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_ImplicitMult_ConstantDice(t *testing.T) {
|
||||
// 2d10 -> 2 * d10 -> 2,4,6...20?
|
||||
// NO! 2d10 should be parsed as "2 dice of 10 sides".
|
||||
// The parser MUST prioritize dice notation over implicit multiplication.
|
||||
expression := "2d10"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// 2d10 is sum of 2 dice. Min 2, Max 20. All values 2..20 possible.
|
||||
if stats.MinValue != 2 {
|
||||
t.Errorf("Expected MinValue 2, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 20 {
|
||||
t.Errorf("Expected MaxValue 20, got %d", stats.MaxValue)
|
||||
}
|
||||
if _, exists := stats.Results[3]; !exists {
|
||||
t.Errorf("Expected outcome 3 to exist for 2d10")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_ImplicitMult_NumberDice(t *testing.T) {
|
||||
// 3 d10 -> 3 * d10
|
||||
expression := "3 d10"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 3 {
|
||||
t.Errorf("Expected MinValue 3, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 30 {
|
||||
t.Errorf("Expected MaxValue 30, got %d", stats.MaxValue)
|
||||
}
|
||||
if _, exists := stats.Results[4]; exists {
|
||||
t.Errorf("Did not expect outcome 4 to exist for 3 * d10")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_Division(t *testing.T) {
|
||||
// 1d6 / 2
|
||||
// Outcomes: 1/2=0, 2/2=1, 3/2=1, 4/2=2, 5/2=2, 6/2=3
|
||||
// Expected: 0, 1, 2, 3
|
||||
expression := "1d6/2"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 0 {
|
||||
t.Errorf("Expected MinValue 0, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 3 {
|
||||
t.Errorf("Expected MaxValue 3, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_Power(t *testing.T) {
|
||||
// 1d4 ^ 2
|
||||
// Outcomes: 1, 4, 9, 16
|
||||
expression := "1d4^2"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 1 {
|
||||
t.Errorf("Expected MinValue 1, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 16 {
|
||||
t.Errorf("Expected MaxValue 16, got %d", stats.MaxValue)
|
||||
}
|
||||
if _, exists := stats.Results[9]; !exists {
|
||||
t.Errorf("Expected outcome 9 to exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDiceStatistics_Decimal(t *testing.T) {
|
||||
// 2.5 + 2.5 = 2 + 2 = 4 (floor logic)
|
||||
expression := "2.5 + 2.5"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 4 {
|
||||
t.Errorf("Expected MinValue 4, got %d", stats.MinValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_SingleD20 tests a single d20 roll
|
||||
func TestCalculateDiceStatistics_SingleD20(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("d20")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 1 {
|
||||
t.Errorf("Expected MinValue 1, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 20 {
|
||||
t.Errorf("Expected MaxValue 20, got %d", stats.MaxValue)
|
||||
}
|
||||
// All outcomes should be equally likely (1/20 probability)
|
||||
expectedProb := 1.0 / 20.0
|
||||
for value := 1; value <= 20; value++ {
|
||||
prob, exists := stats.Results[value]
|
||||
if !exists {
|
||||
t.Errorf("Expected outcome %d to exist", value)
|
||||
}
|
||||
if prob < expectedProb-0.01 || prob > expectedProb+0.01 {
|
||||
t.Errorf("d20 outcome %d has probability %f, expected %f", value, prob, expectedProb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_AverageValue tests average calculation
|
||||
func TestCalculateDiceStatistics_AverageValue(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("1d6")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// Average of 1d6 should be 3.5
|
||||
expectedAvg := 3.5
|
||||
if stats.Average < expectedAvg-0.1 || stats.Average > expectedAvg+0.1 {
|
||||
t.Errorf("Expected average ~3.5, got %f", stats.Average)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_MostCommonValue tests most common outcome
|
||||
func TestCalculateDiceStatistics_MostCommonValue(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("2d6")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// Most common roll for 2d6 should be 7
|
||||
if stats.MostCommon != 7 {
|
||||
t.Errorf("Expected MostCommon 7, got %d", stats.MostCommon)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_SortedOutcomes tests sorted outcomes
|
||||
func TestCalculateDiceStatistics_SortedOutcomes(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("1d4")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
expected := []int{1, 2, 3, 4}
|
||||
if len(stats.SortedOutcomes) != len(expected) {
|
||||
t.Errorf("Expected %d outcomes, got %d", len(expected), len(stats.SortedOutcomes))
|
||||
}
|
||||
for i, outcome := range stats.SortedOutcomes {
|
||||
if outcome != expected[i] {
|
||||
t.Errorf("Expected outcome[%d] = %d, got %d", i, expected[i], outcome)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_Percentages tests percentage calculations
|
||||
func TestCalculateDiceStatistics_Percentages(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("d20")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// Each outcome should be 5% (1/20)
|
||||
expectedPercentage := 5.0
|
||||
for value := 1; value <= 20; value++ {
|
||||
pct, exists := stats.Percentages[value]
|
||||
if !exists {
|
||||
t.Errorf("Expected percentage for outcome %d", value)
|
||||
}
|
||||
if pct < expectedPercentage-0.1 || pct > expectedPercentage+0.1 {
|
||||
t.Errorf("d20 outcome %d has percentage %f%%, expected %f%%", value, pct, expectedPercentage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_TotalOutcomes tests total outcomes calculation
|
||||
func TestCalculateDiceStatistics_TotalOutcomes(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("2d6")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// 2d6 should have 6*6 = 36 total outcomes
|
||||
if stats.TotalOutcomes.Int64() != 36 {
|
||||
t.Errorf("Expected 36 total outcomes for 2d6, got %d", stats.TotalOutcomes.Int64())
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_Subtraction tests subtraction in statistics
|
||||
func TestCalculateDiceStatistics_Subtraction(t *testing.T) {
|
||||
expression := "1d6-1d4"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate %s: %v", expression, err)
|
||||
}
|
||||
// 1d6 (1-6) - 1d4 (1-4) gives range -3 to 5
|
||||
if stats.MinValue != -3 {
|
||||
t.Errorf("Expected MinValue -3, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 5 {
|
||||
t.Errorf("Expected MaxValue 5, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_MultiplyConstants tests multiplying constants
|
||||
func TestCalculateDiceStatistics_MultiplyConstants(t *testing.T) {
|
||||
expression := "3 * 4"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 12 || stats.MaxValue != 12 {
|
||||
t.Errorf("Expected 12-12, got %d-%d", stats.MinValue, stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_DiceMultiplication tests dice multiplication
|
||||
func TestCalculateDiceStatistics_DiceMultiplication(t *testing.T) {
|
||||
expression := "1d4 * 1d3"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// Min: 1*1 = 1, Max: 4*3 = 12
|
||||
if stats.MinValue != 1 {
|
||||
t.Errorf("Expected MinValue 1, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 12 {
|
||||
t.Errorf("Expected MaxValue 12, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_HighestModifier tests highest die modifier
|
||||
func TestCalculateDiceStatistics_HighestModifier(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("4d6H")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// 4d6H returns highest die (1-6)
|
||||
if stats.MinValue != 1 {
|
||||
t.Errorf("Expected MinValue 1, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 6 {
|
||||
t.Errorf("Expected MaxValue 6, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_LowestModifier tests lowest die modifier
|
||||
func TestCalculateDiceStatistics_LowestModifier(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("4d6L")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// 4d6L returns lowest die (1-6)
|
||||
if stats.MinValue != 1 {
|
||||
t.Errorf("Expected MinValue 1, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 6 {
|
||||
t.Errorf("Expected MaxValue 6, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_EmptyExpression tests error handling
|
||||
func TestCalculateDiceStatistics_EmptyExpression(t *testing.T) {
|
||||
_, err := CalculateDiceStatistics("")
|
||||
if err == nil {
|
||||
t.Errorf("Empty expression should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_InvalidDiceCount tests invalid dice count
|
||||
func TestCalculateDiceStatistics_InvalidDiceCount(t *testing.T) {
|
||||
_, err := CalculateDiceStatistics("0d6")
|
||||
if err == nil {
|
||||
t.Errorf("0d6 should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_InvalidDiceSides tests invalid dice sides
|
||||
func TestCalculateDiceStatistics_InvalidDiceSides(t *testing.T) {
|
||||
_, err := CalculateDiceStatistics("1d0")
|
||||
if err == nil {
|
||||
t.Errorf("1d0 should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_UnexpectedCharacter tests invalid character
|
||||
func TestCalculateDiceStatistics_UnexpectedCharacter(t *testing.T) {
|
||||
_, err := CalculateDiceStatistics("1d6@")
|
||||
if err == nil {
|
||||
t.Errorf("Invalid character should return error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_LargeNumberOfDice tests statistics with many dice
|
||||
func TestCalculateDiceStatistics_LargeNumberOfDice(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("10d6")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// 10d6 ranges from 10 to 60
|
||||
if stats.MinValue != 10 {
|
||||
t.Errorf("Expected MinValue 10, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 60 {
|
||||
t.Errorf("Expected MaxValue 60, got %d", stats.MaxValue)
|
||||
}
|
||||
// Average of 10d6 should be around 35 (10 * 3.5)
|
||||
expectedAvg := 35.0
|
||||
if stats.Average < expectedAvg-1 || stats.Average > expectedAvg+1 {
|
||||
t.Errorf("Expected average ~35, got %f", stats.Average)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_DiceDivision tests dice division
|
||||
func TestCalculateDiceStatistics_DiceDivision(t *testing.T) {
|
||||
expression := "1d10 / 2"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// 1d10 (1-10) / 2: floor division gives 0-5
|
||||
if stats.MinValue != 0 {
|
||||
t.Errorf("Expected MinValue 0, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 5 {
|
||||
t.Errorf("Expected MaxValue 5, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_NegativeResults tests expressions with negative results
|
||||
func TestCalculateDiceStatistics_NegativeResults(t *testing.T) {
|
||||
expression := "1d4 - 10"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// 1d4 (1-4) - 10 = -9 to -6
|
||||
if stats.MinValue != -9 {
|
||||
t.Errorf("Expected MinValue -9, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != -6 {
|
||||
t.Errorf("Expected MaxValue -6, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_Parentheses tests parentheses in expressions
|
||||
func TestCalculateDiceStatistics_Parentheses(t *testing.T) {
|
||||
expression := "(1d4 + 2) * 3"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// (1d4 + 2) * 3 = (3-6) * 3 = 9-18
|
||||
if stats.MinValue != 9 {
|
||||
t.Errorf("Expected MinValue 9, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 18 {
|
||||
t.Errorf("Expected MaxValue 18, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_Exponentiation tests exponentiation in expressions
|
||||
func TestCalculateDiceStatistics_Exponentiation(t *testing.T) {
|
||||
expression := "1d4 ^ 2"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// 1d4 ^ 2: outcomes 1, 4, 9, 16
|
||||
if stats.MinValue != 1 {
|
||||
t.Errorf("Expected MinValue 1, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 16 {
|
||||
t.Errorf("Expected MaxValue 16, got %d", stats.MaxValue)
|
||||
}
|
||||
// Check that 2 is NOT in results
|
||||
if _, exists := stats.Results[2]; exists {
|
||||
t.Errorf("Outcome 2 should not exist for 1d4^2")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_MaxPercentage tests max percentage calculation
|
||||
func TestCalculateDiceStatistics_MaxPercentage(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("d6")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// For d6, all outcomes equally likely: 1/6 ≈ 16.67%
|
||||
expectedMaxPct := (1.0 / 6.0) * 100
|
||||
if stats.MaxPercentage < expectedMaxPct-1 || stats.MaxPercentage > expectedMaxPct+1 {
|
||||
t.Errorf("Expected MaxPercentage ~16.67%%, got %f%%", stats.MaxPercentage)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_DistributionSum tests that distribution probabilities sum to 1
|
||||
func TestCalculateDiceStatistics_DistributionSum(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("2d6")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
sum := 0.0
|
||||
for _, prob := range stats.Results {
|
||||
sum += prob
|
||||
}
|
||||
// Probabilities should sum to ~1.0
|
||||
if sum < 0.999 || sum > 1.001 {
|
||||
t.Errorf("Probabilities should sum to 1.0, got %f", sum)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_GetSortedOutcomes tests GetSortedOutcomes method
|
||||
func TestCalculateDiceStatistics_GetSortedOutcomes(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("1d6")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
outcomes := stats.GetSortedOutcomes()
|
||||
if len(outcomes) != 6 {
|
||||
t.Errorf("Expected 6 outcomes, got %d", len(outcomes))
|
||||
}
|
||||
// Check they're sorted
|
||||
for i := 1; i < len(outcomes); i++ {
|
||||
if outcomes[i] <= outcomes[i-1] {
|
||||
t.Errorf("Outcomes not properly sorted: %v", outcomes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_GetMaxPercentage tests GetMaxPercentage method
|
||||
func TestCalculateDiceStatistics_GetMaxPercentage(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("d20")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
maxPct := stats.GetMaxPercentage()
|
||||
expectedMaxPct := 5.0 // 1/20 = 0.05 = 5%
|
||||
if maxPct < expectedMaxPct-0.1 || maxPct > expectedMaxPct+0.1 {
|
||||
t.Errorf("Expected MaxPercentage 5%%, got %f%%", maxPct)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_ComplexExpression tests complex expression
|
||||
func TestCalculateDiceStatistics_ComplexExpression(t *testing.T) {
|
||||
expression := "2d6 + 1d4 + 3"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// 2d6 (2-12) + 1d4 (1-4) + 3 = 6-19
|
||||
if stats.MinValue != 6 {
|
||||
t.Errorf("Expected MinValue 6, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 19 {
|
||||
t.Errorf("Expected MaxValue 19, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_ThreeDiceSum tests sum of three different dice
|
||||
func TestCalculateDiceStatistics_ThreeDiceSum(t *testing.T) {
|
||||
expression := "1d4 + 1d6 + 1d8"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// Min: 1+1+1 = 3, Max: 4+6+8 = 18
|
||||
if stats.MinValue != 3 {
|
||||
t.Errorf("Expected MinValue 3, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 18 {
|
||||
t.Errorf("Expected MaxValue 18, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_ImplicitMultiplication tests implicit multiplication
|
||||
func TestCalculateDiceStatistics_ImplicitMultiplication(t *testing.T) {
|
||||
// 2d10 should be treated as 2 dice of 10 sides, not 2 * d10
|
||||
stats, err := CalculateDiceStatistics("2d10")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 2 {
|
||||
t.Errorf("Expected MinValue 2, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 20 {
|
||||
t.Errorf("Expected MaxValue 20, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_OutcomeAtBoundary tests outcomes at boundaries
|
||||
func TestCalculateDiceStatistics_OutcomeAtBoundary(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("1d20")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// Check minimum boundary
|
||||
if _, exists := stats.Results[1]; !exists {
|
||||
t.Errorf("Expected outcome 1 for d20")
|
||||
}
|
||||
// Check maximum boundary
|
||||
if _, exists := stats.Results[20]; !exists {
|
||||
t.Errorf("Expected outcome 20 for d20")
|
||||
}
|
||||
// Check just outside boundaries
|
||||
if _, exists := stats.Results[0]; exists {
|
||||
t.Errorf("Outcome 0 should not exist for d20")
|
||||
}
|
||||
if _, exists := stats.Results[21]; exists {
|
||||
t.Errorf("Outcome 21 should not exist for d20")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_SortedOutcomesAreUnique tests that sorted outcomes are unique
|
||||
func TestCalculateDiceStatistics_SortedOutcomesAreUnique(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("2d6")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
seen := make(map[int]bool)
|
||||
for _, outcome := range stats.SortedOutcomes {
|
||||
if seen[outcome] {
|
||||
t.Errorf("Outcome %d appears more than once in SortedOutcomes", outcome)
|
||||
}
|
||||
seen[outcome] = true
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_AverageInRange tests that average is within min/max
|
||||
func TestCalculateDiceStatistics_AverageInRange(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("1d20")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.Average < float64(stats.MinValue) || stats.Average > float64(stats.MaxValue) {
|
||||
t.Errorf("Average %f should be between min %d and max %d", stats.Average, stats.MinValue, stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_MostCommonInResults tests that MostCommon is in Results
|
||||
func TestCalculateDiceStatistics_MostCommonInResults(t *testing.T) {
|
||||
stats, err := CalculateDiceStatistics("2d6")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if _, exists := stats.Results[stats.MostCommon]; !exists {
|
||||
t.Errorf("MostCommon value %d should exist in Results", stats.MostCommon)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_WhitespaceHandling tests whitespace in expressions
|
||||
func TestCalculateDiceStatistics_WhitespaceHandling(t *testing.T) {
|
||||
expression := " 1d6 + 2 "
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
if stats.MinValue != 3 {
|
||||
t.Errorf("Expected MinValue 3, got %d", stats.MinValue)
|
||||
}
|
||||
if stats.MaxValue != 8 {
|
||||
t.Errorf("Expected MaxValue 8, got %d", stats.MaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalculateDiceStatistics_ZeroOutcome tests expressions that can produce zero
|
||||
func TestCalculateDiceStatistics_ZeroOutcome(t *testing.T) {
|
||||
expression := "1d4 - 1d4"
|
||||
stats, err := CalculateDiceStatistics(expression)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed: %v", err)
|
||||
}
|
||||
// Should include zero outcome
|
||||
if _, exists := stats.Results[0]; !exists {
|
||||
t.Errorf("Expected outcome 0 for 1d4 - 1d4")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user