diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index 6768ea86..39d261ca 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -2914,3 +2914,167 @@ func TestParseExpression_incompleteFunctionCall(t *testing.T) { }) } } + +func TestAllBoolExpressions(t *testing.T) { + inputs := map[string]cty.Value{ + // truth table for all boolean expressions + "true && true": cty.True, + "true || true": cty.True, + "true && false": cty.False, + "true || false": cty.True, + "true && unknown": cty.DynamicVal, + "true || unknown": cty.True, + "false && true": cty.False, + "false || true": cty.True, + "false && false": cty.False, + "false || false": cty.False, + "false && unknown": cty.False, + "false || unknown": cty.DynamicVal, + "unknown && true": cty.DynamicVal, + "unknown || true": cty.True, + "unknown && false": cty.False, + "unknown || false": cty.DynamicVal, + "unknown && unknown": cty.DynamicVal, + "unknown || unknown": cty.DynamicVal, + + // truth table for all possible combinations 3 part boolean expressions + "true && true && true": cty.True, + "true || true && true": cty.True, + "true || true || true": cty.True, + "true && true || true": cty.True, + "true && true && false": cty.False, + "true || true && false": cty.True, + "true || true || false": cty.True, + "true && true || false": cty.True, + "true && true && unknown": cty.DynamicVal, + "true || true && unknown": cty.True, + "true || true || unknown": cty.True, + "true && true || unknown": cty.True, + "true && false && true": cty.False, + "true || false && true": cty.True, + "true || false || true": cty.True, + "true && false || true": cty.True, + "true && false && false": cty.False, + "true || false && false": cty.True, + "true || false || false": cty.True, + "true && false || false": cty.False, + "true && false && unknown": cty.False, + "true || false && unknown": cty.True, + "true || false || unknown": cty.True, + "true && false || unknown": cty.DynamicVal, + "true && unknown && true": cty.DynamicVal, + "true || unknown && true": cty.True, + "true || unknown || true": cty.True, + "true && unknown || true": cty.True, + "true && unknown && false": cty.False, + "true || unknown && false": cty.True, + "true || unknown || false": cty.True, + "true && unknown || false": cty.DynamicVal, + "true && unknown && unknown": cty.DynamicVal, + "true || unknown && unknown": cty.True, + "true || unknown || unknown": cty.True, + "true && unknown || unknown": cty.DynamicVal, + "false && true && true": cty.False, + "false || true && true": cty.True, + "false || true || true": cty.True, + "false && true || true": cty.True, + "false && true && false": cty.False, + "false || true && false": cty.False, + "false || true || false": cty.True, + "false && true || false": cty.False, + "false && true && unknown": cty.False, + "false || true && unknown": cty.DynamicVal, + "false || true || unknown": cty.True, + "false && true || unknown": cty.DynamicVal, + "false && false && true": cty.False, + "false || false && true": cty.False, + "false || false || true": cty.True, + "false && false || true": cty.True, + "false && false && false": cty.False, + "false || false && false": cty.False, + "false || false || false": cty.False, + "false && false || false": cty.False, + "false && false && unknown": cty.False, + "false || false && unknown": cty.False, + "false || false || unknown": cty.DynamicVal, + "false && false || unknown": cty.DynamicVal, + "false && unknown && true": cty.False, + "false || unknown && true": cty.DynamicVal, + "false || unknown || true": cty.True, + "false && unknown || true": cty.True, + "false && unknown && false": cty.False, + "false || unknown && false": cty.False, + "false || unknown || false": cty.DynamicVal, + "false && unknown || false": cty.False, + "false && unknown && unknown": cty.False, + "false || unknown && unknown": cty.DynamicVal, + "false || unknown || unknown": cty.DynamicVal, + "false && unknown || unknown": cty.DynamicVal, + "unknown && true && true": cty.DynamicVal, + "unknown || true && true": cty.True, + "unknown || true || true": cty.True, + "unknown && true || true": cty.True, + "unknown && true && false": cty.False, + "unknown || true && false": cty.DynamicVal, + "unknown || true || false": cty.True, + "unknown && true || false": cty.DynamicVal, + "unknown && true && unknown": cty.DynamicVal, + "unknown || true && unknown": cty.DynamicVal, + "unknown || true || unknown": cty.True, + "unknown && true || unknown": cty.DynamicVal, + "unknown && false && true": cty.False, + "unknown || false && true": cty.DynamicVal, + "unknown || false || true": cty.True, + "unknown && false || true": cty.True, + "unknown && false && false": cty.False, + "unknown || false && false": cty.DynamicVal, + "unknown || false || false": cty.DynamicVal, + "unknown && false || false": cty.False, + "unknown && false && unknown": cty.False, + "unknown || false && unknown": cty.DynamicVal, + "unknown || false || unknown": cty.DynamicVal, + "unknown && false || unknown": cty.DynamicVal, + "unknown && unknown && true": cty.DynamicVal, + "unknown || unknown && true": cty.DynamicVal, + "unknown || unknown || true": cty.True, + "unknown && unknown || true": cty.True, + "unknown && unknown && false": cty.False, + "unknown || unknown && false": cty.DynamicVal, + "unknown || unknown || false": cty.DynamicVal, + "unknown && unknown || false": cty.DynamicVal, + "unknown && unknown && unknown": cty.DynamicVal, + "unknown || unknown && unknown": cty.DynamicVal, + "unknown || unknown || unknown": cty.DynamicVal, + "unknown && unknown || unknown": cty.DynamicVal, + } + + for input, want := range inputs { + t.Run(input, func(t *testing.T) { + ctx := &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.DynamicPseudoType), + }, + } + expr, diags := ParseExpression([]byte(input), "", hcl.Pos{Line: 1, Column: 1, Byte: 0}) + if diags.HasErrors() { + t.Fatal(diags.Error()) + } + got, diags := expr.Value(ctx) + if diags.HasErrors() { + t.Fatal(diags.Error()) + } + + if got.IsKnown() != want.IsKnown() { + t.Fatalf("%q resulted in %#v, wanted %#v\n", input, got, want) + } + if !got.IsKnown() { + // covered in known comparison + return + } + + if got.Equals(want).False() { + t.Fatalf("%q resulted in %#v, wanted %#v\n", input, got, want) + } + }) + } +}