;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
;;;
;;; fsbv.lisp --- Tests of foreign structure by value calls.
;;;
;;; Copyright (C) 2011, Liam M. Healy
;;;
;;; Permission is hereby granted, free of charge, to any person
;;; obtaining a copy of this software and associated documentation
;;; files (the "Software"), to deal in the Software without
;;; restriction, including without limitation the rights to use, copy,
;;; modify, merge, publish, distribute, sublicense, and/or sell copies
;;; of the Software, and to permit persons to whom the Software is
;;; furnished to do so, subject to the following conditions:
;;;
;;; The above copyright notice and this permission notice shall be
;;; included in all copies or substantial portions of the Software.
;;;
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;;; NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;;; DEALINGS IN THE SOFTWARE.
;;;

(in-package #:cffi-tests)

;; Requires struct.lisp

(defcfun "sumpair" :int
  (p (:struct struct-pair)))

(defcfun "makepair" (:struct struct-pair)
  (condition :bool))

(defcfun "doublepair" (:struct struct-pair)
  (p (:struct struct-pair)))

(defcfun "prodsumpair" :double
  (p (:struct struct-pair+double)))

(defcfun "doublepairdouble" (:struct struct-pair+double)
  (p (:struct struct-pair+double)))

;;; Call struct by value
(deftest fsbv.1
    (sumpair '(1 . 2))
  3)

;;; See lp#1528719
(deftest (fsbv.wfo :expected-to-fail t)
    (with-foreign-object (arg '(:struct struct-pair))
      (convert-into-foreign-memory '(40 . 2) '(:struct struct-pair) arg)
      (sumpair arg))
  42)

;;; Call and return struct by value
(deftest fsbv.2
    (doublepair '(1 . 2))
  (2 . 4))

;;; return struct by value
(deftest (fsbv.makepair.1 :expected-to-fail t)
    (makepair nil)
  (-127 . 43))

(deftest (fsbv.makepair.2 :expected-to-fail t)
    (makepair t)
  (-127 . 42))

;;; Call recursive structure by value
(deftest fsbv.3
    (prodsumpair '(pr (a 4 b 5) dbl 2.5d0))
  22.5d0)

;;; Call and return recursive structure by value
(deftest fsbv.4
    (let ((ans (doublepairdouble '(pr (a 4 b 5) dbl 2.5d0))))
      (values (getf (getf ans 'pr) 'a)
              (getf (getf ans 'pr) 'b)
              (getf ans 'dbl)))
  8
  10
  5.0d0)

(defcstruct (struct-with-array :size 6)
  (s1 (:array :char 6)))

(defcfun "zork" :void
  (p (:struct struct-with-array)))

;;; Typedef fsbv test

(defcfun ("sumpair" sumpair2) :int
  (p struct-pair-typedef1))

(deftest fsbv.5
    (sumpair2 '(1 . 2))
  3)

(defcfun "returnpairpointer" (:pointer (:struct struct-pair))
  (ignored (:struct struct-pair)))

(deftest fsbv.return-a-pointer
    (let ((ptr (returnpairpointer '(1 . 2))))
      (+ (foreign-slot-value ptr '(:struct struct-pair) 'a)
         (foreign-slot-value ptr '(:struct struct-pair) 'b)))
  42)

;;; Test ulonglong on no-long-long implementations.

(defcfun "ullsum" :unsigned-long-long
  (a :unsigned-long-long) (b :unsigned-long-long))

(deftest fsbv.6
    (ullsum #x10DEADBEEF #x2300000000)
  #x33DEADBEEF)

;;; Combine structures by value with a string argument
(defcfun "stringlenpair" (:struct struct-pair)
  (s :string)
  (p (:struct struct-pair)))

(deftest fsbv.7
  (stringlenpair "abc" '(1 . 2))
  (3 . 6))

;;; Combine structures by value with an enum argument
(defcfun "enumpair" (:int)
  (e numeros)
  (p (:struct struct-pair)))

(deftest fsbv.8
  (enumpair :two '(1 . 2))
  5)

;;; returning struct with bitfield member (bug #1474631)
(defbitfield (struct-bitfield :unsigned-int)
  (:a 1)
  (:b 2))

(defcstruct bitfield-struct
  (b struct-bitfield))

(defcfun "structbitfield" (:struct bitfield-struct)
  (x :unsigned-int))

(defctype struct-bitfield-typedef struct-bitfield)

(defcstruct bitfield-struct.2
  (b struct-bitfield-typedef))

(defcfun ("structbitfield" structbitfield.2) (:struct bitfield-struct.2)
  (x :unsigned-int))

;; these would get stuck in an infinite loop previously
(deftest fsbv.struct-bitfield.0
  (structbitfield 0)
  (b nil))

(deftest fsbv.struct-bitfield.1
  (structbitfield 1)
  (b (:a)))

(deftest fsbv.struct-bitfield.2
  (structbitfield 2)
  (b (:b)))

(deftest fsbv.struct-bitfield.3
  (structbitfield.2 2)
  (b (:b)))

;;; Test for a discrepancy between normal and fsbv return values
(cffi:define-foreign-type int-return-code (cffi::foreign-type-alias)
  ()
  (:default-initargs :actual-type (cffi::parse-type :int))
  (:simple-parser int-return-code))

(defmethod cffi:expand-from-foreign (value (type int-return-code))
  ;; NOTE: strictly speaking it should be
  ;; (cffi:convert-from-foreign ,value :int), but it's irrelevant in this case
  `(let ((return-code ,value))
     (check-type return-code integer)
     return-code))

(defcfun (noargs-with-typedef "noargs") int-return-code)

(deftest fsbv.noargs-with-typedef    ; for reference, not an FSBV call
    (noargs-with-typedef)
  42)

(defcfun (sumpair-with-typedef "sumpair") int-return-code
  (p (:struct struct-pair)))

(deftest (fsbv.return-value-typedef)
    (sumpair-with-typedef '(40 . 2))
  42)
