/*  Part of SWI-Prolog

    Author:        Jan Wielemaker
    E-mail:        J.Wielemaker@vu.nl
    WWW:           http://www.swi-prolog.org
    Copyright (c)  2006-2018, Paul Singleton
                              VU University Amsterdam
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in
       the documentation and/or other materials provided with the
       distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.
*/

:- module(test_jpl,
          [ test_jpl/0,
            run_tests/0,
            run_tests/1
          ]).
% ensure we get the local copies

:- asserta(user:file_search_path(foreign, '.')).
:- asserta(user:file_search_path(jpl_examples, 'examples/prolog')).
:- asserta(user:file_search_path(jar, '.')).
:- asserta(user:file_search_path(jar, foreign('src/java'))).
:- asserta(user:file_search_path(jar, foreign('.'))).
:- asserta(user:file_search_path(library, '.')).
:- asserta(user:file_search_path(library, '../plunit')).

% paths for Prolog embedded in Java under CTest
:- asserta(user:file_search_path(library, '../../../packages/jpl')).
:- asserta(user:file_search_path(library, '../../../packages/plunit')).

:- use_module(library(jpl)).
:- use_module(library(plunit)).

% Using cmake location
:- jpl:add_search_path('CLASSPATH', 'src/java/jpltest.jar').
% For before cmake
:- jpl:add_search_path('CLASSPATH', 'jpltest.jar').

test_jpl :-
    run_tests([jpl]).

:- begin_tests(jpl).

test(
        exception_jref_1,
        [       true((
                        E = error(java_exception(JRef), 'java.lang.IllegalArgumentException'),
                        blob(JRef, jref)
                ))
        ]
) :-
    catch(jpl_new('java.util.Date', [never], _), E, true).

test(
        array_to_from_terms_1,
        [       true(
                        Terms1 == Terms2
                )
        ]
) :-
    Terms1 = [x,[1,a,7,[y,z]],k,[]],
    jpl_terms_to_array(Terms1, JRef),
    jpl_array_to_terms(JRef, Terms2).

test(
        ancestor_types_1,
        [       true(
                        Ts == [class([org,jpl7],['Term']),class([java,lang],['Object'])]
                )
        ]
) :-
    jpl:jpl_type_to_ancestor_types(class([org,jpl7],['Atom']), Ts).

test(
        call_array_equals_1,
        [       setup((
                        jpl_new(array(byte), [4,5,6], A1),
                        jpl_new(array(byte), [4,5,6], A2)
                ))
        ]
) :-
    jpl_call(A1, equals, [A2], @(false)).

test(
        call_array_equals_2,
        [       setup((
                        jpl_new(array(byte), [4,5,6], A1)
                ))
        ]
) :-
    jpl_call(A1, equals, [A1], @(true)).

test(
        call_array_hashcode_1,
        [       setup((
                        jpl_new(array(byte), [4,5,6], A)
                )),
                true((
                        integer(H)
                ))
        ]
) :-
    jpl_call(A, hashCode, [], H).

test(
        call_array_hashcode_2,
        [       setup((
                        jpl_new(array(byte), [4,5,6], A1),
                        jpl_new(array(byte), [4,5,6], A2)
                )),
                true((
                        H1 \== H2
                ))
        ]
) :-
    jpl_call(A1, hashCode, [], H1),
    jpl_call(A2, hashCode, [], H2).

test(
        call_array_to_string_1,
        [       setup((
                        jpl_new(array(byte), [4,5,6], A)
                )),
                true((
                        atom_codes(S, [0'[, 0'B | _])
                ))
        ]
) :-
    jpl_call(A, toString, [], S).

test(
        call_instance_param_cyclic_term_1,
        [       setup((
                        T = f(T),
                        jpl_new('org.jpl7.test.Test', [], Test)
                )),
                throws(
                        error(type_error(acyclic,T),context(jpl_call/4,_))
                )
        ]
) :-
    jpl_call(Test, methodInstanceTerm, [{T}], @(true)).

