Merge remote-tracking branch 'origin/stable-2.0'
[bpt/guile.git] / test-suite / tests / foreign.test
index b053639..8ba989e 100644 (file)
@@ -1,6 +1,6 @@
 ;;;; foreign.test --- FFI.           -*- mode: scheme; coding: utf-8; -*-
 ;;;;
-;;;;   Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+;;;;   Copyright (C) 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
 ;;;;
 ;;;; This library is free software; you can redistribute it and/or
 ;;;; modify it under the terms of the GNU Lesser General Public
   #:use-module (rnrs bytevectors)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
+  #:use-module (ice-9 format)
   #:use-module (test-suite lib))
 
 \f
+(with-test-prefix "dynamic-pointer"
+
+  (pass-if-exception
+   "error message"
+   '(misc-error . "^Symbol not found")
+   (dynamic-func "does_not_exist___" (dynamic-link))))
+
+\f
 (with-test-prefix "null pointer"
 
   (pass-if "pointer?"
   (pass-if "null-pointer? %null-pointer"
     (null-pointer? %null-pointer))
 
+  (pass-if-exception "dereference-pointer %null-pointer"
+    exception:null-pointer-error
+    (dereference-pointer %null-pointer))
+
   (pass-if-exception "pointer->bytevector %null-pointer"
     exception:null-pointer-error
     (pointer->bytevector %null-pointer 7)))
     (equal? (make-pointer 123) (make-pointer 123)))
 
   (pass-if "equal? modulo finalizer"
-    (let ((finalizer (dynamic-func "scm_is_pair" (dynamic-link))))
-      (equal? (make-pointer 123)
-              (make-pointer 123 finalizer))))
+    (let ((finalizer (false-if-exception
+                      (dynamic-func "scm_is_pair" (dynamic-link)))))
+      (if (not finalizer)
+          (throw 'unresolved)               ;  Windows or a static build
+          (equal? (make-pointer 123)
+                  (make-pointer 123 finalizer)))))
+
+  (pass-if "equal? modulo finalizer (set-pointer-finalizer!)"
+    (let ((finalizer (false-if-exception
+                      (dynamic-func "scm_is_pair" (dynamic-link))))
+          (ptr       (make-pointer 123)))
+      (if (not finalizer)
+          (throw 'unresolved)                ; Windows or a static build
+          (begin
+            (set-pointer-finalizer! ptr finalizer)
+            (equal? (make-pointer 123) ptr)))))
 
   (pass-if "not equal?"
     (not (equal? (make-pointer 123) (make-pointer 456)))))
 
 \f
