use strict;
use warnings;
use Class::Value;
use Error::Hierarchy::Test 'throws2_ok';
use Test::More tests => 59;
my $n = 42;
my $v = Class::Value->new(value => $n);
test_invalid_op('$v + 1', add => '+');
test_invalid_op('$v++', add => '++');
test_invalid_op('$v += 3', add => '+=');
test_invalid_op('$v - 1', subtract => '-');
test_invalid_op('$v--', subtract => '--');
test_invalid_op('$v -= 3', subtract => '-=');
test_invalid_op('-$v', subtract => 'neg'); # unary minus
test_invalid_op('$v * 2', multiply => '*');
test_invalid_op('$v *= 2', multiply => '*=');
test_invalid_op('$v / 2', divide => '/');
test_invalid_op('$v /= 2', divide => '/=');
test_invalid_op('$v % 2', modulo => '%');
test_invalid_op('$v %= 2', modulo => '%=');
test_invalid_op('$v ** 2', power => '**');
test_invalid_op('$v **= 2', power => '**=');
test_invalid_op('$v << 2', bit_shift_left => '<<');
test_invalid_op('$v <<= 2', bit_shift_left => '<<=');
test_invalid_op('$v >> 2', bit_shift_right => '>>');
test_invalid_op('$v >>= 2', bit_shift_right => '>>=');
test_invalid_op('$v < 2', num_cmp => '<');
test_invalid_op('$v <= 2', num_cmp => '<=');
test_invalid_op('$v > 2', num_cmp => '>');
test_invalid_op('$v >= 2', num_cmp => '>=');
test_invalid_op('$v == 2', num_cmp => '==');
test_invalid_op('$v != 2', num_cmp => '!=');
test_invalid_op('$v <=> 2', num_cmp => '<=>');
test_invalid_op('$v & 1', bit_and => '&');
test_invalid_op('$v | 1', bit_or => '|');
test_invalid_op('$v ^ 1', bit_xor => '^');
test_invalid_op('~$v', bit_not => '~');
test_invalid_op('atan2($v, $v)', atan2 => 'atan2');
test_invalid_op('cos($v)', cos => 'cos');
test_invalid_op('sin($v)', sin => 'sin');
test_invalid_op('exp($v)', exp => 'exp');
test_invalid_op('log($v)', log => 'log');
test_invalid_op('sqrt($v)', sqrt => 'sqrt');
test_invalid_op('int($v)', int => 'int');
test_invalid_op('<$v>', iterate => '<>');
# Do the positive tests after the negative tests as they might alter the value
# and we want to keep comparing to $n.
ok($v->is_well_formed, "$n is a well-formed value");
ok($v->is_valid, "$n is a valid value");
is("$v", $n, 'valid operation [""]');
is_deeply("$v", $n, 'is_deeply: valid operation [""]');
is($v x 3, "$n$n$n", 'valid operation [x]');
is('foo' . $v, "foo$n", 'valid operation [.]');
is(($v ? 'Y' : 'N'), 'Y', "valid operation [bool] is true for value [$n]");
is((!$v ? 'Y' : 'N'), 'N', "valid operation [!] is false for value [$n]");
my $v2 = Class::Value->new(value => 0);
is(($v2 ? 'Y' : 'N'), 'N', 'valid operation [bool] is false for value [0]');
is((!$v2 ? 'Y' : 'N'), 'Y', 'valid operation [!] is true for value [0]');
$v .= 'bar';
$n .= 'bar';
is($v, $n, 'valid operation [.=]');
is(ref $v, '', 'operation [.=] produces a normal string');
my $v3 = Class::Value->new(value => 'abc');
ok($v3 lt 'bbb', 'valid operation [lt]');
ok($v3 le 'abc', 'valid operation [le]');
ok($v3 gt 'aaa', 'valid operation [lt]');
ok($v3 ge 'abc', 'valid operation [ge]');
ok($v3 eq 'abc', 'valid operation [eq]');
ok($v3 ne 'def', 'valid operation [ne]');
is($v3 cmp 'bbb', -1, 'valid operation [cmp] returning -1');
is($v3 cmp 'abc', 0, 'valid operation [cmp] returning 0');
is($v3 cmp 'aaa', 1, 'valid operation [cmp] returning 1');
sub test_invalid_op {
my ($code, $method, $op) = @_;
throws2_ok { eval $code; throw $@ if $@ }
'Class::Value::Exception::UnsupportedOperation',
"Value object of type [Class::Value], value [$n], does not support operation [$method]",
"invalid operation [$op]";
}