testX(
        call_instance_param_cyclic_term_2,
        [       setup((
                        T = f(T),
                        jpl_new('org.jpl7.test.Test', [], Test)
                )),
                throws(
                        error(type_error(acyclic,_),context(jpl_call/4,_))
                )
        ]
) :-
    jpl_call(Test, methodInstanceTerm, [{T}], @(true)).

test(
        call_method_static_array_1,
        [       setup((
                        jpl_new(array(int), [3,4,5], IntArray)
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticArray, [IntArray], 'int[]').

test(
        call_method_static_array_2,
        [       setup((
                        jpl_new(array(byte), [3,4,5], ByteArray)
                )),
                throws(
                        error(
                                type_error(method_params,[ByteArray]),
                                context(jpl_call/4,_)
                        )
                )
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticArray, [ByteArray], _).

test(
        call_static_param_cyclic_term_1,
        [       setup((
                        T = f(T)
                )),
                throws(
                        error(type_error(acyclic,T),context(jpl_call/4,_))
                )
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticTerm, [{T}], @(true)).

test(
        call_class_get_name_1,
        [       setup((
                        ClassName = 'java.lang.Integer',
                        jpl_classname_to_class(ClassName, ClassObject)
                )),
                true((
                        ClassName == ClassName2
                ))
        ]
) :-
    jpl_call(ClassObject, getName, [], ClassName2).

test(
        call_get_array_bad_field_name_1,
        [       setup((
                        jpl_new(array(byte), 5, A),
                        FieldName = colour
                )),
                throws(
                        error(domain_error(array_field_name,FieldName),context(jpl_get/3,_))
                )
        ]
) :-
    jpl_get(A, FieldName, _).

test(
        call_get_array_bad_fspec_1,
        [       setup((
                        jpl_new(array(byte), 5, A),
                        Fspec = poo(77)
                )),
                throws(
                        error(type_error(array_lookup_spec,Fspec),context(jpl_get/3,_))
                )
        ]
) :-
    jpl_get(A, Fspec, _).

test(
        call_get_array_bad_index_range_1,
        [       setup((
                        jpl_new(array(byte), 5, A)
                )),
                throws(
                        error(domain_error(array_index_range,(-1)-2),context(jpl_get/3,_))
                )
        ]
) :-
    jpl_get(A, (-1)-2, _).

test(
        call_get_array_bad_index_range_2,
        [       setup((
                        jpl_new(array(byte), 5, A)
                )),
                throws(
                        error(domain_error(array_index_range,10-12),context(jpl_get/3,_))
                )
        ]
) :-
    jpl_get(A, 10-12, _).

test(
        call_get_array_bad_index_range_3,
        [       setup((
                        jpl_new(array(byte), 5, A)
                )),
                throws(
                        error(domain_error(array_index_range,3-33),context(jpl_get/3,_))
                )
        ]
) :-
    jpl_get(A, 3-33, _).

test(
        call_get_array_bad_index_range_4,
        [       setup((
                        jpl_new(array(byte), 5, A)
                )),
                throws(
                        error(type_error(array_index_range,this-that),context(jpl_get/3,_))
                )
        ]
) :-
    jpl_get(A, this-that, _).

test(
        get_array_element_1,
        [       setup((
                        jpl_new(array(byte), [4,5,6,7,8], A)
                )),
                true((
                        7 == V
                ))
        ]
) :-
    jpl_get(A, 3, V). % should bind V = 7 i.e. a[3] i.e. the fourth array element counting from zero

test(
        get_array_elements_1,
        [       setup((
                        jpl_new(array(byte), [4,5,6,7,8], A)
                )),
                true((
                        [5,6] == V
                ))
        ]
) :-
    jpl_get(A, 1-2, V). % should bind V = [5,6] i.e. a[1-2] i.e. the 2nd to 3rd array elements counting from zero

test(
        get_array_length_1,
        [       setup((
                        Len1 is 5,
                        jpl_new(array(byte), Len1, A)
                )),
                true((
                        Len1 == Len2
                ))
        ]
) :-
    jpl_get(A, length, Len2). % should bind Len2 to the (integer) value of Len1

test(
        get_array_negative_index_1,
        [       setup((
                        BadIndex is -1,
                        jpl_new(array(byte), 5, A)
                )),
                throws(
                        error(domain_error(array_index,BadIndex), context(jpl_get/3,_))
                )
        ]
) :-
    jpl_get(A, BadIndex, _).

test(
        get_array_unbound_fspec_1,
        [       setup((
                        jpl_new(array(byte), 5, A)
                )),
                throws(
                        error(instantiation_error,context(jpl_get/3,_))
                )
        ]
) :-
    jpl_get(A, _, _).

test(
        get_field_static_boolean_1,
        [       true((
                        V == @(false)
                ))
        ]
) :-
    jpl_get('org.jpl7.test.Test', fieldStaticBoolean1, V).

test(
        get_field_static_boolean_2,
        [       true((
                        V == @(true)
                ))
        ]
) :-
    jpl_get('org.jpl7.test.Test', fieldStaticBoolean2, V).

test(
        get_field_static_char_1,
        [       true((
                        V == 0
                ))
        ]
) :-
    jpl_get('org.jpl7.test.Test', fieldStaticChar1, V).

test(
        get_field_static_char_2,
        [       true((
                        V == 65535
                ))
        ]
) :-
    jpl_get('org.jpl7.test.Test', fieldStaticChar2, V).

test(
        get_field_instance_byte_2,
        [       setup((
                        jpl_new('org.jpl7.test.Test', [], Test)
                )),
                true((
                        V == -1
                ))
        ]
) :-
    jpl_get(Test, fieldInstanceByte2, V).

test(
        list_to_array_1,
        [       true((
                        Type == array(byte)
                ))
        ]
) :-
    jpl_list_to_array([1,2,3], A),
    jpl_object_to_type(A, Type).

test(
        method_static_byte_1,
        [       throws(
                        error(
                                type_error(method_params,[-129]),
                                context(jpl_call/4,_)
                        )
                )
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoByte, [-129], _).

test(
        method_static_echo_boolean_1,
        [       setup((
                        jpl_false(V1)
                )),
                true((
                        V1 == V2
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoBoolean, [V1], V2).

test(
        method_static_echo_boolean_2,
        [       setup((
                        jpl_true(V1)
                )),
                true((
                        V1 == V2
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoBoolean, [V1], V2).

test(
        method_static_echo_char_1,
        [       setup((
                        V1 = 0
                )),
                true((
                        V1 == V2
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoChar, [V1], V2).

test(
        method_static_echo_char_2,
        [       setup((
                        V1 = 65535
                )),
                true((
                        V1 == V2
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoChar, [V1], V2).

test(
        method_static_char_3,
        [       setup((
                        V1 = -1
                )),
                throws(
                        error(
                                type_error(method_params,[V1]),
                                context(jpl_call/4,_)
                        )
                )
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoChar, [V1], _).

test(
        method_static_char_4,
        [       setup((
                        V1 = 1.0
                )),
                throws(
                        error(
                                type_error(method_params,[V1]),
                                context(jpl_call/4,_)
                        )
                )
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoChar, [V1], _).

test(
        method_static_char_5,
        [       setup((
                        V1 = a
                )),
                throws(
                        error(
                                type_error(method_params,[V1]),
                                context(jpl_call/4,_)
                        )
                )
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoChar, [V1], _).

test(
        method_static_echo_double_1,
        [       setup((
                        V1 = 1.5
                )),
                true((
                        V1 == V2
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoDouble, [V1], V2).

test(
        method_static_echo_double_2,
        [       setup((
                        V1 = 2
                )),
                true((
                        V2 =:= float(V1)
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoDouble, [V1], V2).

test(
        method_static_echo_double_3,
        [       setup((
                        (   current_prolog_flag(bounded, true)
                    ->  current_prolog_flag(max_integer, V1)
                    ;   V1 is 2**63-1
                    ),
                        V2b is float(V1)
                )),
                true((
                        V2 == V2b
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoDouble, [V1], V2).

test(
        method_static_echo_float_1,
        [       setup((
                        V1 = 1.5
                )),
                true((
                        V1 == V2
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoFloat, [V1], V2).

test(
        method_static_echo_float_2,
        [       setup((
                        V1 is 2,
                        V2b is float(V1)
                )),
                true((
                        V2 == V2b
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoFloat, [V1], V2).

test(
        method_static_echo_float_3,
        [       setup((
                        (       current_prolog_flag(bounded, true)
                    ->  current_prolog_flag(max_integer, V1)
                    ;   V1 is 2**63-1 % was 2**99
                    ),
                        V2b is float(V1)
                )),
                true((
                        V2 == V2b
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoFloat, [V1], V2).

test(
        method_static_echo_float_4,
        [       blocked('we do not yet widen unbounded integers to floats or doubles'),
                setup((
                        (   current_prolog_flag(bounded, true)
                    ->  current_prolog_flag(max_integer, V1)
                    ;   V1 is 2**99             % an unbounded integer
                    ),
                        V2b is float(V1)
                )),
                true((
                        V2 == V2b
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', methodStaticEchoFloat, [V1], V2).

test(
        new_abstract_class_1,
        [       setup((
                        Classname = 'java.util.Dictionary'
                )),
                throws(
                        error(
                                type_error(concrete_class,Classname),
                                context(jpl_new/3,_)
                        )
                )
        ]
) :-
    jpl_new(Classname, [], _).

test(
        new_array_boolean_from_val_1,
        [       setup((
                        jpl_false(V)
                )),
                true((
                        V == V2
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', newArrayBooleanFromValue, [V], A),
    jpl_get(A, 0, V2).

test(
        new_array_double_from_val_1,
        [       setup((
                        V is 1.5
                )),
                true((
                        V == V2
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', newArrayDoubleFromValue, [V], A),
    jpl_get(A, 0, V2).

test(
        new_array_float_from_val_1,
        [       setup((
                        V is 1.5
                )),
                true((
                        V == V2
                ))
        ]
) :-
    jpl_call('org.jpl7.test.Test', newArrayFloatFromValue, [V], A),
    jpl_get(A, 0, V2).

test(
        new_interface_1,
        [       setup((
                        Classname = 'java.util.Enumeration'
                )),
                throws(
                        error(
                                type_error(concrete_class,Classname),
                                context(jpl_new/3,_)
                        )
                )
        ]
) :-
    jpl_new(Classname, [], _).

test(
        new_param_cyclic_term_1,
        [       setup((
                        T = f(T)
                )),
                throws(
                        error(
                                type_error(acyclic,T),
                                context(jpl_new/3,_)
                        )
                )
        ]
) :-
    jpl_new('org.jpl7.test.Test', [{T}], _).

test(
        prolog_calls_java_calls_prolog_1,
        [       true((
                        V == @(true)
                ))
        ]
) :-
    jpl_new('org.jpl7.Query', ['4 is 2+2'], Q),
    jpl_call(Q, hasSolution, [], V).

test(
        set_array_element_cyclic_term_1,
        [       setup((
                        T = f(T),
                        jpl_new(array(class([org,jpl7,test],['Test'])), 5, A)
                )),
                throws(
                        error(
                                type_error(acyclic,T),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set(A, 0, {T}).

test(
        set_array_elements_bad_type_1,
        [       setup((
                        jpl_new(array(byte), 3, A)
                )),
                throws(
                        error(
                                type_error(array(byte),[128]),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set(A, 0, 128).

test(
        set_array_length_1,
        [       setup((
                        jpl_new(array(byte), 6, A)
                )),
                throws(
                        error(
                                permission_error(modify,final_field,length),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set(A, length, 13).

test(
        set_field_bad_field_spec_1,
        [       setup((
                        BadFieldName = 3.7
                )),
                throws(
                        error(
                                type_error(field_name,BadFieldName),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set('org.jpl7.test.Test', BadFieldName, a).

test(
        set_field_instance_cyclic_term_1,
        [       setup((
                        T = f(T),
                        jpl_new('org.jpl7.test.Test', [], Test)
                )),
                throws(
                        error(
                                type_error(acyclic,T),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set(Test, instanceTerm, {T}).

test(
        set_field_long_array_1,
        [       setup((
                        jpl_new(array(long), [1,2,3], LongArray)
                ))
        ]
) :-
    jpl_set('org.jpl7.test.Test', fieldStaticLongArray, LongArray).

test(
        set_field_long_array_2,
        [       setup((
                        jpl_new(array(int), [1,2,3], IntArray)
                )),
                throws(
                        error(
                                type_error('[J',IntArray),      % NB '[J' is *not* how the type was specified in the failing goal
                                context(
                                        jpl_set/3,
                                        'the value is not assignable to the named field of the class'
                                )
                        )
                )
        ]
) :-
    jpl_set('org.jpl7.test.Test', fieldStaticLongArray, IntArray).

test(
        set_field_object_array_1,
        [       setup((
                        jpl_new('java.util.Date', [], Date),
                        jpl_new(array(class([java,lang],['Object'])), [Date,Date], ObjArray)
                ))
        ]
) :-
    jpl_set('org.jpl7.test.Test', fieldStaticObjectArray, ObjArray).

test(
        set_field_static_bad_type_1,
        [       setup((
                        BadVal = 27
                )),
                throws(
                        error(
                                type_error(boolean,BadVal),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set('org.jpl7.test.Test', fieldStaticBoolean, BadVal).

test(
        set_field_static_boolean_1,
        [       setup((
                        jpl_true(V)
                ))
        ]
) :-
    jpl_set('org.jpl7.test.Test', fieldStaticBoolean, V).

test(
        set_field_static_boolean_2,
        [       setup((
                        jpl_false(V)
                ))
        ]
) :-
    jpl_set('org.jpl7.test.Test', fieldStaticBoolean, V).

test(
        set_field_static_boolean_bad_1,
        [       setup((
                        BadVal = foo(bar)
                )),
                throws(
                        error(
                                type_error(field_value,BadVal),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set('org.jpl7.test.Test', fieldStaticBoolean, BadVal).

test(
        set_field_static_cyclic_term_1,
        [       setup((
                        T = f(T)
                )),
                throws(
                        error(
                                type_error(acyclic,T),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set('org.jpl7.test.Test', staticTerm, {T}).

test(
        set_field_static_final_int_1,
        [       setup((
                        FieldName = fieldStaticFinalInt,
                        Value = 6
                )),
                throws(
                        error(
                                permission_error(modify,final_field,FieldName),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set('org.jpl7.test.Test', FieldName, Value).

test(
        set_field_static_shadow_1,
        [       blocked('we do not yet resolve same-named shadowed fields')
        ]
) :-
    jpl_set('org.jpl7.test.ShadowB', fieldStaticInt, 3).

test(
        set_field_static_term_1,
        [       setup((
                        T1 = foo(bar,33),
                        T2 = bar(77,bing)
                )),
                true((
                        T1 == T1a,
                        T2 == T2a
                ))
        ]
) :-
    jpl_set('org.jpl7.test.Test', fieldStaticTerm, {T1}),
    jpl_get('org.jpl7.test.Test', fieldStaticTerm, {T1a}),
    jpl_set('org.jpl7.test.Test', fieldStaticTerm, {T2}),
    jpl_get('org.jpl7.test.Test', fieldStaticTerm, {T2a}).

test(
        set_field_static_term_2,
        [       setup((
                        T1 = foo(bar,33),
                        T2 = bar(77,bing)
                ))
        ]
) :-
    jpl_set('org.jpl7.test.Test', fieldStaticTerm, {T1}),
    jpl_get('org.jpl7.test.Test', fieldStaticTerm, {T1}),
    jpl_set('org.jpl7.test.Test', fieldStaticTerm, {T2}),
    jpl_get('org.jpl7.test.Test', fieldStaticTerm, {T2}).

test(
        set_get_array_element_boolean_1,
        [       setup((
                        jpl_new(array(boolean), 3, A),
                        V = @(false)
                )),
                true((
                        V == Vr
                ))
        ]
) :-
    jpl_set(A, 2, V),
    jpl_get(A, 2, Vr).

test(
        set_get_array_element_boolean_2,
        [       setup((
                        jpl_new(array(boolean), 3, A),
                        V = @(true)
                )),
                true((
                        V == Vr
                ))
        ]
) :-
    jpl_set(A, 2, V),
    jpl_get(A, 2, Vr).

test(
        set_get_array_element_boolean_3,
        [       setup((
                        jpl_new(array(boolean), 3, A),
                        V = bogus
                )),
                throws(
                        error(
                                type_error(array(boolean),[V]),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set(A, 2, V).

test(
        set_get_array_element_byte_1,
        [       setup((
                        jpl_new(array(byte), 3, A),
                        V = 33
                )),
                true((
                        V == Vr
                ))
        ]
) :-
    jpl_set(A, 2, V),
    jpl_get(A, 2, Vr).

test(
        set_get_array_element_byte_2,
        [       setup((
                        jpl_new(array(byte), 3, A),
                        V = 128
                )),
                throws(
                        error(
                                type_error(array(byte),[V]),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set(A, 2, V).

test(
        set_get_array_element_char_1,
        [       setup((
                        jpl_new(array(char), 3, A),
                        V = 65535
                )),
                true((
                        V == Vr
                ))
        ]
) :-
    jpl_set(A, 2, V),
    jpl_get(A, 2, Vr).

test(
        set_get_array_element_double_1,
        [       setup((
                        jpl_new(array(double), 3, A),
                        V = 2.5
                )),
                true((
                        V == Vr
                ))
        ]
) :-
    jpl_set(A, 2, V),
    jpl_get(A, 2, Vr).

test(
        set_get_array_element_float_1,
        [       setup((
                        jpl_new(array(float), 3, A),
                        V = 7.5
                )),
                true((
                        V == Vr
                ))
        ]
) :-
    jpl_set(A, 2, V),
    jpl_get(A, 2, Vr).

test(
        set_get_array_element_float_2,
        [       setup((
                        jpl_new(array(float), 3, A),
                        V is 2,
                        VrX is float(V)
                )),
                true((
                        VrX == Vr
                ))
        ]
) :-
    jpl_set(A, 2, V),
    jpl_get(A, 2, Vr).

test(
        set_get_array_element_float_3,
        [       setup((
                        jpl_new(array(float), 3, A),
                        (       current_prolog_flag(bounded, true)
                        ->      current_prolog_flag(max_integer, Imax)
                        ;       Imax is 2**63-1
                        ),
                        VrX is float(Imax)
                )),
                true((
                        VrX == Vr
                ))
        ]
) :-
    jpl_set(A, 2, Imax),
    jpl_get(A, 2, Vr).

test(
        set_get_array_element_long_1,
        [       setup((
                        jpl_new(array(long), 3, A),
                        (       current_prolog_flag(bounded, true)
                        ->      current_prolog_flag(max_integer, V)
                        ;       V is 2**63-1
                        )
                )),
                true((
                        V == Vr
                ))
        ]
) :-
    jpl_set(A, 2, V),
    jpl_get(A, 2, Vr).

test(
        set_get_array_element_long_2,
        [       setup((
                        jpl_new(array(long), 3, A),
                        (       current_prolog_flag(bounded, true)
                        ->      current_prolog_flag(max_integer, V)
                        ;       V is 2**63
                        )
                )),
                throws(
                        error(
                                type_error(array(long),[V]),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set(A, 2, V).

test(
        set_get_array_elements_boolean_1,
        [       setup((
                        jpl_new(array(boolean), 3, A),
                        Vf = @(false),
                        Vt = @(true)
                )),
                true((
                        Vf+Vt+Vf == Vr0+Vr1+Vr2
                ))
        ]
) :-
    jpl_set(A, 0, Vf),
    jpl_set(A, 1, Vt),
    jpl_set(A, 2, Vf),
    jpl_get(A, 0, Vr0),
    jpl_get(A, 1, Vr1),
    jpl_get(A, 2, Vr2).

test(
        set_get_field_static_long_1,
        [       setup((
                        (   current_prolog_flag(bounded, true)
                    ->  current_prolog_flag(max_integer, V)
                    ;   V is 2**63-1
                    )
                )),
                true((
                        V == V2
                ))
        ]
) :-
    jpl_set('org.jpl7.test.Test', fieldStaticLong, V),
    jpl_get('org.jpl7.test.Test', fieldStaticLong, V2).

test(
        set_non_accessible_field_1,
        [       throws(
                        error(
                                existence_error(field,gagaga),
                                context(jpl_set/3,_)
                        )
                )
        ]
) :-
    jpl_set('org.jpl7.test.Test', gagaga, 4).

test(
        terms_to_array_1,
        []
) :-
    jpl_terms_to_array([foo(bar)], A),
    jpl_object_to_type(A, array(class([org,jpl7],['Term']))),
    jpl_get(A, length, 1),
    jpl_get(A, 0, T),
    jpl_call(T, toString, [], 'foo(bar)').

test(
        throw_java_exception_1,
        [       blocked('part of the error term is nondeterministic: we need to match with _'),
                throws(
                        error(
                                java_exception(@(_)),
                                'java.lang.NumberFormatException'
                        )
                )
        ]
) :-
    jpl_call('java.lang.Integer', decode, [q], _).

test(
        versions_1,
        [       true((
                        Vpl == Vc,
                        Vc == Vjava
                ))
        ]
) :-
    jpl_pl_lib_version(Vpl),
    jpl_c_lib_version(Vc),
    jpl_call('org.jpl7.JPL', version_string, [], Vjava).

%       JW: Mutual recursion check.  Moved from jpl.pl to here.  As the
%       callback is in module user, we define it there.

user:jpl_test_fac(N, F) :-
    (       N == 1
    ->      F = 1
    ;       N > 1
    ->      N2 is N-1,
            jpl_call('org.jpl7.test.Test', fac, [N2], F2),  % call its Java counterpart, which does vice versa
            F is N*F2
    ;       F = 0
    ).

test(fac10,
        [       true(N==3628800)
        ]
) :-
    user:jpl_test_fac(10, N).

test(threads1,
        [       true((
                        thread_create(jpl_call('java.lang.System', currentTimeMillis, [], _), ThreadId, []),
                        thread_join(ThreadId, true)
                ))
        ]
) :-
    jpl_call('java.lang.System', currentTimeMillis, [], _).

test(threads2,
        [       true(X==true)
        ]
) :-
    jpl_call('java.lang.System', currentTimeMillis, [], _),
    thread_create(jpl_call('java.lang.System', currentTimeMillis, [], _), ThreadId, []),
    thread_join(ThreadId, X).

test(threads3,
        [       true((
                        length(Ss, N),
                        sort(Ss, [true])
                ))
        ]
) :-
    N is 100,  % was 1000 (ok in V6); in V7 traditional, fails with 200
    jpl_call('java.lang.System', currentTimeMillis, [], _),
    findall(
            Status,
            (       between(1, N, _),
                    thread_create(jpl_call('java.lang.System', currentTimeMillis, [], _), ThreadId, []),
                    thread_join(ThreadId, Status)
            ),
            Ss
    ).

test(jref1,
        [       true((
                        Term1 \== Term2
                ))
        ]
) :-
    length(Term1, 5),
    jpl:jni_term_to_jref(Term1, JRef),
    jpl:jni_jref_to_term(JRef, Term2).

test(jref2,
        [       true((
                        Term1 =@= Term2
                ))
        ]
) :-
    length(Term1, 5),
    jpl:jni_term_to_jref(Term1, JRef),
    jpl:jni_jref_to_term(JRef, Term2).

:- end_tests(jpl).