+(with-test-prefix "pointer<->scm"
+
+  (pass-if "immediates"
+    (equal? (pointer->scm (scm->pointer #\newline))
+            #\newline))
+
+  (pass-if "non-immediates"
+    (equal? (pointer->scm (scm->pointer "Hello, world!"))
+            "Hello, world!")))
+
+\f
 (define-wrapped-pointer-type foo
   foo?
   wrap-foo unwrap-foo
 
   (pass-if "pointer from bits"
     (let* ((bytes (iota (sizeof '*)))
-           (bv    (u8-list->bytevector bytes)))
+           (bv    (u8-list->bytevector bytes))
+           (fold  (case (native-endianness)
+                    ((little) fold-right)
+                    ((big)    fold)
+                    (else     (error "unsupported endianness")))))
       (= (pointer-address
           (make-pointer (bytevector-uint-ref bv 0 (native-endianness)
                                              (sizeof '*))))
-         (fold-right (lambda (byte address)
-                       (+ byte (* 256 address)))
-                     0
-                     bytes))))
+         (fold (lambda (byte address)
+                 (+ byte (* 256 address)))
+               0
+               bytes))))
 
   (pass-if "dereference-pointer"
     (let* ((bytes (iota (sizeof '*)))
-           (bv    (u8-list->bytevector bytes)))
+           (bv    (u8-list->bytevector bytes))
+           (fold  (case (native-endianness)
+                    ((little) fold-right)
+                    ((big)    fold)
+                    (else     (error "unsupported endianness")))))
       (= (pointer-address
           (dereference-pointer (bytevector->pointer bv)))
-         (fold-right (lambda (byte address)
-                       (+ byte (* 256 address)))
-                     0
-                     bytes)))))
+         (fold (lambda (byte address)
+                 (+ byte (* 256 address)))
+               0
+               bytes)))))
 
 \f
 (with-test-prefix "pointer<->string"
 
+  (pass-if-exception "%default-port-conversion-strategy is error"
+    exception:encoding-error
+    (let ((s "χαοσ"))
+      (with-fluids ((%default-port-conversion-strategy 'error))
+        (string->pointer s "ISO-8859-1"))))
+
+  (pass-if "%default-port-conversion-strategy is escape"
+    (let ((s "teĥniko"))
+      (equal? (with-fluids ((%default-port-conversion-strategy 'escape))
+                (pointer->string (string->pointer s "ISO-8859-1")))
+              (format #f "te\\u~4,'0xniko"
+                      (char->integer #\ĥ)))))
+
+  (pass-if "%default-port-conversion-strategy is substitute"
+    (let ((s "teĥniko")
+          (member (negate (negate member))))
+      (member (with-fluids ((%default-port-conversion-strategy 'substitute))
+                (pointer->string (string->pointer s "ISO-8859-1")))
+              '("te?niko"
+
+                ;; This form is found on FreeBSD 8.2 and Darwin 10.8.0.
+                "te^hniko"))))
+
   (pass-if "bijection"
     (let ((s "hello, world"))
       (string=? s (pointer->string (string->pointer s)))))
   (pass-if "bijection [latin1]"
     (with-latin1-locale
       (let ((s "Szép jó napot!"))
-        (string=? s (pointer->string (string->pointer s)))))))
+        (string=? s (pointer->string (string->pointer s))))))
+
+  (pass-if "bijection, utf-8"
+    (let ((s "hello, world"))
+      (string=? s (pointer->string (string->pointer s "utf-8")
+                                   -1 "utf-8"))))
+
+  (pass-if "bijection, utf-8 [latin1]"
+    (let ((s "Szép jó napot!"))
+      (string=? s (pointer->string (string->pointer s "utf-8")
+                                   -1 "utf-8")))))
+
 
 \f
 (with-test-prefix "pointer->procedure"
 
   (define qsort
     ;; Bindings for libc's `qsort' function.
-    (pointer->procedure void
-                        (dynamic-func "qsort" (dynamic-link))
-                        (list '* size_t size_t '*)))
+    ;; On some platforms, such as MinGW, `qsort' is visible only if
+    ;; linking with `-export-dynamic'.  Just skip these tests when it's
+    ;; not visible.
+    (false-if-exception
+     (pointer->procedure void
+                         (dynamic-func "qsort" (dynamic-link))
+                         (list '* size_t size_t '*))))
 
   (define (dereference-pointer-to-byte ptr)
     (let ((b (pointer->bytevector ptr 1)))
     '(7 1 127 3 5 4 77 2 9 0))
 
   (pass-if "qsort"
-    (if (defined? 'procedure->pointer)
+    (if (and qsort (defined? 'procedure->pointer))
         (let* ((called? #f)
                (cmp     (lambda (x y)
                           (set! called? #t)
   (pass-if-exception "qsort, wrong return type"
     exception:wrong-type-arg
 
-    (if (defined? 'procedure->pointer)
+    (if (and qsort (defined? 'procedure->pointer))
         (let* ((cmp     (lambda (x y) #f)) ; wrong return type
                (ptr     (procedure->pointer int cmp (list '* '*)))
                (bv      (u8-list->bytevector input)))
   (pass-if-exception "qsort, wrong arity"
     exception:wrong-num-args
 
-    (if (defined? 'procedure->pointer)
+    (if (and qsort (defined? 'procedure->pointer))
         (let* ((cmp     (lambda (x y z) #f)) ; wrong arity
                (ptr     (procedure->pointer int cmp (list '* '*)))
                (bv      (u8-list->bytevector input)))
                (arg3  (map (cut / <> 4.0) (iota 123 100 4))))
           (equal? (map proc arg1 arg2 arg3)
                   (map proc* arg1 arg2 arg3)))
+        (throw 'unresolved)))
+
+  (pass-if "procedures returning a pointer"
+    (if (defined? 'procedure->pointer)
+        (let* ((called? #f)
+               (proc    (lambda (i) (set! called? #t) (make-pointer i)))
+               (pointer (procedure->pointer '* proc (list int)))
+               (proc*   (pointer->procedure '* pointer (list int)))
+               (result  (proc* 777)))
+          (and called? (equal? result (make-pointer 777))))
+        (throw 'unresolved)))
+
+  (pass-if "procedures returning void"
+    (if (defined? 'procedure->pointer)
+        (let* ((called? #f)
+               (proc    (lambda () (set! called? #t)))
+               (pointer (procedure->pointer void proc '()))
+               (proc*   (pointer->procedure void pointer '())))
+          (proc*)
+          called?)
+        (throw 'unresolved)))
+
+  (pass-if "procedure is retained"
+    ;; The lambda passed to `procedure->pointer' must remain live.
+    (if (defined? 'procedure->pointer)
+        (let* ((ptr   (procedure->pointer int
+                                          (lambda (x) (+ x 7))
+                                          (list int)))
+               (procs (unfold (cut >= <> 10000)
+                              (lambda (i)
+                                (pointer->procedure int ptr (list int)))
+                              1+
+                              0)))
+          (gc) (gc) (gc)
+          (every (cut = <> 9)
+                 (map (lambda (f) (f 2)) procs)))
         (throw 'unresolved))))
 
 \f
     (= (sizeof (list int8 double))
        (+ (alignof double) (sizeof double))))
 
+  (pass-if "sizeof { double, int8 }"
+    (= (sizeof (list double int8))
+       (+ (alignof double) (sizeof double))))
+
   (pass-if "sizeof { short, int, long, pointer }"
     (let ((layout (list short int long '*)))
       (>= (sizeof layout)