Imported Upstream version 1.15.1 upstream/1.15.1
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 31 Oct 2018 02:17:09 +0000 (11:17 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 31 Oct 2018 02:17:09 +0000 (11:17 +0900)
36 files changed:
NEWS
PKG-INFO
README.rst
cairo/cairomodule.c
cairo/compat.h
cairo/context.c
cairo/device.c
cairo/font.c
cairo/glyph.c
cairo/matrix.c
cairo/misc.c
cairo/path.c
cairo/private.h
cairo/region.c
cairo/surface.c
docs/reference/constants.rst
docs/reference/devices.rst
docs/reference/surfaces.rst
setup.cfg
setup.py
tests/__init__.py [new file with mode: 0644]
tests/hypothesis_fspaths.py [new file with mode: 0644]
tests/test_api.py
tests/test_context.py
tests/test_device.py
tests/test_enums.py
tests/test_error.py
tests/test_font.py
tests/test_glyph.py
tests/test_hypothesis.py
tests/test_matrix.py [new file with mode: 0644]
tests/test_path.py [new file with mode: 0644]
tests/test_pattern.py
tests/test_region.py
tests/test_surface.py
tests/test_surface_numpy.py

diff --git a/NEWS b/NEWS
index 91dcb9a..9f33577 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,23 @@
+Since version 1.11.0 Pycairo uses `Semantic Versioning
+<http://semver.org/>`__ i.e. the newest version is the latest stable one.
+
+.. _v1.15.1:
+
+1.15.1 - 2017-08-19
+-------------------
+
+Fixes:
+  * Improved support for Python filesystem paths including
+    :class:`os.PathLike`. See :class:`pathlike` for details.
+  * Various minor fixes
+
+Changes:
+  * Expose :class:`cairo.Path`
+
+Tests:
+  * Improved test coverage from ~70% to ~90%
+
+
 .. _v1.15.0:
 
 1.15.0 - 2017-07-24
index 168dd00..ccde036 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pycairo
-Version: 1.15.0
+Version: 1.15.1
 Summary: Python interface for cairo
 Home-page: https://pycairo.readthedocs.io
 Author: Christoph Reiter
@@ -45,6 +45,9 @@ Description: .. image:: https://cdn.rawgit.com/pygobject/pycairo/master/docs/ima
         .. image:: https://ci.appveyor.com/api/projects/status/9hurdbb19lg2i9xm/branch/master?svg=true
             :target: https://ci.appveyor.com/project/lazka/pycairo/branch/master
         
+        .. image:: https://codecov.io/gh/pygobject/pycairo/branch/master/graph/badge.svg
+          :target: https://codecov.io/gh/pygobject/pycairo
+        
 Platform: UNKNOWN
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python :: 2
index 78f2b01..62d513e 100644 (file)
@@ -36,3 +36,6 @@ For more information visit https://pycairo.readthedocs.io
 
 .. image:: https://ci.appveyor.com/api/projects/status/9hurdbb19lg2i9xm/branch/master?svg=true
     :target: https://ci.appveyor.com/project/lazka/pycairo/branch/master
+
+.. image:: https://codecov.io/gh/pygobject/pycairo/branch/master/graph/badge.svg
+  :target: https://codecov.io/gh/pygobject/pycairo
index 8af0abc..a4bfbd9 100644 (file)
@@ -403,6 +403,9 @@ PYCAIRO_MOD_INIT(_cairo)
   Py_INCREF(&PycairoTextExtents_Type);
   PyModule_AddObject(m, "TextExtents", (PyObject *)&PycairoTextExtents_Type);
 
+  Py_INCREF(&PycairoPath_Type);
+  PyModule_AddObject(m, "Path", (PyObject *)&PycairoPath_Type);
+
 #ifdef CAIRO_HAS_SCRIPT_SURFACE
   Py_INCREF(&PycairoScriptDevice_Type);
   PyModule_AddObject(m, "ScriptDevice", (PyObject *)&PycairoScriptDevice_Type);
index 62fe3d5..7f877d0 100644 (file)
@@ -61,8 +61,6 @@
 #define PYCAIRO_ENC_TEXT_FORMAT "et"
 #define PYCAIRO_DATA_FORMAT "s"
 
-#define PYCAIRO_PyFilenameBase_Type PyBaseString_Type
-
 #define PYCAIRO_Py_hash_t long
 
 #else
@@ -90,8 +88,6 @@
 #define PYCAIRO_ENC_TEXT_FORMAT "es"
 #define PYCAIRO_DATA_FORMAT "y"
 
-#define PYCAIRO_PyFilenameBase_Type PyUnicode_Type
-
 #define PYCAIRO_Py_hash_t Py_hash_t
 
 #endif
index 97675ff..6649b0d 100644 (file)
@@ -380,10 +380,7 @@ pycairo_get_font_options (PycairoContext *o) {
 static PyObject *
 pycairo_get_group_target (PycairoContext *o) {
   cairo_surface_t *surface = cairo_get_group_target (o->ctx);
-  if (surface != NULL)
-    return PycairoSurface_FromSurface (cairo_surface_reference (surface),
-                                      NULL);
-  Py_RETURN_NONE;
+  return PycairoSurface_FromSurface (cairo_surface_reference (surface), NULL);
 }
 
 static PyObject *
index cd8f7c3..383a22e 100644 (file)
@@ -206,48 +206,39 @@ _write_func (void *closure, const unsigned char *data, unsigned int length) {
 
 static PyObject *
 script_device_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
-    PyObject *file, *writer;
+    char *name = NULL;
+    PyObject *file;
     cairo_device_t *device;
 
-    if (!PyArg_ParseTuple(args, "O:ScriptDevice.__new__", &file))
-        return NULL;
-
-    if (PyObject_TypeCheck (file, &PYCAIRO_PyFilenameBase_Type)) {
-        /* filename (str or unicode) argument */
-        char *name = NULL;  /* the encoded filename */
+  if (!PyArg_ParseTuple (args, "O:ScriptDevice.__new__", &file))
+    return NULL;
 
-        if (!PyArg_ParseTuple(args,
-                PYCAIRO_ENC_TEXT_FORMAT ":ScriptDevice.__new__",
-                Py_FileSystemDefaultEncoding, &name))
-          return NULL;
+  if (Pycairo_is_fspath (file)) {
+    if (!PyArg_ParseTuple (args, "O&:ScriptDevice.__new__",
+                           Pycairo_fspath_converter, &name))
+      return NULL;
 
+    Py_BEGIN_ALLOW_THREADS;
+    device = cairo_script_create (name);
+    Py_END_ALLOW_THREADS;
+    PyMem_Free (name);
+    return PycairoDevice_FromDevice (device);
+  } else {
+    if (PyArg_ParseTuple (args, "O&:ScriptDevice.__new__",
+                          Pycairo_writer_converter, &file)) {
         Py_BEGIN_ALLOW_THREADS;
-        device = cairo_script_create (name);
+        device = cairo_script_create_for_stream (_write_func, file);
         Py_END_ALLOW_THREADS;
-
-        PyMem_Free(name);
-        return PycairoDevice_FromDevice (device);
-    }
-
-    /* else: file or file-like object argument */
-    writer = PyObject_GetAttrString (file, "write");
-    if (writer == NULL || !PyCallable_Check (writer)) {
-        Py_XDECREF(writer);
-        PyErr_SetString(PyExc_TypeError,
-            "ScriptDevice argument 1 must be\n"
-            "  a filename (str), or\n"
-            "  a file object, or\n"
-            "  an object that has a \"write\" method (like StringIO)."
-        );
+        return _device_create_with_object (device, file);
+    } else {
+        PyErr_Clear ();
+        PyErr_SetString (PyExc_TypeError,
+                         "ScriptDevice takes one argument which must be "
+                         "a filename, file object, or a file-like object "
+                         "which has a \"write\" method (like StringIO)");
         return NULL;
     }
-    Py_DECREF(writer);
-
-    Py_BEGIN_ALLOW_THREADS;
-    device = cairo_script_create_for_stream (_write_func, file);
-    Py_END_ALLOW_THREADS;
-
-    return _device_create_with_object (device, file);
+  }
 }
 
 static PyObject *
index 9fc02c0..fd96c24 100644 (file)
@@ -618,6 +618,10 @@ font_options_dealloc(PycairoFontOptions *o) {
 
 static PyObject *
 font_options_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
+
+  if (!PyArg_ParseTuple (args, ":FontOptions.__new__"))
+    return NULL;
+
   return PycairoFontOptions_FromFontOptions (cairo_font_options_create());
 }
 
index 51a5e13..eae4a5d 100644 (file)
@@ -107,10 +107,13 @@ static PyObject *
 glyph_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
     double x, y;
     unsigned long index;
-    PyObject *tuple_args, *result;
+    PyObject *pyindex, *tuple_args, *result;
 
-    if (!PyArg_ParseTupleAndKeywords (args, kwds, "kdd:Glyph.__new__",
-            KWDS, &index, &x, &y))
+    if (!PyArg_ParseTupleAndKeywords (args, kwds, "Odd:Glyph.__new__",
+            KWDS, &pyindex, &x, &y))
+        return NULL;
+
+    if (_conv_pyobject_to_ulong (pyindex, &index) < 0)
         return NULL;
 
     tuple_args = Py_BuildValue ("((kdd))", index, x, y);
index 3f9eba8..4b0e462 100644 (file)
@@ -129,8 +129,12 @@ matrix_richcmp (PycairoMatrix *m1, PycairoMatrix *m2, int op) {
   cairo_matrix_t *mx1 = &m1->matrix;
   cairo_matrix_t *mx2 = &m2->matrix;
 
-  if (!PyObject_TypeCheck(m2, &PycairoMatrix_Type) ||
-      !(op == Py_EQ || op == Py_NE)) {
+  if (op != Py_EQ && op != Py_NE) {
+    PyErr_SetString(PyExc_TypeError, "Only support testing for == or !=");
+    return NULL;
+  }
+
+  if (!PyObject_TypeCheck(m2, &PycairoMatrix_Type)) {
     Py_INCREF(Py_NotImplemented);
     return Py_NotImplemented;
   }
index fd1a4a4..0c773ab 100644 (file)
 #include "private.h"
 
 
+/* Returns 1 if the object has the correct file type for a filesystem path.
+ * Parsing it with Pycairo_fspath_converter() might still fail.
+ */
+int
+Pycairo_is_fspath (PyObject *obj) {
+#if PY_MAJOR_VERSION < 3
+    return (PyString_Check (obj) || PyUnicode_Check (obj));
+#elif PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 6
+    PyObject *real = PyOS_FSPath (obj);
+    if (real == NULL) {
+        PyErr_Clear ();
+        return 0;
+    } else {
+        Py_DECREF (real);
+        return 1;
+    }
+#else
+    return (PyBytes_Check (obj) || PyUnicode_Check (obj));
+#endif
+}
+
+/* Converts a Python object to a cairo path. The result needs to be freed with
+ * PyMem_Free().
+ */
+int
+Pycairo_fspath_converter (PyObject *obj, char** result) {
+    char *internal, *buf;
+    PyObject *bytes;
+
+#if defined(MS_WINDOWS) && PY_MAJOR_VERSION < 3
+    PyObject *uni, *other;
+
+    if (PyString_Check (obj)) {
+        uni = PyString_AsDecodedObject (
+            obj, Py_FileSystemDefaultEncoding, "strict");
+        if (uni == NULL)
+            return 0;
+    } else if (PyUnicode_Check (obj)) {
+        uni = obj;
+        Py_INCREF (uni);
+    } else {
+        PyErr_SetString (PyExc_TypeError, "paths must be str/unicode");
+        return 0;
+    }
+
+    bytes = PyUnicode_AsMBCSString (uni);
+    if (bytes == NULL) {
+        Py_DECREF (uni);
+        return 0;
+    }
+
+    if (PyString_AsStringAndSize (bytes, &internal, NULL) == -1) {
+        Py_DECREF (uni);
+        Py_DECREF (bytes);
+        return 0;
+    }
+
+    /* PyUnicode_AsMBCSString doesn't do error handling, so we have to
+     * decode and compare again */
+    other = PyUnicode_DecodeMBCS (internal, PyString_Size (bytes), "strict");
+    if (other == NULL) {
+        Py_DECREF (uni);
+        Py_DECREF (bytes);
+        return 0;
+    }
+
+    if (PyUnicode_Compare (uni, other) != 0) {
+        Py_DECREF (uni);
+        Py_DECREF (bytes);
+        Py_DECREF (other);
+        PyErr_SetString (
+            PyExc_ValueError, "only ANSI paths supported on Windows");
+        return 0;
+    }
+
+    Py_DECREF (uni);
+    Py_DECREF (other);
+
+#elif defined(MS_WINDOWS) && PY_MAJOR_VERSION >= 3
+    PyObject *uni;
+
+    if (PyUnicode_FSDecoder (obj, &uni) == 0)
+        return 0;
+
+    bytes = PyUnicode_AsMBCSString (uni);
+    Py_DECREF (uni);
+    if (bytes == NULL)
+        return 0;
+
+    if (PyBytes_AsStringAndSize (bytes, &internal, NULL) == -1) {
+        Py_DECREF (bytes);
+        return 0;
+    }
+#elif !defined(MS_WINDOWS) && PY_MAJOR_VERSION < 3
+    if (PyUnicode_Check (obj)) {
+        bytes = PyUnicode_AsEncodedString (
+            obj, Py_FileSystemDefaultEncoding, "strict");
+        if (bytes == 0)
+            return 0;
+    } else if (PyString_Check (obj)) {
+        bytes = obj;
+        Py_INCREF (bytes);
+    } else {
+        PyErr_SetString (PyExc_TypeError, "paths must be str/unicode");
+        return 0;
+    }
+
+    if (PyString_AsStringAndSize (bytes, &internal, NULL) == -1) {
+        Py_DECREF (bytes);
+        return 0;
+    }
+#elif !defined(MS_WINDOWS) && PY_MAJOR_VERSION >= 3
+    if (PyUnicode_FSConverter (obj, &bytes) == 0)
+        return 0;
+
+    if (PyBytes_AsStringAndSize (bytes, &internal, NULL) == -1) {
+        Py_DECREF (bytes);
+        return 0;
+    }
+#else
+#error "unsupported"
+#endif
+
+    buf = PyMem_Malloc (strlen (internal) + 1);
+    if (buf == NULL) {
+        Py_DECREF (bytes);
+        PyErr_NoMemory ();
+        return 0;
+    }
+    strcpy (buf, internal);
+    Py_DECREF (bytes);
+    *result = buf;
+    return 1;
+}
+
+/* Verifies that the object has a callable write() method.
+ * Gives a borrowed reference.
+ */
+int
+Pycairo_writer_converter (PyObject *obj, PyObject** file) {
+    PyObject *attr;
+
+    attr = PyObject_GetAttrString (obj, "write");
+    if (attr == NULL)
+        return 0;
+
+    if (!PyCallable_Check (attr)) {
+        Py_DECREF (attr);
+        PyErr_SetString (
+            PyExc_TypeError, "'write' attribute not callable");
+        return 0;
+    }
+
+    Py_DECREF (attr);
+    *file = obj;
+    return 1;
+}
+
+int
+Pycairo_reader_converter (PyObject *obj, PyObject** file) {
+    PyObject *attr;
+
+    attr = PyObject_GetAttrString (obj, "read");
+    if (attr == NULL)
+        return 0;
+
+    if (!PyCallable_Check (attr)) {
+        Py_DECREF (attr);
+        PyErr_SetString (
+            PyExc_TypeError, "'read' attribute not callable");
+        return 0;
+    }
+
+    Py_DECREF (attr);
+    *file = obj;
+    return 1;
+}
+
+int
+Pycairo_fspath_none_converter (PyObject *obj, char** result) {
+    if (obj == Py_None) {
+        *result = NULL;
+        return 1;
+    }
+
+    return Pycairo_fspath_converter (obj, result);
+}
+
 PyObject*
 Pycairo_tuple_getattro (PyObject *self, char **kwds, PyObject *name) {
     PyObject *value, *item;
@@ -91,3 +279,43 @@ Pycairo_richcompare (void* a, void *b, int op)
     Py_INCREF (res);
     return res;
 }
+
+/* NULL on error */
+static PyObject *
+_conv_pyobject_to_pylong (PyObject *pyobj) {
+#if PY_MAJOR_VERSION < 3
+    if (PyInt_Check (pyobj)) {
+        return PyNumber_Long (pyobj);
+    } else if (!PyLong_Check (pyobj)) {
+        PyErr_SetString (PyExc_TypeError, "not of type int or long");
+        return NULL;
+    }
+    Py_INCREF (pyobj);
+    return pyobj;
+#else
+    if (!PyLong_Check (pyobj)) {
+        PyErr_SetString (PyExc_TypeError, "not of type int");
+        return NULL;
+    }
+    Py_INCREF (pyobj);
+    return pyobj;
+#endif
+}
+
+/* -1 on error */
+int
+_conv_pyobject_to_ulong (PyObject *pyobj, unsigned long *result) {
+    unsigned long temp;
+    PyObject *pylong;
+
+    pylong = _conv_pyobject_to_pylong (pyobj);
+    if (pylong == NULL)
+        return -1;
+
+    temp = PyLong_AsUnsignedLong (pylong);
+    if (PyErr_Occurred ())
+        return -1;
+
+    *result = temp;
+    return 0;
+}
index 1e38bbf..38d2818 100644 (file)
@@ -60,17 +60,11 @@ PycairoPath_FromPath (cairo_path_t *path) {
 
 static void
 path_dealloc(PycairoPath *p) {
-#ifdef DEBUG
-  printf("path_dealloc start\n");
-#endif
   if (p->path) {
     cairo_path_destroy(p->path);
     p->path = NULL;
   }
   Py_TYPE(p)->tp_free(p);
-#ifdef DEBUG
-  printf("path_dealloc end\n");
-#endif
 }
 
 static PyObject *
index 26e858b..72340d5 100644 (file)
 #include "pycairo.h"
 #include "compat.h"
 
+int Pycairo_fspath_converter (PyObject *obj, char** result);
+int Pycairo_fspath_none_converter (PyObject *obj, char** result);
+int Pycairo_writer_converter (PyObject *obj, PyObject** file);
+int Pycairo_reader_converter (PyObject *obj, PyObject** file);
+int Pycairo_is_fspath (PyObject *obj);
 
 cairo_glyph_t * _PycairoGlyphs_AsGlyphs (PyObject *py_object, int *num_glyphs);
 int _PyGlyph_AsGlyph (PyObject *pyobj, cairo_glyph_t *glyph);
 int _PyTextCluster_AsTextCluster (PyObject *pyobj,
                                   cairo_text_cluster_t *cluster);
 
+int _conv_pyobject_to_ulong (PyObject *pyobj, unsigned long *result);
+
 PyObject *_Pycairo_Get_Error(void);
 
 PyObject* Pycairo_richcompare (void* a, void *b, int op);
index 167dcbb..8579537 100644 (file)
@@ -93,11 +93,11 @@ rectangle_int_richcompare(PycairoRectangleInt *self,
     PyErr_SetString(PyExc_TypeError, "Only support testing for == or !=");
     return NULL;
   }
-  if (!PyObject_IsInstance((PyObject*)other,
-        (PyObject*)&PycairoRectangleInt_Type)) {
-    res = 0;
-  }
-  else if (
+
+  if (!PyObject_TypeCheck((PyObject*)other, &PycairoRectangleInt_Type)) {
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+  else if (
       self->rectangle_int.x == other->rectangle_int.x &&
       self->rectangle_int.y == other->rectangle_int.y &&
       self->rectangle_int.width == other->rectangle_int.width &&
@@ -230,8 +230,8 @@ region_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
 
     for(i=0; i<rect_size; i++) {
       PyObject *obj_tmp = PySequence_Fast_GET_ITEM(seq, i);
-      if (PyObject_IsInstance(obj_tmp,
-            (PyObject*)&PycairoRectangleInt_Type) != 1) {
+      if (!PyObject_TypeCheck(obj_tmp, &PycairoRectangleInt_Type)) {
+        PyErr_SetString(PyExc_TypeError, "Must be RectangleInt");
         Py_DECREF(seq);
         PyMem_Free(rect);
         return NULL;
@@ -377,8 +377,10 @@ region_richcompare(PycairoRegion *self, PycairoRegion *other, int op) {
     PyErr_SetString(PyExc_TypeError, "Only support testing for == or !=");
     return NULL;
   }
-  if (!PyObject_IsInstance((PyObject*)other, (PyObject*)&PycairoRegion_Type)) {
-    res = 0;
+
+  if (!PyObject_TypeCheck((PyObject*)other, &PycairoRegion_Type)) {
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
   } else {
     res = cairo_region_equal (self->region, other->region);
   }
@@ -409,13 +411,12 @@ region_intersect (PycairoRegion *o, PyObject *args) {
   if (!PyArg_ParseTuple (args, "O:Region.intersect", &other))
     return NULL;
 
-  if (PyObject_IsInstance(other, (PyObject*)&PycairoRegion_Type) == 1) {
+  if (PyObject_TypeCheck(other, &PycairoRegion_Type)) {
       Py_BEGIN_ALLOW_THREADS;
       res = cairo_region_intersect(o->region,
               ((PycairoRegion *)other)->region);
       Py_END_ALLOW_THREADS;
-  } else if (PyObject_IsInstance(other,
-              (PyObject*)&PycairoRectangleInt_Type) == 1) {
+  } else if (PyObject_TypeCheck(other, &PycairoRectangleInt_Type)) {
       Py_BEGIN_ALLOW_THREADS;
       res = cairo_region_intersect_rectangle(o->region,
           &(((PycairoRectangleInt *)other)->rectangle_int));
@@ -437,13 +438,12 @@ region_subtract (PycairoRegion *o, PyObject *args) {
   if (!PyArg_ParseTuple (args, "O:Region.subtract", &other))
     return NULL;
 
-  if (PyObject_IsInstance(other, (PyObject*)&PycairoRegion_Type) == 1) {
+  if (PyObject_TypeCheck(other, &PycairoRegion_Type)) {
       Py_BEGIN_ALLOW_THREADS;
       res = cairo_region_subtract(o->region,
               ((PycairoRegion *)other)->region);
       Py_END_ALLOW_THREADS;
-  } else if (PyObject_IsInstance(other,
-              (PyObject*)&PycairoRectangleInt_Type) == 1) {
+  } else if (PyObject_TypeCheck(other, &PycairoRectangleInt_Type)) {
       Py_BEGIN_ALLOW_THREADS;
       res = cairo_region_subtract_rectangle(o->region,
           &(((PycairoRectangleInt *)other)->rectangle_int));
@@ -464,13 +464,12 @@ region_union (PycairoRegion *o, PyObject *args) {
   if (!PyArg_ParseTuple (args, "O:Region.union", &other))
     return NULL;
 
-  if (PyObject_IsInstance(other, (PyObject*)&PycairoRegion_Type) == 1) {
+  if (PyObject_TypeCheck(other, &PycairoRegion_Type)) {
       Py_BEGIN_ALLOW_THREADS;
       res = cairo_region_union(o->region,
               ((PycairoRegion *)other)->region);
       Py_END_ALLOW_THREADS;
-  } else if (PyObject_IsInstance(other,
-              (PyObject*)&PycairoRectangleInt_Type) == 1) {
+  } else if (PyObject_TypeCheck(other, &PycairoRectangleInt_Type)) {
       Py_BEGIN_ALLOW_THREADS;
       res = cairo_region_union_rectangle(o->region,
           &(((PycairoRectangleInt *)other)->rectangle_int));
@@ -491,13 +490,12 @@ region_xor (PycairoRegion *o, PyObject *args) {
   if (!PyArg_ParseTuple (args, "O:Region.xorg", &other))
     return NULL;
 
-  if (PyObject_IsInstance(other, (PyObject*)&PycairoRegion_Type) == 1) {
+  if (PyObject_TypeCheck(other, &PycairoRegion_Type)) {
       Py_BEGIN_ALLOW_THREADS;
       res = cairo_region_xor(o->region,
               ((PycairoRegion *)other)->region);
       Py_END_ALLOW_THREADS;
-  } else if (PyObject_IsInstance(other,
-              (PyObject*)&PycairoRectangleInt_Type) == 1) {
+  } else if (PyObject_TypeCheck(other, &PycairoRectangleInt_Type)) {
       Py_BEGIN_ALLOW_THREADS;
       res = cairo_region_xor_rectangle(o->region,
           &(((PycairoRectangleInt *)other)->rectangle_int));
index ffd7730..74107e6 100644 (file)
@@ -387,41 +387,38 @@ surface_create_similar_image (PycairoSurface *o, PyObject *args) {
 static PyObject *
 surface_write_to_png (PycairoSurface *o, PyObject *args) {
   cairo_status_t status;
+  char *name = NULL;
   PyObject *file;
 
-  if (!PyArg_ParseTuple(args, "O:Surface.write_to_png", &file))
+  if (!PyArg_ParseTuple (args, "O:Surface.write_to_png", &file))
     return NULL;
 
-  if (PyObject_TypeCheck (file, &PYCAIRO_PyFilenameBase_Type)) {
-    /* filename (str or unicode) argument */
-    char *name = NULL;  /* the encoded filename */
-
-    if (!PyArg_ParseTuple(args, PYCAIRO_ENC_TEXT_FORMAT ":Surface.write_to_png",
-                         Py_FileSystemDefaultEncoding, &name))
+  if (Pycairo_is_fspath (file)) {
+    if (!PyArg_ParseTuple (args, "O&:Surface.write_to_png",
+                           Pycairo_fspath_converter, &name))
       return NULL;
-
     Py_BEGIN_ALLOW_THREADS;
     status = cairo_surface_write_to_png (o->surface, name);
     Py_END_ALLOW_THREADS;
-
-    PyMem_Free(name);
-
-  } else {  /* file or file-like object argument */
-    PyObject* writer = PyObject_GetAttrString (file, "write");
-    if (writer == NULL || !PyCallable_Check (writer)) {
-      Py_XDECREF(writer);
-      PyErr_SetString(PyExc_TypeError,
-"Surface.write_to_png takes one argument which must be a filename (str), file "
-"object, or a file-like object which has a \"write\" method (like StringIO)");
+    PyMem_Free (name);
+  } else {
+    if (PyArg_ParseTuple (args, "O&:Surface.write_to_png",
+                          Pycairo_writer_converter, &file)) {
+      Py_BEGIN_ALLOW_THREADS;
+      status = cairo_surface_write_to_png_stream (o->surface, _write_func,
+                                                  file);
+      Py_END_ALLOW_THREADS;
+    } else {
+      PyErr_Clear ();
+      PyErr_SetString (PyExc_TypeError,
+                       "Surface.write_to_png takes one argument which must be "
+                       "a filename, file object, or a file-like object "
+                       "which has a \"write\" method (like StringIO)");
       return NULL;
     }
-    Py_DECREF(writer);
-    Py_BEGIN_ALLOW_THREADS;
-    status = cairo_surface_write_to_png_stream (o->surface, _write_func,
-                                               file);
-    Py_END_ALLOW_THREADS;
   }
-  RETURN_NULL_IF_CAIRO_ERROR(status);
+
+  RETURN_NULL_IF_CAIRO_ERROR (status);
   Py_RETURN_NONE;
 }
 #endif  /* CAIRO_HAS_PNG_FUNCTIONS */
@@ -567,17 +564,11 @@ surface_map_to_image (PycairoSurface *self, PyObject *args) {
   PyObject *pyextents, *pymapped;
   cairo_rectangle_int_t *extents;
   cairo_surface_t *mapped_surface;
-  int result;
 
   if (!PyArg_ParseTuple(args, "O:Surface.map_to_image", &pyextents))
     return NULL;
 
-  result = PyObject_IsInstance (
-    pyextents, (PyObject*)&PycairoRectangleInt_Type);
-
-  if (result == -1) {
-    return NULL;
-  } else if (result == 1) {
+  if (PyObject_TypeCheck (pyextents, &PycairoRectangleInt_Type)) {
     extents = &(((PycairoRectangleInt *)pyextents)->rectangle_int);
   } else {
     if (pyextents == Py_None) {
@@ -853,7 +844,7 @@ _read_func (void *closure, unsigned char *data, unsigned int length) {
     goto end;
   }
   ret = PYCAIRO_PyBytes_AsStringAndSize(pystr, &buffer, &str_length);
-  if (ret == -1 || str_length < length) {
+  if (ret == -1 || str_length < (Py_ssize_t)length) {
     PyErr_Clear();
     goto end;
   }
@@ -869,42 +860,39 @@ _read_func (void *closure, unsigned char *data, unsigned int length) {
 /* METH_CLASS */
 static PyObject *
 image_surface_create_from_png (PyTypeObject *type, PyObject *args) {
-  cairo_surface_t *is;
-  PyObject *reader, *file;
+  cairo_surface_t *image_surface;
+  PyObject *file;
+  char *name;
 
-  if (!PyArg_ParseTuple(args, "O:ImageSurface.create_from_png", &file))
+  if (!PyArg_ParseTuple (args, "O:ImageSurface.create_from_png", &file))
     return NULL;
 
-  if (PyObject_TypeCheck (file, &PYCAIRO_PyFilenameBase_Type)) {
-    char *name = NULL;  /* the encoded filename */
-
-    if (!PyArg_ParseTuple(args, PYCAIRO_ENC_TEXT_FORMAT ":Surface.create_from_png",
-                         Py_FileSystemDefaultEncoding, &name))
+  if (Pycairo_is_fspath (file)) {
+    if (!PyArg_ParseTuple(args, "O&:ImageSurface.create_from_png",
+                          Pycairo_fspath_converter, &name))
       return NULL;
 
     Py_BEGIN_ALLOW_THREADS;
-    is = cairo_image_surface_create_from_png (name);
+    image_surface = cairo_image_surface_create_from_png (name);
     Py_END_ALLOW_THREADS;
-
     PyMem_Free(name);
-    return PycairoSurface_FromSurface (is, NULL);
-  }
-
-  /* file or file-like object argument */
-  reader = PyObject_GetAttrString (file, "read");
-  if (reader == NULL || !PyCallable_Check (reader)) {
-    Py_XDECREF(reader);
-    PyErr_SetString(PyExc_TypeError,
-"ImageSurface.create_from_png argument must be a filename (str), file object, "
-"or an object that has a \"read\" method (like StringIO)");
-    return NULL;
+    return PycairoSurface_FromSurface (image_surface, NULL);
+  } else {
+    if (PyArg_ParseTuple (args, "O&:ImageSurface.create_from_png",
+                          Pycairo_reader_converter, &file)) {
+      Py_BEGIN_ALLOW_THREADS;
+      image_surface = cairo_image_surface_create_from_png_stream (
+        _read_func, file);
+      Py_END_ALLOW_THREADS;
+      return PycairoSurface_FromSurface (image_surface, NULL);
+    } else {
+      PyErr_SetString(PyExc_TypeError,
+                      "ImageSurface.create_from_png argument must be a "
+                      "filename (str), file object, or an object that has a "
+                      "\"read\" method (like StringIO)");
+      return NULL;
+    }
   }
-  Py_DECREF(reader);
-
-  Py_BEGIN_ALLOW_THREADS;
-  is = cairo_image_surface_create_from_png_stream (_read_func, file);
-  Py_END_ALLOW_THREADS;
-  return PycairoSurface_FromSurface (is, NULL);
 }
 #endif /* CAIRO_HAS_PNG_FUNCTIONS */
 
@@ -1196,56 +1184,44 @@ PyTypeObject PycairoMappedImageSurface_Type = {
 static PyObject *
 pdf_surface_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
   double width_in_points, height_in_points;
-  PyObject *file, *writer;
+  PyObject *file;
   cairo_surface_t *sfc;
+  char *name;
 
-  if (!PyArg_ParseTuple(args, "Odd:PDFSurface.__new__",
-                       &file, &width_in_points, &height_in_points))
+  if (!PyArg_ParseTuple (args, "Odd:PDFSurface.__new__",
+                         &file, &width_in_points, &height_in_points))
     return NULL;
 
-  if (file == Py_None) {
-    Py_BEGIN_ALLOW_THREADS;
-    sfc = cairo_pdf_surface_create (NULL, width_in_points, height_in_points);
-    Py_END_ALLOW_THREADS;
-    return PycairoSurface_FromSurface (sfc, NULL);
-
-  }else if (PyObject_TypeCheck (file, &PYCAIRO_PyFilenameBase_Type)) {
-    /* filename (str or unicode) argument */
-    char *name = NULL;  /* the encoded filename */
-
-    if (!PyArg_ParseTuple(args, PYCAIRO_ENC_TEXT_FORMAT "dd:PDFSurface.__new__",
-                         Py_FileSystemDefaultEncoding,
-                         &name, &width_in_points, &height_in_points))
+  if (Pycairo_is_fspath (file) || file == Py_None) {
+    if (!PyArg_ParseTuple (args, "O&dd:PDFSurface.__new__",
+                           Pycairo_fspath_none_converter, &name,
+                           &width_in_points, &height_in_points))
       return NULL;
 
     Py_BEGIN_ALLOW_THREADS;
     sfc = cairo_pdf_surface_create (name, width_in_points, height_in_points);
     Py_END_ALLOW_THREADS;
-
     PyMem_Free(name);
     return PycairoSurface_FromSurface (sfc, NULL);
+  } else {
+    if (PyArg_ParseTuple (args, "O&dd:PDFSurface.__new__",
+                          Pycairo_writer_converter, &file,
+                          &width_in_points, &height_in_points)) {
+      Py_BEGIN_ALLOW_THREADS;
+      sfc = cairo_pdf_surface_create_for_stream (
+        _write_func, file, width_in_points, height_in_points);
+      Py_END_ALLOW_THREADS;
+      return _surface_create_with_object (sfc, file);
+    } else {
+      PyErr_Clear ();
+      PyErr_SetString(PyExc_TypeError,
+                      "PDFSurface argument 1 must be "
+                      "None, or a filename (str), or "
+                      "a file object, or an object that has a "
+                      "\"write\" method (like StringIO).");
+      return NULL;
+    }
   }
-
-  /* file or file-like object argument */
-  writer = PyObject_GetAttrString (file, "write");
-  if (writer == NULL || !PyCallable_Check (writer)) {
-    Py_XDECREF(writer);
-    PyErr_SetString(PyExc_TypeError,
-"PDFSurface argument 1 must be\n"
-"  None, or\n"
-"  a filename (str), or\n"
-"  a file object, or\n"
-"  an object that has a \"write\" method (like StringIO)."
-                   );
-    return NULL;
-  }
-  Py_DECREF(writer);
-
-  Py_BEGIN_ALLOW_THREADS;
-  sfc = cairo_pdf_surface_create_for_stream (_write_func, file,
-                                            width_in_points, height_in_points);
-  Py_END_ALLOW_THREADS;
-  return _surface_create_with_object (sfc, file);
 }
 
 static PyObject *
@@ -1471,55 +1447,44 @@ PyTypeObject PycairoScriptSurface_Type = {
 static PyObject *
 ps_surface_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
   double width_in_points, height_in_points;
-  PyObject *file, *writer;
+  PyObject *file;
   cairo_surface_t *sfc;
+  char *name;
 
-  if (!PyArg_ParseTuple(args, "Odd:PSSurface.__new__",
-                       &file, &width_in_points, &height_in_points))
+  if (!PyArg_ParseTuple (args, "Odd:PSSurface.__new__",
+                         &file, &width_in_points, &height_in_points))
     return NULL;
 
-  if (file == Py_None) {
-    Py_BEGIN_ALLOW_THREADS;
-    sfc = cairo_ps_surface_create (NULL, width_in_points, height_in_points);
-    Py_END_ALLOW_THREADS;
-    return PycairoSurface_FromSurface (sfc, NULL);
-
-  }else if (PyObject_TypeCheck (file, &PYCAIRO_PyFilenameBase_Type)) {
-    /* filename (str or unicode) argument */
-    char *name = NULL;  /* the encoded filename */
-
-    if (!PyArg_ParseTuple(args, PYCAIRO_ENC_TEXT_FORMAT "dd:PSSurface.__new__",
-                         Py_FileSystemDefaultEncoding,
-                         &name, &width_in_points, &height_in_points))
+  if (Pycairo_is_fspath (file) || file == Py_None) {
+    if (!PyArg_ParseTuple (args, "O&dd:PSSurface.__new__",
+                           Pycairo_fspath_none_converter, &name,
+                           &width_in_points, &height_in_points))
       return NULL;
 
     Py_BEGIN_ALLOW_THREADS;
     sfc = cairo_ps_surface_create (name, width_in_points, height_in_points);
     Py_END_ALLOW_THREADS;
-
     PyMem_Free(name);
     return PycairoSurface_FromSurface (sfc, NULL);
+  } else {
+    if (PyArg_ParseTuple (args, "O&dd:PSSurface.__new__",
+                          Pycairo_writer_converter, &file,
+                          &width_in_points, &height_in_points)) {
+      Py_BEGIN_ALLOW_THREADS;
+      sfc = cairo_ps_surface_create_for_stream (
+        _write_func, file, width_in_points, height_in_points);
+      Py_END_ALLOW_THREADS;
+      return _surface_create_with_object (sfc, file);
+    } else {
+      PyErr_Clear ();
+      PyErr_SetString(PyExc_TypeError,
+                      "PSSurface argument 1 must be "
+                      "None, or a filename (str), or "
+                      "a file object, or an object that has a "
+                      "\"write\" method (like StringIO).");
+      return NULL;
+    }
   }
-  /* else: file or file-like object argument */
-  writer = PyObject_GetAttrString (file, "write");
-  if (writer == NULL || !PyCallable_Check (writer)) {
-    Py_XDECREF(writer);
-    PyErr_SetString(PyExc_TypeError,
-"PSSurface argument 1 must be\n"
-"  None, or\n"
-"  a filename (str), or\n"
-"  a file object, or\n"
-"  an object that has a \"write\" method (like StringIO)."
-                   );
-    return NULL;
-  }
-  Py_DECREF(writer);
-
-  Py_BEGIN_ALLOW_THREADS;
-  sfc = cairo_ps_surface_create_for_stream (_write_func, file,
-                                           width_in_points, height_in_points);
-  Py_END_ALLOW_THREADS;
-  return _surface_create_with_object (sfc, file);
 }
 
 static PyObject *
@@ -1817,55 +1782,44 @@ PyTypeObject PycairoRecordingSurface_Type = {
 static PyObject *
 svg_surface_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
   double width_in_points, height_in_points;
-  PyObject *file, *writer;
+  PyObject *file;
   cairo_surface_t *sfc;
+  char *name;
 
-  if (!PyArg_ParseTuple(args, "Odd:SVGSurface.__new__",
-                       &file, &width_in_points, &height_in_points))
+  if (!PyArg_ParseTuple (args, "Odd:SVGSurface.__new__",
+                         &file, &width_in_points, &height_in_points))
     return NULL;
 
-  if (file == Py_None) {
-    Py_BEGIN_ALLOW_THREADS;
-    sfc = cairo_svg_surface_create (NULL, width_in_points, height_in_points);
-    Py_END_ALLOW_THREADS;
-    return PycairoSurface_FromSurface (sfc, NULL);
-
-  }else if (PyObject_TypeCheck (file, &PYCAIRO_PyFilenameBase_Type)) {
-    /* filename (str or unicode) argument */
-    char *name = NULL;  /* the encoded filename */
-
-    if (!PyArg_ParseTuple(args, PYCAIRO_ENC_TEXT_FORMAT "dd:SVGSurface.__new__",
-                         Py_FileSystemDefaultEncoding,
-                         &name, &width_in_points, &height_in_points))
+  if (Pycairo_is_fspath (file) || file == Py_None) {
+    if (!PyArg_ParseTuple (args, "O&dd:SVGSurface.__new__",
+                           Pycairo_fspath_none_converter, &name,
+                           &width_in_points, &height_in_points))
       return NULL;
 
     Py_BEGIN_ALLOW_THREADS;
     sfc = cairo_svg_surface_create (name, width_in_points, height_in_points);
     Py_END_ALLOW_THREADS;
-
     PyMem_Free(name);
     return PycairoSurface_FromSurface (sfc, NULL);
+  } else {
+    if (PyArg_ParseTuple (args, "O&dd:SVGSurface.__new__",
+                          Pycairo_writer_converter, &file,
+                          &width_in_points, &height_in_points)) {
+      Py_BEGIN_ALLOW_THREADS;
+      sfc = cairo_svg_surface_create_for_stream (
+        _write_func, file, width_in_points, height_in_points);
+      Py_END_ALLOW_THREADS;
+      return _surface_create_with_object (sfc, file);
+    } else {
+      PyErr_Clear ();
+      PyErr_SetString(PyExc_TypeError,
+                      "SVGSurface argument 1 must be "
+                      "None, or a filename (str), or "
+                      "a file object, or an object that has a "
+                      "\"write\" method (like StringIO).");
+      return NULL;
+    }
   }
-  /* else: file or file-like object argument */
-  writer = PyObject_GetAttrString (file, "write");
-  if (writer == NULL || !PyCallable_Check (writer)) {
-    Py_XDECREF(writer);
-    PyErr_SetString(PyExc_TypeError,
-"SVGSurface argument 1 must be\n"
-"  None, or\n"
-"  a filename (str), or\n"
-"  a file object, or\n"
-"  an object that has a \"write\" method (like StringIO)."
-                   );
-    return NULL;
-  }
-  Py_DECREF(writer);
-
-  Py_BEGIN_ALLOW_THREADS;
-  sfc = cairo_svg_surface_create_for_stream (_write_func, file,
-                                            width_in_points, height_in_points);
-  Py_END_ALLOW_THREADS;
-  return _surface_create_with_object (sfc, file);
 }
 
 static PyObject *
index 1cc5d3b..db35ba6 100644 (file)
@@ -123,3 +123,15 @@ Other Classes and Functions
     This type only exists for documentation purposes. It represents
     :obj:`python:str`/:obj:`python:unicode` under Python 2 and
     :obj:`python3:str` under Python 3.
+
+
+.. class:: pathlike()
+
+    This type only exists for documentation purposes. It represents everything
+    Python allows as a filesystem path except on Windows where only ANSI paths
+    are supported. To use Unicode paths on Windows most functions take an
+    already open file object which you can create from a Unicode path and then
+    pass to pycairo instead.
+
+    .. versionadded:: 1.15.1
+        Older versions only supported a subset of :obj:`str` paths
index 9144a0b..fe9fe0f 100644 (file)
@@ -83,7 +83,7 @@ class ScriptDevice(:class:`Device`)
 .. class:: ScriptDevice(fobj)
 
     :param fobj: a filename or writable file object.
-    :type fobj: :obj:`text`, file or file-like object
+    :type fobj: :obj:`pathlike`, file or file-like object
 
     Creates a output device for emitting the script, used when creating the
     individual surfaces.
index 46a3e79..a9b7baf 100644 (file)
@@ -280,7 +280,7 @@ class Surface()
    .. method:: write_to_png(fobj)
 
       :param fobj: the file to write to
-      :type fobj: filename (:obj:`text`), file or file-like object
+      :type fobj: filename (:obj:`pathlike`), file or file-like object
       :raises: :exc:`MemoryError` if memory could not be allocated for the operation
 
                :exc:`IOError` if an I/O error occurs while attempting to write
@@ -476,7 +476,8 @@ those defined in :class:`cairo.Format`.
 
    .. classmethod:: create_from_png(fobj)
 
-      :param fobj: a filename, file, or file-like object of the PNG to load.
+      :param fobj:
+        a :obj:`pathlike`, file, or file-like object of the PNG to load.
       :returns: a new *ImageSurface* initialized the contents to the given
         PNG file.
 
@@ -523,7 +524,7 @@ multi-page vector surface backend.
 .. class:: PDFSurface(fobj, width_in_points, height_in_points)
 
    :param fobj: a filename or writable file object. None may be used to specify no output. This will generate a *PDFSurface* that may be queried and used as a source, without generating a temporary file.
-   :type fobj: None, :obj:`text`, file or file-like object
+   :type fobj: None, :obj:`pathlike`, file or file-like object
    :param width_in_points: width of the surface, in points
      (1 point == 1/72.0 inch)
    :type  width_in_points: float
@@ -602,7 +603,7 @@ is a multi-page vector surface backend.
 .. class:: PSSurface(fobj, width_in_points, height_in_points)
 
    :param fobj: a filename or writable file object. None may be used to specify no output. This will generate a *PSSurface* that may be queried and used as a source, without generating a temporary file.
-   :type fobj: None, :obj:`text`, file or file-like object
+   :type fobj: None, :obj:`pathlike`, file or file-like object
    :param width_in_points: width of the surface, in points
      (1 point == 1/72.0 inch)
    :type  width_in_points: float
@@ -894,7 +895,7 @@ multi-page vector surface backend
 .. class:: SVGSurface(fobj, width_in_points, height_in_points)
 
    :param fobj: a filename or writable file object. None may be used to specify no output. This will generate a *SVGSurface* that may be queried and used as a source, without generating a temporary file.
-   :type fobj: None, :obj:`text`, file or file-like object
+   :type fobj: None, :obj:`pathlike`, file or file-like object
    :param width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
    :type  width_in_points: float
    :param height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
index a4b23e5..d318d3d 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,8 @@
 [flake8]
 ignore=E402
-builtins=buffer
+builtins=buffer,unichr
+
+[coverage:run]
+include=
+    cairo/*
+    tests/*
index e1cd916..190c3be 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -8,7 +8,7 @@ import errno
 from distutils.core import Extension, setup, Command, Distribution
 
 
-PYCAIRO_VERSION = '1.15.0'
+PYCAIRO_VERSION = '1.15.1'
 CAIRO_VERSION_REQUIRED = '1.13.1'
 XPYB_VERSION_REQUIRED = '1.3'
 
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/hypothesis_fspaths.py b/tests/hypothesis_fspaths.py
new file mode 100644 (file)
index 0000000..1b21727
--- /dev/null
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+# Copyright 2017 Christoph Reiter
+#
+# 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.
+
+import os
+import sys
+
+from hypothesis.strategies import composite, sampled_from, lists, \
+    integers, binary, randoms
+
+
+class _PathLike(object):
+
+    def __init__(self, value):
+        self._value = value
+
+    def __fspath__(self):
+        return self._value
+
+
+@composite
+def fspaths(draw, allow_pathlike=True):
+    """A hypothesis strategy which gives valid path values.
+
+    Valid path values are everything which when passed to open() will not raise
+    ValueError or TypeError (but might raise OSError due to file system or
+    operating system restrictions).
+
+    Args:
+        allow_pathlike (bool):
+            If the result can be a pathlike (see :class:`os.PathLike`)
+    """
+
+    s = []
+
+    if os.name == "nt":
+        if sys.version_info[0] == 3:
+            unichr_ = chr
+        else:
+            unichr_ = unichr
+
+        hight_surrogate = integers(
+            min_value=0xD800, max_value=0xDBFF).map(lambda i: unichr_(i))
+        low_surrogate = integers(
+            min_value=0xDC00, max_value=0xDFFF).map(lambda i: unichr_(i))
+        uni_char = integers(
+            min_value=1, max_value=sys.maxunicode).map(lambda i: unichr_(i))
+        any_char = sampled_from([
+            draw(uni_char), draw(hight_surrogate), draw(low_surrogate)])
+        any_text = lists(any_char).map(lambda l: u"".join(l))
+
+        windows_path_text = any_text
+        s.append(windows_path_text)
+
+        def text_to_bytes(path):
+            fs_enc = sys.getfilesystemencoding()
+            try:
+                return path.encode(fs_enc, "surrogatepass")
+            except UnicodeEncodeError:
+                return path.encode(fs_enc, "replace")
+
+        windows_path_bytes = windows_path_text.map(text_to_bytes)
+        s.append(windows_path_bytes)
+    else:
+        unix_path_bytes = binary().map(lambda b: b.replace(b"\x00", b" "))
+        s.append(unix_path_bytes)
+
+        if sys.version_info[0] == 3:
+            unix_path_text = unix_path_bytes.map(
+                lambda b: b.decode(
+                    sys.getfilesystemencoding(), "surrogateescape"))
+        else:
+            unix_path_text = unix_path_bytes.map(
+                lambda b: b.decode(
+                    sys.getfilesystemencoding(), "ignore"))
+
+        r = draw(randoms())
+
+        def shuffle_text(t):
+            l = list(t)
+            r.shuffle(l)
+            return u"".join(l)
+
+        s.append(unix_path_text.map(shuffle_text))
+
+    result = draw(sampled_from(list(map(draw, s))))
+
+    if allow_pathlike and hasattr(os, "fspath"):
+        result = draw(sampled_from([result, _PathLike(result)]))
+
+    return result
index a83f2a4..0109fc7 100644 (file)
@@ -16,10 +16,10 @@ import cairo
 import pytest
 import py.test as test
 
-try:
-    long
-except NameError:
-    long = int
+
+def test_version():
+    cairo.cairo_version()
+    cairo.cairo_version_string()
 
 
 def test_show_unicode_text():
@@ -54,12 +54,6 @@ def test_unicode_filenames():
         shutil.rmtree(dirname)
 
 
-def test_ps_surface_level_to_string():
-    level_id = cairo.PSSurface.level_to_string(cairo.PS_LEVEL_2)
-    assert isinstance(level_id, str)
-    assert cairo.PSSurface.ps_level_to_string(cairo.PS_LEVEL_2) == level_id
-
-
 def test_surface_has_show_text_glyphs():
     surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
     assert not surface.has_show_text_glyphs()
@@ -68,153 +62,36 @@ def test_surface_has_show_text_glyphs():
         surface.has_show_text_glyphs()
 
 
-def test_surface_create_for_rectangle():
-    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
-    new = surface.create_for_rectangle(0, 0, 10, 10)
-    assert new
-    assert isinstance(new, cairo.Surface)
-
-    with pytest.raises(cairo.Error) as excinfo:
-        surface.create_for_rectangle(0, 0, 10, -1)
-    assert excinfo.value.status == cairo.STATUS_INVALID_SIZE
-
-
-def test_context_in_clip():
-    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
-    context = cairo.Context(surface)
-    assert context.in_clip(50, 50)
-    context.clip()
-    assert not context.in_clip(50, 50)
-    context.reset_clip()
-    assert context.in_clip(50, 50)
-
-
-def test_surface_create_similar_image():
-    surface = cairo.PDFSurface(None, 1, 1)
-    image = surface.create_similar_image(cairo.FORMAT_ARGB32, 24, 42)
-    assert image
-    assert isinstance(image, cairo.ImageSurface)
-    del surface
-    assert image.get_width() == 24
-    assert image.get_height() == 42
-
-
-def test_pdf_surface_restrict_to_version():
-    surface = cairo.PDFSurface(None, 10, 10)
-    surface.restrict_to_version(cairo.PDF_VERSION_1_4)
-    surface.finish()
-    with pytest.raises(cairo.Error):
-        surface.restrict_to_version(cairo.PDF_VERSION_1_5)
-
-
-def test_pdf_version_to_string():
-    ver = cairo.PDFSurface.version_to_string(cairo.PDF_VERSION_1_4)
-    assert ver and isinstance(ver, str)
-    with pytest.raises(ValueError):
-        cairo.PDFSurface.version_to_string(-1)
-
-
 def test_context():
-    if cairo.HAS_IMAGE_SURFACE:
-        f, w, h = cairo.FORMAT_ARGB32, 100, 100
-        s = cairo.ImageSurface(f, w, h)
-        ctx = cairo.Context(s)
-        ctx.set_source_rgb(1.0, 1.0, 1.0)
-        ctx.set_operator(cairo.OPERATOR_SOURCE)
-        ctx.paint()
-
-
-def test_matrix():
-    m = cairo.Matrix()
-    m.rotate(10)
-    m.scale(1.5, 2.5)
-    m.translate(10, 20)
-
-    with pytest.raises(TypeError):
-        m * 42
-
-    with pytest.raises(TypeError):
-        m + 42
-
-    assert m != 42
-    assert m == m
-    assert m != cairo.Matrix()
-
-
-def test_matrix_properties():
-    m = cairo.Matrix(*range(6))
-    assert [m.xx, m.yx, m.xy, m.yy, m.x0, m.y0] == list(range(6))
-    m.xx = 42
-    assert m.xx == 42
-    m.scale(2, 2)
-    assert m.xx == 84
-
-
-def test_path():
-    # AttributeError: 'module' object has no attribute 'Path'
-    test.raises(AttributeError, "p = cairo.Path()")
-    # see examples/warpedtext.py
-
-
-def test_pattern():
-    # TypeError: The Pattern type cannot be instantiated
-    test.raises(TypeError, "p = cairo.Pattern()")
-
-    r, g, b, a = 0.1, 0.2, 0.3, 0.4
-    p = cairo.SolidPattern(r, g, b, a)
-    assert p.get_rgba() == (r, g, b, a)
-
-    # SurfacePattern
-
-    # TypeError: The Gradient type cannot be instantiated
-    test.raises(TypeError, "p = cairo.Gradient()")
-
-    x0, y0, x1, y1 = 0.0, 0.0, 0.0, 1.0
-    p = cairo.LinearGradient(x0, y0, x1, y1)
-    assert p.get_linear_points() == (x0, y0, x1, y1)
-    p.add_color_stop_rgba(1, 0, 0, 0, 1)
-    p.add_color_stop_rgba(0, 1, 1, 1, 1)
-
-    cx0, cy0, radius0, cx1, cy1, radius1 = 1.0, 1.0, 1.0, 2.0, 2.0, 1.0
-    p = cairo.RadialGradient(cx0, cy0, radius0, cx1, cy1, radius1)
-    assert p.get_radial_circles() == (cx0, cy0, radius0, cx1, cy1, radius1)
-    p.add_color_stop_rgba(0, 1, 1, 1, 1)
-    p.add_color_stop_rgba(1, 0, 0, 0, 1)
-
-
-def test_pattern_filter():
-    pattern = cairo.SolidPattern(1, 2, 3)
-    assert pattern.get_filter() == cairo.FILTER_GOOD
-    pattern.set_filter(cairo.FILTER_NEAREST)
-    assert pattern.get_filter() == cairo.FILTER_NEAREST
+    f, w, h = cairo.FORMAT_ARGB32, 100, 100
+    s = cairo.ImageSurface(f, w, h)
+    ctx = cairo.Context(s)
+    ctx.set_source_rgb(1.0, 1.0, 1.0)
+    ctx.set_operator(cairo.OPERATOR_SOURCE)
+    ctx.paint()
 
 
 def test_surface():
     # TypeError: The Surface type cannot be instantiated
     test.raises(TypeError, "s = cairo.Surface()")
 
-    if cairo.HAS_IMAGE_SURFACE:
-        f, w, h = cairo.FORMAT_ARGB32, 100, 100
-        s = cairo.ImageSurface(f, w, h)
-        assert s.get_format() == f
-        assert s.get_width() == w
-        assert s.get_height() == h
+    f, w, h = cairo.FORMAT_ARGB32, 100, 100
+    s = cairo.ImageSurface(f, w, h)
+    assert s.get_format() == f
+    assert s.get_width() == w
+    assert s.get_height() == h
 
-    if cairo.HAS_PDF_SURFACE:
-        f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100
-        s = cairo.PDFSurface(f, w, h)
+    f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100
+    s = cairo.PDFSurface(f, w, h)
 
-    if cairo.HAS_PS_SURFACE:
-        f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100
-        s = cairo.PSSurface(f, w, h)
+    f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100
+    s = cairo.PSSurface(f, w, h)
 
-    if cairo.HAS_RECORDING_SURFACE:
-        s = cairo.RecordingSurface(cairo.CONTENT_COLOR, None)
-        s = cairo.RecordingSurface(cairo.CONTENT_COLOR, (1, 1, 10, 10))
+    s = cairo.RecordingSurface(cairo.CONTENT_COLOR, None)
+    s = cairo.RecordingSurface(cairo.CONTENT_COLOR, (1, 1, 10, 10))
 
-    if cairo.HAS_SVG_SURFACE:
-        f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100
-        s = cairo.SVGSurface(f, w, h)
+    f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100
+    s = cairo.SVGSurface(f, w, h)
 
 
 def test_surface_destroy_before_context():
@@ -263,28 +140,6 @@ def test_image_surface_get_data():
     assert newbuf[0:1] == b"\x00"
 
 
-def test_image_surface_create_for_data():
-    format_ = cairo.FORMAT_ARGB32
-    surface = cairo.ImageSurface(format_, 3, 3)
-    ctx = cairo.Context(surface)
-    ctx.paint()
-    surface.flush()
-    buf = surface.get_data()
-
-    new = cairo.ImageSurface.create_for_data(buf, format_, 3, 3)
-    assert new.get_data() == buf
-
-    with pytest.raises(ValueError):
-        cairo.ImageSurface.create_for_data(buf, format_, 3, -1)
-    with pytest.raises(ValueError):
-        cairo.ImageSurface.create_for_data(buf, format_, -1, 3)
-
-    with pytest.raises(cairo.Error) as excinfo:
-        cairo.ImageSurface.create_for_data(buf, format_, 3, 3, 3)
-
-    assert excinfo.value.status == cairo.STATUS_INVALID_STRIDE
-
-
 def test_surface_file_obj_error():
     class Fail(object):
 
@@ -404,22 +259,6 @@ def test_constants():
     assert cairo.SVG_VERSION_1_2 == 1
 
 
-def test_surface_get_set_mime_data():
-    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
-    assert surface.get_mime_data("foo") is None
-    assert surface.get_mime_data(cairo.MIME_TYPE_JPEG) is None
-
-    surface.set_mime_data("foo", b"bar")
-    assert surface.get_mime_data("foo") == b"bar"
-    surface.set_mime_data("foo", None)
-    assert surface.get_mime_data("foo") is None
-
-    surface.set_mime_data(cairo.MIME_TYPE_JPEG, b"\x00quux\x00")
-    assert surface.get_mime_data(cairo.MIME_TYPE_JPEG)[:] == b"\x00quux\x00"
-    surface.set_mime_data(cairo.MIME_TYPE_JPEG, None)
-    assert surface.get_mime_data(cairo.MIME_TYPE_JPEG) is None
-
-
 def test_surface_get_set_mime_data_references():
     surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
     if sys.version_info[0] == 2:
@@ -444,61 +283,6 @@ def test_surface_get_set_mime_data_references():
     assert sys.getrefcount(x) == 2
 
 
-def test_supports_mime_type():
-    surface = cairo.PDFSurface(None, 3, 3)
-    assert surface.supports_mime_type(cairo.MIME_TYPE_JPEG)
-    assert not surface.supports_mime_type("nope")
-
-
-def test_font_options_copy_equal():
-    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
-    font_options = surface.get_font_options()
-    font_options.set_hint_metrics(cairo.HINT_METRICS_DEFAULT)
-    new = font_options.copy()
-    assert font_options.equal(new)
-    assert new.get_hint_metrics() == cairo.HINT_METRICS_DEFAULT
-    font_options.set_hint_metrics(cairo.HINT_METRICS_ON)
-    assert not font_options.equal(new)
-    assert new.get_hint_metrics() == cairo.HINT_METRICS_DEFAULT
-
-
-def test_font_options_hash():
-    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
-    font_options = surface.get_font_options()
-    assert font_options.hash() == font_options.hash()
-    assert isinstance(font_options.hash(), long)
-
-
-def test_font_options_merge():
-    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
-    font_options = surface.get_font_options()
-    font_options.set_hint_metrics(cairo.HINT_METRICS_DEFAULT)
-    new = font_options.copy()
-    new.set_hint_metrics(cairo.HINT_METRICS_ON)
-    font_options.merge(new)
-    assert font_options.get_hint_metrics() == cairo.HINT_METRICS_ON
-
-
-def test_font_options_hashable_protocol():
-    # make sure __eq__ and __ne__ work
-    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
-    font_options = surface.get_font_options()
-    assert font_options == font_options.copy()
-    assert not font_options != font_options.copy()
-    font_options.set_hint_metrics(cairo.HINT_METRICS_DEFAULT)
-    different = font_options.copy()
-    different.set_hint_metrics(cairo.HINT_METRICS_ON)
-    assert font_options != different
-    assert not font_options == different
-    assert font_options != object()
-
-    # make sure the other operators are undefined
-    if sys.version_info[0] == 3:
-        with pytest.raises(TypeError):
-            font_options < font_options
-    assert font_options.__gt__(font_options) is NotImplemented
-
-
 def test_surface_mime_data_for_pdf():
     jpeg_bytes = zlib.decompress(base64.b64decode(
         b'eJz7f+P/AwYBLzdPNwZGRkYGDyBk+H+bwRnEowj8P8TAzcHACDJHkOH/EQYRIBsV'
@@ -514,18 +298,3 @@ def test_surface_mime_data_for_pdf():
     context.paint()
     surface.finish()
     assert jpeg_bytes in file_like.getvalue()
-
-
-def test_svg_version_to_string():
-    ver = cairo.SVGSurface.version_to_string(cairo.SVG_VERSION_1_1)
-    assert ver and isinstance(ver, str)
-    with pytest.raises(ValueError):
-        cairo.SVGSurface.version_to_string(-1)
-
-
-def test_svg_surface_restrict_to_version():
-    surface = cairo.SVGSurface(None, 10, 10)
-    surface.restrict_to_version(cairo.SVG_VERSION_1_1)
-    surface.finish()
-    with pytest.raises(cairo.Error):
-        surface.restrict_to_version(cairo.SVG_VERSION_1_2)
index 6eb6c62..a8cad59 100644 (file)
@@ -8,7 +8,7 @@ def context():
     return cairo.Context(surface)
 
 
-def text_cmp_hash(context):
+def test_cmp_hash(context):
     other = cairo.Context(context.get_target())
     assert context != other
     hash(context)
@@ -68,3 +68,441 @@ def test_show_text_glyphs():
 
     with pytest.raises(TypeError):
         context.show_text_glyphs("", glyphs, object(), flags)
+
+
+def test_append_path(context):
+    context.line_to(1, 2)
+    p = context.copy_path()
+    context.new_path()
+    context.append_path(p)
+    assert str(context.copy_path()) == str(p)
+    with pytest.raises(TypeError):
+        context.append_path(object())
+
+
+def test_arc(context):
+    assert not list(context.copy_path())
+    context.arc(0, 0, 0, 0, 0)
+    assert list(context.copy_path())
+    with pytest.raises(TypeError):
+        context.arc(object())
+
+
+def test_arc_negative(context):
+    assert not list(context.copy_path())
+    context.arc_negative(0, 0, 0, 0, 0)
+    assert list(context.copy_path())
+    with pytest.raises(TypeError):
+        context.arc_negative(object())
+
+
+def test_clip_extents(context):
+    assert context.clip_extents() == (0.0, 0.0, 42.0, 42.0)
+
+
+def test_in_clip():
+    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
+    context = cairo.Context(surface)
+    assert context.in_clip(50, 50)
+    context.clip()
+    assert not context.in_clip(50, 50)
+    context.reset_clip()
+    assert context.in_clip(50, 50)
+
+    with pytest.raises(TypeError):
+        context.in_clip(None, None)
+
+
+def test_device_to_user(context):
+    assert context.device_to_user(0, 0) == (0, 0)
+    with pytest.raises(TypeError):
+        context.device_to_user(None, None)
+
+
+def test_device_to_user_distance(context):
+    assert context.device_to_user_distance(0, 0) == (0, 0)
+    with pytest.raises(TypeError):
+        context.device_to_user_distance(None, None)
+
+
+def test_fill_extents(context):
+    context.line_to(1, 1)
+    context.line_to(1, 0)
+    context.line_to(0, 0)
+    context.line_to(0, 1)
+    context.line_to(1, 1)
+    assert context.fill_extents() == (0, 0, 1, 1)
+
+
+def test_curve_to(context):
+    with pytest.raises(TypeError):
+        context.curve_to(1, 2, 3, 4, 5, object())
+
+
+def test_set_get_dash(context):
+    assert context.get_dash() == ((), 0)
+    assert context.get_dash_count() == 0
+
+    context.set_dash([0, 1, 2, 3], 10)
+    assert context.get_dash() == ((0.0, 1.0, 2.0, 3.0), 4.0)
+    assert context.get_dash_count() == 4
+
+    with pytest.raises(TypeError):
+        context.set_dash()
+
+    with pytest.raises(TypeError):
+        context.set_dash(1, 10)
+
+    with pytest.raises(TypeError):
+        context.set_dash([object()], 1)
+
+
+def test_glyph_extents(context):
+    with pytest.raises(TypeError):
+        context.glyph_extents(None)
+    with pytest.raises(TypeError):
+        context.glyph_extents()
+
+
+def test_glyph_path(context):
+    with pytest.raises(TypeError):
+        context.glyph_path(None)
+    with pytest.raises(TypeError):
+        context.glyph_path()
+
+
+def test_in_stroke(context):
+    context.line_to(0, 0)
+    context.line_to(1, 1)
+    assert context.in_stroke(0, 0)
+    assert not context.in_stroke(0, 2)
+    with pytest.raises(TypeError):
+        context.in_stroke(object(), 0)
+
+
+def test_current_point(context):
+    assert not context.has_current_point()
+    assert context.get_current_point() == (0, 0)
+    context.move_to(10, 10)
+    assert context.has_current_point()
+    assert context.get_current_point() == (10, 10)
+
+
+def test_in_fill(context):
+    assert not context.in_fill(0.1, 0.1)
+    with pytest.raises(TypeError):
+        context.in_fill(0.1, object())
+
+
+def test_line_to(context):
+    with pytest.raises(TypeError):
+        context.line_to(0.1, object())
+
+
+def test_mask(context):
+    pattern = cairo.SolidPattern(0, 0, 0)
+    context.mask(pattern)
+    with pytest.raises(TypeError):
+        context.mask(object())
+
+
+def test_mask_surface(context):
+    context.mask_surface(context.get_target(), 0, 0)
+    with pytest.raises(TypeError):
+        context.mask_surface(object(), 0, 0)
+
+
+def test_paint_with_alpha(context):
+    context.paint_with_alpha(0.5)
+    with pytest.raises(TypeError):
+        context.paint_with_alpha(object())
+
+
+def test_path_extents(context):
+    context.line_to(1, 1)
+    context.line_to(1, 0)
+    context.line_to(0, 0)
+    context.line_to(0, 1)
+    context.line_to(1, 1)
+    assert context.path_extents() == (0.0, 0.0, 1.0, 1.0)
+
+
+def test_push_pop_group(context):
+    context.push_group()
+    context.pop_group()
+
+    context.push_group()
+    context.pop_group_to_source()
+
+    with pytest.raises(TypeError):
+        context.push_group_with_content(object())
+
+    context.push_group_with_content(cairo.Content.COLOR)
+    context.pop_group()
+
+    with pytest.raises(cairo.Error):
+        context.pop_group()
+
+
+def test_rectangle(context):
+    context.rectangle(1, 2, 4, 5)
+    assert context.path_extents() == (1.0, 2.0, 5.0, 7.0)
+    with pytest.raises(TypeError):
+        context.rectangle(1, 2, 3, object())
+
+
+def test_rotate(context):
+    context.rotate(0.3)
+    with pytest.raises(TypeError):
+        context.rotate(object())
+
+
+def test_rel_curve_to(context):
+    context.line_to(0, 0)
+    context.rel_curve_to(0, 0, 0, 0, 0, 0)
+    with pytest.raises(TypeError):
+        context.rel_curve_to(object(), 0, 0, 0, 0, 0)
+
+
+def test_move_to(context):
+    context.move_to(10, 10)
+    assert context.get_current_point() == (10, 10)
+    with pytest.raises(TypeError):
+        context.move_to(object(), 0)
+
+
+def test_rel_line_to(context):
+    context.line_to(0, 0)
+    context.rel_line_to(1, 1)
+    with pytest.raises(TypeError):
+        context.rel_line_to(object(), 0)
+
+
+def test_rel_move_to(context):
+    context.line_to(0, 0)
+    context.rel_move_to(1, 1)
+    with pytest.raises(TypeError):
+        context.rel_move_to(object(), 0)
+
+
+def test_save_restore(context):
+    context.save()
+    context.restore()
+
+
+def test_scale(context):
+    context.scale(2, 2)
+    with pytest.raises(TypeError):
+        context.scale(object(), 0)
+
+
+def test_select_font_face(context):
+    context.select_font_face("")
+    with pytest.raises(TypeError):
+        context.select_font_face(None)
+
+
+def test_set_antialias(context):
+    context.set_antialias(cairo.Antialias.SUBPIXEL)
+    assert context.get_antialias() == cairo.Antialias.SUBPIXEL
+    with pytest.raises(TypeError):
+        context.set_antialias(object())
+
+
+def test_set_fill_rule(context):
+    context.set_fill_rule(cairo.FillRule.EVEN_ODD)
+    assert context.get_fill_rule() == cairo.FillRule.EVEN_ODD
+    with pytest.raises(TypeError):
+        context.set_fill_rule(object())
+
+
+def test_set_font_face(context):
+    assert context.get_font_face()
+    context.set_font_face(None)
+    assert context.get_font_face()
+    ff = context.get_font_face()
+    context.set_font_face(ff)
+    assert context.get_font_face() == ff
+    with pytest.raises(TypeError):
+        context.set_font_face(object())
+
+
+def test_set_font_matrix(context):
+    m = cairo.Matrix()
+    context.set_font_matrix(m)
+    assert context.get_font_matrix() == m
+    with pytest.raises(TypeError):
+        context.set_font_matrix(object())
+
+
+def test_set_line_cap(context):
+    context.set_line_cap(cairo.LineCap.SQUARE)
+    assert context.get_line_cap() == cairo.LineCap.SQUARE
+    with pytest.raises(TypeError):
+        context.set_line_cap(object())
+
+
+def test_set_line_join(context):
+    context.set_line_join(cairo.LineJoin.BEVEL)
+    assert context.get_line_join() == cairo.LineJoin.BEVEL
+    with pytest.raises(TypeError):
+        context.set_line_join(object())
+
+
+def test_set_line_width(context):
+    context.set_line_width(42)
+    assert context.get_line_width() == 42
+    with pytest.raises(TypeError):
+        context.set_line_width(object())
+
+
+def test_set_matrix(context):
+    m = cairo.Matrix()
+    context.set_matrix(m)
+    assert context.get_matrix() == m
+    with pytest.raises(TypeError):
+        context.set_matrix(object())
+
+
+def test_set_miter_limit(context):
+    context.set_miter_limit(42)
+    assert context.get_miter_limit() == 42
+    with pytest.raises(TypeError):
+        context.set_miter_limit(object())
+
+
+def test_set_scaled_font(context):
+    context.set_scaled_font(context.get_scaled_font())
+    with pytest.raises(TypeError):
+        context.set_scaled_font(object())
+
+
+def test_set_font_options(context):
+    context.set_font_options(context.get_font_options())
+    with pytest.raises(TypeError):
+        context.set_font_options(object())
+
+
+def test_set_font_size(context):
+    context.set_font_size(42)
+    assert context.get_font_matrix() == cairo.Matrix(42, 0, 0, 42, 0, 0)
+    with pytest.raises(TypeError):
+        context.set_font_size(object())
+
+
+def test_set_source(context):
+    p = cairo.SolidPattern(0, 0, 0)
+    context.set_source(p)
+    assert context.get_source() == p
+    with pytest.raises(TypeError):
+        context.set_source(object())
+
+
+def test_set_source_rgb(context):
+    with pytest.raises(TypeError):
+        context.set_source_rgb(1, 1, object())
+
+
+def test_get_source_rgba(context):
+    context.set_source_rgba(1, 1, 1)
+    assert context.get_source().get_rgba() == (1, 1, 1, 1)
+    context.set_source_rgba(1, 1, 1, 0.5)
+    assert context.get_source().get_rgba() == (1, 1, 1, 0.5)
+    with pytest.raises(TypeError):
+        context.set_source_rgba(1, 1, object())
+
+
+def test_set_source_surface(context):
+    with pytest.raises(TypeError):
+        context.set_source_surface(object())
+
+
+def test_set_tolerance(context):
+    context.set_tolerance(42)
+    assert context.get_tolerance() == 42
+    with pytest.raises(TypeError):
+        context.set_tolerance(object())
+
+
+def test_show_glyphs(context):
+    with pytest.raises(TypeError):
+        context.show_glyphs()
+
+    with pytest.raises(TypeError):
+        context.show_glyphs(object())
+
+    context.show_glyphs([], 0)
+
+
+def test_show_text(context):
+    with pytest.raises(TypeError):
+        context.show_text()
+
+
+def test_stroke_extents(context):
+    assert context.stroke_extents() == (0.0, 0.0, 0.0, 0.0)
+
+
+def test_text_extents(context):
+    with pytest.raises(TypeError):
+        context.text_extents()
+
+
+def test_text_path(context):
+    context.text_path("foo")
+    with pytest.raises(TypeError):
+        context.text_path(object())
+
+
+def test_transform(context):
+    context.transform(cairo.Matrix())
+    with pytest.raises(TypeError):
+        context.transform(object())
+
+
+def test_translate(context):
+    context.translate(0.5, 0.5)
+    with pytest.raises(TypeError):
+        context.translate(0.5, object())
+
+
+def test_user_to_device(context):
+    assert context.user_to_device(0, 0) == (0, 0)
+    with pytest.raises(TypeError):
+        context.user_to_device(0, object())
+
+
+def test_user_to_device_distance(context):
+    assert context.user_to_device_distance(0, 0) == (0, 0)
+    with pytest.raises(TypeError):
+        context.user_to_device_distance(0, object())
+
+
+def test_context(context):
+    with pytest.raises(TypeError):
+        cairo.Context(None)
+
+    assert not context == object()
+
+
+def test_simple(context):
+    context.clip_preserve()
+    context.copy_page()
+    context.copy_path_flat()
+    context.fill()
+    context.fill_preserve()
+    context.font_extents()
+    context.identity_matrix()
+    context.new_sub_path()
+    context.show_page()
+    context.stroke_preserve()
+
+    assert context.get_dash_count() == 0
+    assert isinstance(context.get_font_matrix(), cairo.Matrix)
+
+    assert context.get_group_target()
+    context.get_line_width()
+
+    assert isinstance(context.get_tolerance(), float)
+    assert isinstance(context.get_miter_limit(), float)
+    assert isinstance(context.get_matrix(), cairo.Matrix)
index 950c4a1..97381f0 100644 (file)
@@ -1,6 +1,9 @@
 # -*- coding: utf-8 -*-
 
+import os
 import io
+import tempfile
+
 import cairo
 import pytest
 
@@ -13,6 +16,7 @@ def test_cmp_hash():
     assert dev == other
     assert not dev != other
     assert hash(dev) == hash(other)
+    assert dev != object()
 
 
 def test_get_device():
@@ -39,6 +43,12 @@ def test_script_device():
     with pytest.raises(TypeError):
         cairo.ScriptDevice(None)
 
+    with pytest.raises(TypeError):
+        cairo.ScriptDevice()
+
+    with pytest.raises((ValueError, TypeError)):
+        cairo.ScriptDevice("\x00")
+
 
 def test_script_device_mode():
     assert hasattr(cairo, "ScriptMode")
@@ -50,6 +60,8 @@ def test_script_device_mode():
     assert mode == cairo.ScriptMode.ASCII
     dev.set_mode(cairo.ScriptMode.BINARY)
     assert dev.get_mode() == cairo.ScriptMode.BINARY
+    with pytest.raises(TypeError):
+        dev.set_mode(object())
 
 
 def test_script_device_write_comment():
@@ -60,6 +72,8 @@ def test_script_device_write_comment():
     dev.flush()
     assert b"pycairo foo" in f.getvalue()
     assert b"pycairo bar" in f.getvalue()
+    with pytest.raises(TypeError):
+        dev.write_comment(object())
 
 
 def test_from_recording_surface():
@@ -85,3 +99,19 @@ def test_from_recording_surface():
     # No None allowed
     with pytest.raises(TypeError):
         dev.from_recording_surface(None)
+
+
+def test_device_acquire():
+    f = io.BytesIO()
+    dev = cairo.ScriptDevice(f)
+    dev.acquire()
+    dev.release()
+
+
+def test_script_device_to_path():
+    fd, fname = tempfile.mkstemp()
+    os.close(fd)
+    try:
+        cairo.ScriptDevice(fname).finish()
+    finally:
+        os.unlink(fname)
index 62aaec9..7db0728 100644 (file)
@@ -16,6 +16,9 @@ def test_type():
         t()
 
     with pytest.raises(TypeError):
+        t(object())
+
+    with pytest.raises(TypeError):
         type("foo", (t,), {})
 
     assert hasattr(t, "DEFAULT")
index 63b65a1..d7e12e7 100644 (file)
@@ -33,6 +33,15 @@ def test_error_check_status():
     assert e.value.status == cairo.Status.WRITE_ERROR
     assert type(e.value).__name__ == "cairo.IOError"
 
+    err = e.value
+    err.status = cairo.Status.DEVICE_FINISHED
+    assert err.status == cairo.Status.DEVICE_FINISHED
+
+    with pytest.raises(TypeError):
+        del err.status
+
+    str(cairo.Error())
+
 
 def test_error_context():
     surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
index f5196bd..1ff1591 100644 (file)
@@ -1,7 +1,129 @@
+import sys
+
 import cairo
 import pytest
 
 
+try:
+    long
+except NameError:
+    long = int
+
+
+@pytest.fixture
+def font_options():
+    surface = cairo.ImageSurface(0, 10, 10)
+    return surface.get_font_options()
+
+
+@pytest.fixture
+def font_face():
+    surface = cairo.ImageSurface(0, 10, 10)
+    context = cairo.Context(surface)
+    return context.get_font_face()
+
+
+@pytest.fixture
+def scaled_font(font_face, font_options):
+    return cairo.ScaledFont(
+        font_face, cairo.Matrix(), cairo.Matrix(), font_options)
+
+
+def test_font_options():
+    assert isinstance(cairo.FontOptions(), cairo.FontOptions)
+    with pytest.raises(TypeError):
+        cairo.FontOptions(object())
+
+
+def test_font_options_copy_equal():
+    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
+    font_options = surface.get_font_options()
+    font_options.set_hint_metrics(cairo.HINT_METRICS_DEFAULT)
+    new = font_options.copy()
+    assert font_options.equal(new)
+    assert new.get_hint_metrics() == cairo.HINT_METRICS_DEFAULT
+    font_options.set_hint_metrics(cairo.HINT_METRICS_ON)
+    assert not font_options.equal(new)
+    assert new.get_hint_metrics() == cairo.HINT_METRICS_DEFAULT
+    with pytest.raises(TypeError):
+        font_options.equal(object())
+
+
+def test_font_options_hash():
+    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
+    font_options = surface.get_font_options()
+    assert font_options.hash() == font_options.hash()
+    assert isinstance(font_options.hash(), long)
+
+
+def test_font_options_merge():
+    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
+    font_options = surface.get_font_options()
+    font_options.set_hint_metrics(cairo.HINT_METRICS_DEFAULT)
+    new = font_options.copy()
+    new.set_hint_metrics(cairo.HINT_METRICS_ON)
+    font_options.merge(new)
+    assert font_options.get_hint_metrics() == cairo.HINT_METRICS_ON
+    with pytest.raises(TypeError):
+        font_options.merge(object())
+
+
+def test_font_options_hashable_protocol():
+    # make sure __eq__ and __ne__ work
+    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
+    font_options = surface.get_font_options()
+    assert font_options == font_options.copy()
+    assert not font_options != font_options.copy()
+    font_options.set_hint_metrics(cairo.HINT_METRICS_DEFAULT)
+    different = font_options.copy()
+    different.set_hint_metrics(cairo.HINT_METRICS_ON)
+    assert font_options != different
+    assert not font_options == different
+    assert font_options != object()
+
+    # make sure the other operators are undefined
+    if sys.version_info[0] == 3:
+        with pytest.raises(TypeError):
+            font_options < font_options
+    assert font_options.__gt__(font_options) is NotImplemented
+
+
+def test_font_options_set_antialias(font_options):
+    font_options.set_antialias(cairo.Antialias.GRAY)
+    assert font_options.get_antialias() == cairo.Antialias.GRAY
+    with pytest.raises(TypeError):
+        font_options.set_antialias(object())
+
+
+def test_font_options_set_hint_metrics(font_options):
+    font_options.set_hint_metrics(cairo.HintMetrics.OFF)
+    assert font_options.get_hint_metrics() == cairo.HintMetrics.OFF
+    with pytest.raises(TypeError):
+        font_options.set_hint_metrics(object())
+
+
+def test_font_options_set_hint_style(font_options):
+    font_options.set_hint_style(cairo.HintStyle.SLIGHT)
+    assert font_options.get_hint_style() == cairo.HintStyle.SLIGHT
+    with pytest.raises(TypeError):
+        font_options.set_hint_style(object())
+
+
+def test_font_options_set_subpixel_order(font_options):
+    font_options.set_subpixel_order(cairo.SubpixelOrder.VRGB)
+    assert font_options.get_subpixel_order() == cairo.SubpixelOrder.VRGB
+    with pytest.raises(TypeError):
+        font_options.set_subpixel_order(object())
+
+
+def test_font_face(font_face):
+    with pytest.raises(TypeError):
+        cairo.FontFace()
+
+    assert font_face == font_face
+    assert font_face != object()
+
+
 def test_font_face_cmp_hash():
     surface = cairo.ImageSurface(0, 10, 10)
     context = cairo.Context(surface)
@@ -24,6 +146,50 @@ def test_font_face_cmp_hash():
         hash(fo)
 
 
+def test_scaled_font(scaled_font):
+    with pytest.raises(TypeError):
+        cairo.ScaledFont()
+
+    assert scaled_font == scaled_font
+    assert scaled_font != object()
+
+
+def test_scaled_font_extents(scaled_font):
+    assert isinstance(scaled_font.extents(), tuple)
+
+
+def test_scaled_font_get_font_face(scaled_font):
+    assert isinstance(scaled_font.get_font_face(), cairo.FontFace)
+
+
+def test_scaled_font_get_scale_matrix(scaled_font):
+    assert isinstance(scaled_font.get_scale_matrix(), cairo.Matrix)
+
+
+def test_scaled_font_text_extents(scaled_font):
+    with pytest.raises(TypeError):
+        scaled_font.text_extents(object())
+
+
+def test_scaled_font_glyph_extents(scaled_font):
+    with pytest.raises(TypeError):
+        scaled_font.glyph_extents(object())
+    with pytest.raises(TypeError):
+        scaled_font.glyph_extents([object()])
+    with pytest.raises(TypeError):
+        scaled_font.glyph_extents()
+
+
+def test_toy_font_face():
+    with pytest.raises(TypeError):
+        cairo.ToyFontFace(object())
+
+
+def test_toy_font_get_family():
+    font_face = cairo.ToyFontFace("")
+    assert isinstance(font_face.get_family(), str)
+
+
 def test_toy_font_get_slant():
     font_face = cairo.ToyFontFace("")
     assert font_face.get_slant() == cairo.FontSlant.NORMAL
@@ -36,12 +202,6 @@ def test_toy_font_get_weight():
     assert isinstance(font_face.get_weight(), cairo.FontWeight)
 
 
-@pytest.fixture
-def font_options():
-    surface = cairo.ImageSurface(0, 10, 10)
-    return surface.get_font_options()
-
-
 def test_font_options_get_antialias(font_options):
     assert font_options.get_antialias() == cairo.Antialias.DEFAULT
     assert isinstance(font_options.get_antialias(), cairo.Antialias)
@@ -103,3 +263,6 @@ def test_scaled_font_text_to_glyphs():
     assert len(glyphs) == 3
     assert glyphs[0] != glyphs[1]
     assert len(clusters) == 3
+
+    with pytest.raises(TypeError):
+        sf.text_to_glyphs(object())
index c49998b..8424473 100644 (file)
@@ -40,3 +40,6 @@ def test_context():
     assert context.glyph_extents([g])
     context.glyph_path([g])
     context.show_glyphs([(0, 0, 0)])
+
+    with pytest.raises(TypeError):
+        context.glyph_path([object()])
index a8f30b9..6d5d698 100644 (file)
@@ -1,12 +1,78 @@
 # -*- coding: utf-8 -*-
 
 import math
+import os
+import sys
 
 import pytest
 import cairo
+import tempfile
 
 pytest.importorskip("hypothesis")
-from hypothesis import given, strategies, assume
+from hypothesis import given, strategies, assume, settings
+from hypothesis.strategies import floats, integers
+
+from .hypothesis_fspaths import fspaths
+
+
+@pytest.fixture(scope='module')
+def tempdir_path():
+    dir_ = tempfile.mkdtemp()
+    try:
+        yield dir_
+    finally:
+        os.rmdir(dir_)
+
+
+def _to_temp_path(tempdir_path, p):
+    basename = os.path.basename(p)
+    if sys.version_info[0] == 3 and isinstance(basename, bytes):
+        tempdir_path = os.fsencode(tempdir_path)
+    res = os.path.join(tempdir_path, basename)
+    if not isinstance(p, (type(u""), type(b""))):
+        res = type(p)(res)
+    return res
+
+
+@given(path=fspaths())
+@settings(max_examples=5000)
+def test_fspaths(tempdir_path, path):
+    p = _to_temp_path(tempdir_path, path)
+
+    # filter out "."
+    assert not os.listdir(tempdir_path)
+    if os.path.exists(p):
+        return
+
+    # cairo uses fopen, which only supports ANSI paths under Windows.
+    # Make sure we fail if not ANSI and succeed otherwise
+    is_valid = True
+    if os.name == "nt":
+        temp = os.path.join(p)
+        if isinstance(temp, type(b"")):
+            if sys.version_info[0] == 3:
+                temp = os.fsdecode(temp)
+            else:
+                temp = temp.decode(sys.getfilesystemencoding(), "strict")
+        if isinstance(temp, type(u"")):
+            try:
+                if temp.encode("mbcs").decode("mbcs") != temp:
+                    is_valid = False
+            except UnicodeEncodeError:
+                is_valid = False
+
+    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
+    try:
+        surface.write_to_png(p)
+    except (TypeError, ValueError):
+        assert not is_valid
+        assert not os.path.exists(p)
+    except cairo.Error:
+        assert not os.path.exists(p)
+    else:
+        assert is_valid
+        assert os.path.exists(p), os.listdir(tempdir_path)
+        os.unlink(p)
 
 
 @given(strategies.floats(), strategies.floats())
@@ -40,3 +106,90 @@ def test_surface_create_for_rectangle(x, y, w, h):
         surface.create_for_rectangle(x, y, w, h)
     except cairo.Error as e:
         assert e.status == cairo.Status.INVALID_SIZE
+
+
+@given(integers(), floats(allow_nan=False), floats(allow_nan=False))
+def test_glyph(index, x, y):
+    try:
+        g = cairo.Glyph(index, x, y)
+    except OverflowError:
+        pass
+    else:
+        assert g.index == index
+        assert g.x == x
+        assert g.y == y
+
+
+@given(floats(allow_nan=False), floats(allow_nan=False),
+       floats(allow_nan=False), floats(allow_nan=False))
+def test_rectangle(x, y, width, height):
+    r = cairo.Rectangle(x, y, width, height)
+    assert r.x == x
+    assert r.y == y
+    assert r.width == width
+    assert r.height == height
+
+
+@given(integers(), integers())
+def test_text_cluster(num_bytes, num_glyphs):
+    try:
+        tc = cairo.TextCluster(num_bytes, num_glyphs)
+    except OverflowError:
+        pass
+    else:
+        assert tc.num_bytes == num_bytes
+        assert tc.num_glyphs == num_glyphs
+
+
+@given(floats(allow_nan=False), floats(allow_nan=False),
+       floats(allow_nan=False), floats(allow_nan=False),
+       floats(allow_nan=False), floats(allow_nan=False))
+def test_text_extents(x_bearing, y_bearing, width, height, x_advance,
+                      y_advance):
+    te = cairo.TextExtents(x_bearing, y_bearing, width, height, x_advance,
+                           y_advance)
+    assert te.x_bearing == x_bearing
+    assert te.y_bearing == y_bearing
+    assert te.width == width
+    assert te.height == height
+    assert te.x_advance == x_advance
+    assert te.y_advance == y_advance
+
+
+@given(integers(), integers(), integers(), integers())
+def test_rect_int(x, y, width, height):
+    try:
+        r = cairo.RectangleInt(x, y, width, height)
+    except OverflowError:
+        pass
+    else:
+        assert r.x == x
+        assert r.y == y
+        assert r.width == width
+        assert r.height == height
+
+
+@given(integers())
+def test_enums(value):
+    try:
+        e = cairo.Antialias(value)
+    except OverflowError:
+        pass
+    else:
+        assert e == value
+
+
+@given(integers())
+def test_context_get_set_operator(value):
+    try:
+        op = cairo.Operator(value)
+    except OverflowError:
+        return
+
+    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
+    context = cairo.Context(surface)
+    try:
+        context.set_operator(op)
+    except OverflowError:
+        return
+    assert context.get_operator() == op
diff --git a/tests/test_matrix.py b/tests/test_matrix.py
new file mode 100644 (file)
index 0000000..4222929
--- /dev/null
@@ -0,0 +1,104 @@
+import cairo
+import pytest
+
+
+def test_matrix():
+    m = cairo.Matrix()
+    m.rotate(10)
+    m.scale(1.5, 2.5)
+    m.translate(10, 20)
+
+    with pytest.raises(TypeError):
+        m * 42
+
+    with pytest.raises(TypeError):
+        m + 42
+
+    with pytest.raises(TypeError):
+        cairo.Matrix(object())
+
+    assert m != 42
+    assert m == m
+    assert m != cairo.Matrix()
+
+    assert repr(cairo.Matrix()) == "cairo.Matrix(1, 0, 0, 1, 0, 0)"
+
+
+def test_init_rotate():
+    r = cairo.Matrix.init_rotate(0)
+    assert cairo.Matrix() == r
+
+    with pytest.raises(TypeError):
+        cairo.Matrix.init_rotate(object())
+
+
+def test_invert():
+    m = cairo.Matrix(1, 1)
+    m.invert()
+    assert m == cairo.Matrix(1, -1, -0, 1, 0, 0)
+
+
+def test_matrix_properties():
+    m = cairo.Matrix(*range(6))
+    assert [m.xx, m.yx, m.xy, m.yy, m.x0, m.y0] == list(range(6))
+    m.xx = 42
+    assert m.xx == 42
+    m.scale(2, 2)
+    assert m.xx == 84
+
+
+def test_get_item():
+    m = cairo.Matrix(1, 2, 3, 4, 5, 6)
+    for i in range(6):
+        assert m[i] == i + 1
+    with pytest.raises(IndexError):
+        m[6]
+    with pytest.raises(IndexError):
+        m[-1]
+
+
+def test_multiply():
+    with pytest.raises(TypeError):
+        cairo.Matrix().multiply(object())
+
+    m = cairo.Matrix(1, 1, 0, 1)
+    assert m.multiply(m) == cairo.Matrix(1, 2, 0, 1, 0, 0)
+    assert m * m == m.multiply(m)
+
+
+def test_translate():
+    m = cairo.Matrix()
+    m.translate(1, 1)
+    assert m == cairo.Matrix(1, 0, 0, 1, 1, 1)
+    with pytest.raises(TypeError):
+        m.translate(1, object())
+
+
+def test_rotate():
+    m = cairo.Matrix()
+    with pytest.raises(TypeError):
+        m.rotate(object())
+
+
+def test_scale():
+    m = cairo.Matrix()
+    with pytest.raises(TypeError):
+        m.scale(object())
+    m.scale(2, 2)
+    assert m != cairo.Matrix()
+    m.scale(0.5, 0.5)
+    assert m == cairo.Matrix()
+
+
+def test_transform_distance():
+    m = cairo.Matrix()
+    assert m.transform_distance(1, 1) == (1, 1)
+    with pytest.raises(TypeError):
+        m.transform_distance(1, object())
+
+
+def test_transform_point():
+    m = cairo.Matrix()
+    assert m.transform_point(1, 1) == (1, 1)
+    with pytest.raises(TypeError):
+        m.transform_point(1, object())
diff --git a/tests/test_path.py b/tests/test_path.py
new file mode 100644 (file)
index 0000000..5ef84b0
--- /dev/null
@@ -0,0 +1,73 @@
+import cairo
+import pytest
+
+
+@pytest.fixture
+def context():
+    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 42, 42)
+    return cairo.Context(surface)
+
+
+def test_path():
+    assert cairo.Path
+
+    with pytest.raises(TypeError):
+        cairo.Path()
+
+
+def test_path_str(context):
+    p = context.copy_path()
+    assert isinstance(p, cairo.Path)
+    assert str(p) == ""
+
+    context.line_to(1, 2)
+    p = context.copy_path()
+    assert str(p) == "move_to 1.000000 2.000000"
+
+    context.line_to(1, 2)
+    p = context.copy_path()
+    assert str(p) == "move_to 1.000000 2.000000\nline_to 1.000000 2.000000"
+
+    context.new_path()
+    context.curve_to(0, 1, 2, 3, 4, 5)
+    p = context.copy_path()
+    assert str(p) == (
+        "move_to 0.000000 1.000000\n"
+        "curve_to 0.000000 1.000000 2.000000 3.000000 4.000000 5.000000")
+
+    context.new_path()
+    context.line_to(1, 2)
+    context.close_path()
+    p = context.copy_path()
+    assert str(p) == (
+        "move_to 1.000000 2.000000\n"
+        "close path\n"
+        "move_to 1.000000 2.000000")
+
+
+def test_path_compare_hash(context):
+    p = context.copy_path()
+    assert p == p
+    hash(p)
+    assert not p != p
+    assert p != object()
+    assert not p < p
+    assert p <= p
+    assert p >= p
+    assert not p > p
+
+
+def test_path_iter(context):
+    context.line_to(1, 2)
+    context.line_to(2, 3)
+    context.curve_to(0, 1, 2, 3, 4, 5)
+    context.close_path()
+    p = context.copy_path()
+    i = iter(p)
+    assert list(i) == [
+        (0, (1.0, 2.0)),
+        (1, (2.0, 3.0)),
+        (2, (0.0, 1.0, 2.0, 3.0, 4.0, 5.0)),
+        (3, ()),
+        (0, (1.0, 2.0)),
+    ]
index 7f93688..0f2b967 100644 (file)
@@ -7,6 +7,9 @@ def test_raster_source():
     assert isinstance(pattern, cairo.RasterSourcePattern)
     assert issubclass(cairo.RasterSourcePattern, cairo.Pattern)
 
+    with pytest.raises(TypeError):
+        cairo.RasterSourcePattern(object())
+
     was_called = []
 
     def acquire_callback(target, extents):
@@ -23,6 +26,9 @@ def test_raster_source():
         was_called.append("release")
         return None
 
+    with pytest.raises(TypeError):
+        pattern.set_acquire()
+
     pattern.set_acquire(None, release_callback)
     assert pattern.get_acquire() == (None, release_callback)
 
@@ -68,6 +74,16 @@ def test_get_filter():
     assert pattern.get_filter() == cairo.Filter.GOOD
 
 
+def test_linear_gradient():
+    with pytest.raises(TypeError):
+        cairo.LinearGradient()
+
+
+def test_radial_gradient():
+    with pytest.raises(TypeError):
+        cairo.RadialGradient()
+
+
 def test_gradient_get_color_stops():
     pattern = cairo.LinearGradient(1, 2, 4, 5)
     assert pattern.get_color_stops_rgba() == []
@@ -78,10 +94,29 @@ def test_gradient_get_color_stops():
         [(0.125, 0.25, 0.5, 0.75, 1.0), (1.0, 0.75, 0.5, 0.25, 0.125)]
 
 
+def test_gradient_add_color_stop_rgb():
+    pattern = cairo.LinearGradient(1, 2, 4, 5)
+    with pytest.raises(TypeError):
+        pattern.add_color_stop_rgb()
+
+
+def test_gradient_add_color_stop_rgba():
+    pattern = cairo.LinearGradient(1, 2, 4, 5)
+    with pytest.raises(TypeError):
+        pattern.add_color_stop_rgba()
+
+
+def test_solid_pattern():
+    with pytest.raises(TypeError):
+        cairo.SolidPattern()
+
+
 def test_mesh_pattern():
     mesh = cairo.MeshPattern()
     assert isinstance(mesh, cairo.MeshPattern)
     assert issubclass(cairo.MeshPattern, cairo.Pattern)
+    with pytest.raises(TypeError):
+        cairo.MeshPattern(object())
 
 
 def test_mesh_pattern_example1():
@@ -114,6 +149,60 @@ def test_mesh_pattern_example1():
         pattern.get_path(9)
 
 
+def test_mesh_pattern_curve_to():
+    pattern = cairo.MeshPattern()
+    with pytest.raises(TypeError):
+        pattern.curve_to(object())
+
+
+def test_mesh_pattern_get_control_point():
+    pattern = cairo.MeshPattern()
+    with pytest.raises(TypeError):
+        pattern.get_control_point(object())
+
+
+def test_mesh_pattern_get_corner_color_rgba():
+    pattern = cairo.MeshPattern()
+    with pytest.raises(TypeError):
+        pattern.get_corner_color_rgba(object())
+
+
+def test_mesh_pattern_get_path():
+    pattern = cairo.MeshPattern()
+    with pytest.raises(TypeError):
+        pattern.get_path(object())
+
+
+def test_mesh_pattern_line_to():
+    pattern = cairo.MeshPattern()
+    with pytest.raises(TypeError):
+        pattern.line_to(object())
+
+
+def test_mesh_pattern_move_to():
+    pattern = cairo.MeshPattern()
+    with pytest.raises(TypeError):
+        pattern.move_to(object())
+
+
+def test_mesh_pattern_set_control_point():
+    pattern = cairo.MeshPattern()
+    with pytest.raises(TypeError):
+        pattern.set_control_point(object())
+
+
+def test_mesh_pattern_set_corner_color_rgb():
+    pattern = cairo.MeshPattern()
+    with pytest.raises(TypeError):
+        pattern.set_corner_color_rgb(object())
+
+
+def test_mesh_pattern_set_corner_color_rgba():
+    pattern = cairo.MeshPattern()
+    with pytest.raises(TypeError):
+        pattern.set_corner_color_rgba(object())
+
+
 def test_mesh_pattern_example2():
     pattern = cairo.MeshPattern()
     pattern.begin_patch()
@@ -169,3 +258,64 @@ def test_mesh_pattern_error_states():
 
     with pytest.raises(cairo.Error):
         cairo.MeshPattern().set_corner_color_rgb(0, 0.125, 0.25, 0.5)
+
+
+def test_get_matrix():
+    pattern = cairo.SolidPattern(1, 2, 4)
+    assert isinstance(pattern.get_matrix(), cairo.Matrix)
+    pattern.set_matrix(cairo.Matrix())
+    with pytest.raises(TypeError):
+        pattern.set_matrix(object())
+
+
+def test_set_extend():
+    pattern = cairo.SolidPattern(1, 2, 4)
+    pattern.set_extend(42)
+    assert pattern.get_extend() == 42
+    with pytest.raises(TypeError):
+        pattern.set_extend(object())
+
+
+def test_set_filter():
+    pattern = cairo.SolidPattern(1, 2, 4)
+    with pytest.raises(TypeError):
+        pattern.set_filter(object())
+
+
+def test_pattern():
+    with pytest.raises(TypeError):
+        cairo.Pattern()
+
+    r, g, b, a = 0.1, 0.2, 0.3, 0.4
+    p = cairo.SolidPattern(r, g, b, a)
+    assert p.get_rgba() == (r, g, b, a)
+
+    assert not p == object()
+    hash(p)
+
+    with pytest.raises(TypeError):
+        cairo.Gradient()
+
+    x0, y0, x1, y1 = 0.0, 0.0, 0.0, 1.0
+    p = cairo.LinearGradient(x0, y0, x1, y1)
+    assert p.get_linear_points() == (x0, y0, x1, y1)
+    p.add_color_stop_rgba(1, 0, 0, 0, 1)
+    p.add_color_stop_rgba(0, 1, 1, 1, 1)
+
+    cx0, cy0, radius0, cx1, cy1, radius1 = 1.0, 1.0, 1.0, 2.0, 2.0, 1.0
+    p = cairo.RadialGradient(cx0, cy0, radius0, cx1, cy1, radius1)
+    assert p.get_radial_circles() == (cx0, cy0, radius0, cx1, cy1, radius1)
+    p.add_color_stop_rgba(0, 1, 1, 1, 1)
+    p.add_color_stop_rgba(1, 0, 0, 0, 1)
+
+
+def test_pattern_filter():
+    pattern = cairo.SolidPattern(1, 2, 3)
+    assert pattern.get_filter() == cairo.FILTER_GOOD
+    pattern.set_filter(cairo.FILTER_NEAREST)
+    assert pattern.get_filter() == cairo.FILTER_NEAREST
+
+
+def test_surface_pattern():
+    with pytest.raises(TypeError):
+        cairo.SurfacePattern(object())
index 87301e7..bdc96c5 100644 (file)
@@ -2,11 +2,108 @@ import cairo
 import pytest
 
 
+def test_region():
+    with pytest.raises(TypeError):
+        cairo.Region(object())
+
+    with pytest.raises(TypeError):
+        cairo.Region(object(), object())
+
+    with pytest.raises(TypeError):
+        cairo.Region([object()])
+
+
+def test_get_rectangle():
+    rect = cairo.RectangleInt(0, 0, 10, 10)
+    r = cairo.Region(rect)
+    with pytest.raises(ValueError):
+        r.get_rectangle(-1)
+    with pytest.raises(ValueError):
+        r.get_rectangle(1)
+    assert r.get_rectangle(0) == rect
+    with pytest.raises(TypeError):
+        r.get_rectangle(object())
+
+
+def test_contains_point():
+    rect = cairo.RectangleInt(0, 0, 10, 10)
+    r = cairo.Region(rect)
+    assert r.contains_point(0, 0)
+    assert not r.contains_point(0, 20)
+    with pytest.raises(TypeError):
+        r.contains_point(0, object())
+
+
+def test_intersect():
+    rect = cairo.RectangleInt(0, 0, 10, 10)
+    r = cairo.Region(rect)
+    r.intersect(r)
+    r.intersect(rect)
+    with pytest.raises(TypeError):
+        r.intersect(object())
+    with pytest.raises(TypeError):
+        r.intersect()
+
+    assert r.__eq__(object()) == NotImplemented
+    assert rect.__eq__(object()) == NotImplemented
+
+
+def test_equal():
+    rect = cairo.RectangleInt(0, 0, 10, 10)
+    r = cairo.Region(rect)
+    assert r.equal(r)
+    with pytest.raises(TypeError):
+        r.equal(object())
+    with pytest.raises(TypeError):
+        r.equal()
+
+
+def test_subtract():
+    rect = cairo.RectangleInt(0, 0, 10, 10)
+    r = cairo.Region(rect)
+    r.subtract(r)
+    with pytest.raises(TypeError):
+        r.subtract(object())
+    with pytest.raises(TypeError):
+        r.subtract()
+
+
+def test_union():
+    rect = cairo.RectangleInt(0, 0, 10, 10)
+    r = cairo.Region(rect)
+    r.union(r)
+    r.union(rect)
+    with pytest.raises(TypeError):
+        r.union(object())
+    with pytest.raises(TypeError):
+        r.union()
+
+
+def test_xor():
+    rect = cairo.RectangleInt(0, 0, 10, 10)
+    r = cairo.Region(rect)
+    r.xor(r)
+    r.xor(rect)
+    with pytest.raises(TypeError):
+        r.xor(object())
+    with pytest.raises(TypeError):
+        r.xor()
+
+
+def test_translate():
+    r = cairo.Region()
+    r.translate(1, 1)
+    with pytest.raises(TypeError):
+        r.translate(1, object())
+
+
 def test_region_contains_rectangle():
     rect = cairo.RectangleInt(1, 2, 10, 13)
     region = cairo.Region()
     assert region.contains_rectangle(rect) == cairo.RegionOverlap.OUT
     assert isinstance(region.contains_rectangle(rect), cairo.RegionOverlap)
+    with pytest.raises(TypeError):
+        region.contains_rectangle(object())
 
 
 def test_region_cmp_hash():
@@ -20,6 +117,12 @@ def test_region_cmp_hash():
     assert not region != other
     assert region != differ
 
+    with pytest.raises(TypeError):
+        region < region
+
+    with pytest.raises(TypeError):
+        region > region
+
     rect = cairo.RectangleInt(1, 2, 10, 13)
     same = cairo.RectangleInt(1, 2, 10, 13)
     other = cairo.RectangleInt(2, 2, 10, 13)
@@ -28,3 +131,9 @@ def test_region_cmp_hash():
 
     assert rect == same
     assert rect != other
+
+    with pytest.raises(TypeError):
+        rect < same
+
+    with pytest.raises(TypeError):
+        rect > same
index 84837f5..37038af 100644 (file)
@@ -16,6 +16,7 @@ def test_surface_cmp_hash():
     ctx = cairo.Context(main)
     assert ctx.get_target() == main
     assert not ctx.get_target() != main
+    assert main != object()
     assert hash(ctx.get_target()) == hash(main)
 
     # we implement some hackery to change the underlying object after unmap
@@ -31,6 +32,9 @@ def test_surface_map_to_image():
     main = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
     image = main.map_to_image(None)
 
+    with pytest.raises(TypeError):
+        type(image)()
+
     other = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
     with pytest.raises(ValueError):
         other.unmap_image(image)
@@ -50,6 +54,15 @@ def test_surface_map_to_image():
     with pytest.raises(cairo.Error):
         cairo.Context(image)
 
+    with pytest.raises(TypeError):
+        main.map_to_image(object())
+
+    with pytest.raises(TypeError):
+        main.map_to_image()
+
+    gced = main.map_to_image(None)
+    del gced
+
 
 def test_surface_map_to_image_data():
     main = cairo.ImageSurface(cairo.Format.RGB24, 2, 1)
@@ -78,7 +91,14 @@ def test_tee_surface():
     main = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
     tee = cairo.TeeSurface(main)
     assert isinstance(tee, cairo.TeeSurface)
-
+    with pytest.raises(TypeError):
+        cairo.TeeSurface(object())
+    with pytest.raises(TypeError):
+        tee.add(object())
+    with pytest.raises(TypeError):
+        tee.remove(object())
+    with pytest.raises(TypeError):
+        tee.index(object())
     # the API is horrible, passing a wrong arg sets the surface to an error
     # state instead of returning the status.
     s1 = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
@@ -123,12 +143,162 @@ def test_pdf_get_versions():
     assert all(isinstance(v, cairo.PDFVersion) for v in versions)
 
 
+def test_pdf_set_size():
+    fileobj = io.BytesIO()
+    surface = cairo.PDFSurface(fileobj, 128, 128)
+    surface.set_size(10, 10)
+    with pytest.raises(TypeError):
+        surface.set_size(10, object())
+
+
+def test_pdf_surface():
+    fd, fname = tempfile.mkstemp()
+    os.close(fd)
+    try:
+        cairo.PDFSurface(fname, 10, 10).finish()
+    finally:
+        os.unlink(fname)
+
+    with pytest.raises(TypeError):
+        cairo.PDFSurface()
+
+    with pytest.raises((ValueError, TypeError)):
+        cairo.PDFSurface("\x00")
+
+    with pytest.raises(TypeError):
+        cairo.PDFSurface(object(), 100, 100)
+
+
+def test_svg_version_to_string():
+    ver = cairo.SVGSurface.version_to_string(cairo.SVG_VERSION_1_1)
+    assert ver and isinstance(ver, str)
+    with pytest.raises(ValueError):
+        cairo.SVGSurface.version_to_string(-1)
+    with pytest.raises(TypeError):
+        cairo.SVGSurface.version_to_string(object())
+
+
+def test_svg_surface_restrict_to_version():
+    surface = cairo.SVGSurface(None, 10, 10)
+    surface.restrict_to_version(cairo.SVG_VERSION_1_1)
+    surface.finish()
+    with pytest.raises(cairo.Error):
+        surface.restrict_to_version(cairo.SVG_VERSION_1_2)
+    with pytest.raises(TypeError):
+        surface.restrict_to_version(object())
+
+
+def test_pdf_surface_restrict_to_version():
+    surface = cairo.PDFSurface(None, 10, 10)
+    surface.restrict_to_version(cairo.PDF_VERSION_1_4)
+    surface.finish()
+    with pytest.raises(cairo.Error):
+        surface.restrict_to_version(cairo.PDF_VERSION_1_5)
+    with pytest.raises(TypeError):
+        surface.restrict_to_version(object())
+
+
+def test_pdf_version_to_string():
+    ver = cairo.PDFSurface.version_to_string(cairo.PDF_VERSION_1_4)
+    assert ver and isinstance(ver, str)
+    with pytest.raises(ValueError):
+        cairo.PDFSurface.version_to_string(-1)
+    with pytest.raises(TypeError):
+        cairo.PDFSurface.version_to_string(object())
+
+
+def test_ps_surface_misc():
+    surface = cairo.PSSurface(None, 10, 10)
+    surface.dsc_begin_page_setup()
+    surface.dsc_begin_setup()
+
+
+def test_ps_surface_dsc_comment():
+    surface = cairo.PSSurface(None, 10, 10)
+    surface.dsc_comment("%%Title: My excellent document")
+    with pytest.raises(cairo.Error):
+        surface.dsc_comment("")
+    with pytest.raises(TypeError):
+        surface.dsc_comment(object())
+
+
+def test_ps_get_eps():
+    surface = cairo.PSSurface(None, 10, 10)
+    assert isinstance(surface.get_eps(), bool)
+    surface.set_eps(True)
+    assert surface.get_eps()
+    with pytest.raises(TypeError):
+        surface.set_eps(object())
+
+
+def test_ps_set_size():
+    surface = cairo.PSSurface(None, 10, 10)
+    surface.set_size(10, 10)
+    with pytest.raises(TypeError):
+        surface.set_size(10, object())
+
+
+def test_ps_restrict_to_level():
+    surface = cairo.PSSurface(None, 10, 10)
+    surface.restrict_to_level(cairo.PSLevel.LEVEL_2)
+    with pytest.raises(TypeError):
+        surface.restrict_to_level(object())
+
+
+def test_ps_surface_level_to_string():
+    level_id = cairo.PSSurface.level_to_string(cairo.PS_LEVEL_2)
+    assert isinstance(level_id, str)
+    assert cairo.PSSurface.ps_level_to_string(cairo.PS_LEVEL_2) == level_id
+    with pytest.raises(ValueError):
+        cairo.PSSurface.level_to_string(-1)
+    with pytest.raises(TypeError):
+        cairo.PSSurface.level_to_string(object())
+
+
 def test_ps_surface_get_levels():
     levels = cairo.PSSurface.get_levels()
     assert isinstance(levels, list)
     assert all(isinstance(v, cairo.PSLevel) for v in levels)
 
 
+def test_ps_surface():
+    assert isinstance(cairo.PSSurface(None, 10, 10), cairo.PSSurface)
+
+    fd, fname = tempfile.mkstemp()
+    os.close(fd)
+    try:
+        cairo.PSSurface(fname, 10, 10).finish()
+    finally:
+        os.unlink(fname)
+
+    with pytest.raises(TypeError):
+        cairo.PSSurface()
+
+    with pytest.raises((ValueError, TypeError)):
+        cairo.PSSurface("\x00", 100, 100)
+
+    with pytest.raises(TypeError):
+        cairo.PSSurface(object(), 100, 100)
+
+
+def test_scg_surface():
+    fd, fname = tempfile.mkstemp()
+    os.close(fd)
+    try:
+        cairo.SVGSurface(fname, 10, 10).finish()
+    finally:
+        os.unlink(fname)
+
+    with pytest.raises(TypeError):
+        cairo.SVGSurface()
+
+    with pytest.raises((ValueError, TypeError)):
+        cairo.SVGSurface("\x00", 10, 10)
+
+    with pytest.raises(TypeError):
+        cairo.SVGSurface(object(), 100, 100)
+
+
 def test_svg_surface_get_versions():
     versions = cairo.SVGSurface.get_versions()
     assert isinstance(versions, list)
@@ -150,8 +320,65 @@ def test_surface_set_device_scale():
     with pytest.raises(cairo.Error):
         surface.set_device_scale(1, 0)
 
+    with pytest.raises(TypeError):
+        surface.set_device_scale(1, object())
+
+
+def test_surface_create_for_rectangle():
+    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
+    new = surface.create_for_rectangle(0, 0, 10, 10)
+    assert new
+    assert isinstance(new, cairo.Surface)
+
+    with pytest.raises(cairo.Error) as excinfo:
+        surface.create_for_rectangle(0, 0, 10, -1)
+    assert excinfo.value.status == cairo.STATUS_INVALID_SIZE
+
+    with pytest.raises(TypeError):
+        surface.create_for_rectangle(0, 0, 10, object())
+
+
+def test_surface_create_similar_image():
+    surface = cairo.PDFSurface(None, 1, 1)
+    image = surface.create_similar_image(cairo.FORMAT_ARGB32, 24, 42)
+    assert image
+    assert isinstance(image, cairo.ImageSurface)
+    del surface
+    assert image.get_width() == 24
+    assert image.get_height() == 42
+    surface = cairo.PDFSurface(None, 1, 1)
+    with pytest.raises(TypeError):
+        surface.create_similar_image(cairo.FORMAT_ARGB32, 24, object())
+
+
+def test_surface_get_set_mime_data():
+    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
+    assert surface.get_mime_data("foo") is None
+    assert surface.get_mime_data(cairo.MIME_TYPE_JPEG) is None
+
+    surface.set_mime_data("foo", b"bar")
+    assert surface.get_mime_data("foo") == b"bar"
+    surface.set_mime_data("foo", None)
+    assert surface.get_mime_data("foo") is None
+
+    surface.set_mime_data(cairo.MIME_TYPE_JPEG, b"\x00quux\x00")
+    assert surface.get_mime_data(cairo.MIME_TYPE_JPEG)[:] == b"\x00quux\x00"
+    surface.set_mime_data(cairo.MIME_TYPE_JPEG, None)
+    assert surface.get_mime_data(cairo.MIME_TYPE_JPEG) is None
+    with pytest.raises(TypeError):
+        surface.set_mime_data(cairo.MIME_TYPE_JPEG, object())
+    with pytest.raises(TypeError):
+        surface.get_mime_data(object())
+
+
+def test_supports_mime_type():
+    surface = cairo.PDFSurface(None, 3, 3)
+    assert surface.supports_mime_type(cairo.MIME_TYPE_JPEG)
+    assert not surface.supports_mime_type("nope")
+    with pytest.raises(TypeError):
+        surface.supports_mime_type(object())
+
 
-@pytest.mark.skipif(not cairo.HAS_PNG_FUNCTIONS, reason="not png support")
 def test_image_surface_create_for_data_array():
     width, height = 255, 255
     data = array.array('B', [0] * width * height * 4)
@@ -177,7 +404,6 @@ def test_image_surface_create_for_data_array():
     os.unlink(filename)
 
 
-@pytest.mark.skipif(not cairo.HAS_PNG_FUNCTIONS, reason="not png support")
 def test_image_surface_write_to_png_filename_and_obj_compare():
     surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 128, 128)
     fd, filename = tempfile.mkstemp(prefix='pycairo_', suffix='.png')
@@ -193,7 +419,6 @@ def test_image_surface_write_to_png_filename_and_obj_compare():
     os.unlink(filename)
 
 
-@pytest.mark.skipif(not cairo.HAS_PNG_FUNCTIONS, reason="not png support")
 def test_image_surface_png_obj_roundtrip():
     fileobj = io.BytesIO()
     surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 128, 128)
@@ -201,9 +426,14 @@ def test_image_surface_png_obj_roundtrip():
     fileobj.seek(0)
     new_surface = cairo.ImageSurface.create_from_png(fileobj)
     assert surface.get_data() == new_surface.get_data()
+    with pytest.raises(TypeError):
+        cairo.ImageSurface.create_from_png()
+    with pytest.raises((ValueError, TypeError)):
+        cairo.ImageSurface.create_from_png("\x00")
+    with pytest.raises(TypeError):
+        cairo.ImageSurface.create_from_png(object())
 
 
-@pytest.mark.skipif(not cairo.HAS_PNG_FUNCTIONS, reason="not png support")
 def test_image_surface_png_file_roundtrip():
     fd, filename = tempfile.mkstemp(prefix='pycairo_', suffix='.png')
     os.close(fd)
@@ -216,11 +446,12 @@ def test_image_surface_png_file_roundtrip():
     os.unlink(filename)
 
 
-@pytest.mark.skipif(not cairo.HAS_PNG_FUNCTIONS, reason="not png support")
 def test_image_surface_write_to_png_error():
     surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 128, 128)
     with pytest.raises(TypeError):
         surface.write_to_png(42)
+    with pytest.raises((ValueError, TypeError)):
+        surface.write_to_png("\x00")
 
 
 def test_surface_from_stream_closed_before_finished():
@@ -241,6 +472,8 @@ def test_script_surface():
     dev.flush()
     assert b"42" in f.getvalue()
     assert b"paint" in f.getvalue()
+    with pytest.raises(TypeError):
+        cairo.ScriptSurface()
 
 
 def test_script_device_device_ref():
@@ -258,6 +491,8 @@ def test_script_surface_create_for_target():
     dev = cairo.ScriptDevice(f)
     surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
     script = cairo.ScriptSurface.create_for_target(dev, surface)
+    with pytest.raises(TypeError):
+        cairo.ScriptSurface.create_for_target(dev, object())
     assert isinstance(script, cairo.ScriptSurface)
     ctx = cairo.Context(script)
     ctx.set_source_rgb(0.25, 0.5, 1.0)
@@ -273,3 +508,116 @@ def test_script_surface_create_for_target():
     ctx.paint()
     surface.flush()
     assert bytes(surface.get_data()) == image_data
+
+
+def test_misc():
+    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
+    surface.copy_page()
+    surface.mark_dirty()
+    surface.show_page()
+
+
+@pytest.fixture
+def surface():
+    return cairo.ImageSurface(cairo.Format.ARGB32, 10, 10)
+
+
+@pytest.fixture
+def image_surface():
+    return cairo.ImageSurface(cairo.Format.ARGB32, 10, 10)
+
+
+def test_create_similar(surface):
+    similar = surface.create_similar(cairo.Content.COLOR, 10, 10)
+    assert isinstance(similar, cairo.Surface)
+    with pytest.raises(TypeError):
+        surface.create_similar()
+
+
+def test_get_device_offset(surface):
+    surface.set_device_offset(1, 1)
+    assert surface.get_device_offset() == (1, 1)
+    with pytest.raises(TypeError):
+        surface.set_device_offset(1, object())
+
+
+def test_get_fallback_resolution(surface):
+    surface.set_fallback_resolution(42, 42)
+    assert surface.get_fallback_resolution() == (42, 42)
+    with pytest.raises(TypeError):
+        surface.set_fallback_resolution(42, object())
+
+
+def test_mark_dirty_rectangle(surface):
+    surface.mark_dirty_rectangle(0, 0, 10, 10)
+    with pytest.raises(TypeError):
+        surface.mark_dirty_rectangle(0, 0, 10, object())
+
+
+def test_write_to_png(image_surface):
+    with pytest.raises(TypeError):
+        image_surface.write_to_png()
+
+    with pytest.raises((ValueError, TypeError)) as excinfo:
+        image_surface.write_to_png("\x00")
+    excinfo.match(r'.* (null|NUL) .*')
+
+    with pytest.raises(TypeError):
+        image_surface.write_to_png(object())
+
+
+def test_image_surface():
+    with pytest.raises(TypeError):
+        cairo.ImageSurface(cairo.FORMAT_ARGB32, 3, object())
+
+
+def test_image_surface_create_for_data():
+    format_ = cairo.FORMAT_ARGB32
+    surface = cairo.ImageSurface(format_, 3, 3)
+    ctx = cairo.Context(surface)
+    ctx.paint()
+    surface.flush()
+    buf = surface.get_data()
+
+    new = cairo.ImageSurface.create_for_data(buf, format_, 3, 3)
+    assert new.get_data() == buf
+
+    with pytest.raises(ValueError):
+        cairo.ImageSurface.create_for_data(buf, format_, 3, -1)
+    with pytest.raises(ValueError):
+        cairo.ImageSurface.create_for_data(buf, format_, -1, 3)
+
+    with pytest.raises(ValueError):
+        cairo.ImageSurface.create_for_data(buf, format_, 0, 0, -1)
+
+    with pytest.raises(cairo.Error) as excinfo:
+        cairo.ImageSurface.create_for_data(buf, format_, 3, 3, 3)
+
+    assert excinfo.value.status == cairo.STATUS_INVALID_STRIDE
+
+    with pytest.raises(TypeError):
+        cairo.ImageSurface.create_for_data(buf, format_, 3, object())
+
+
+def test_image_surface_stride_for_width():
+    v = cairo.ImageSurface.format_stride_for_width(cairo.Format.ARGB32, 10)
+    assert v == 40
+
+    with pytest.raises(TypeError):
+        cairo.ImageSurface.format_stride_for_width(
+            cairo.Format.ARGB32, object())
+
+
+def test_image_surface_get_stride(image_surface):
+    assert image_surface.get_stride() == 40
+
+
+def test_recording_surface():
+    with pytest.raises(TypeError):
+        cairo.RecordingSurface(cairo.CONTENT_COLOR, object())
+
+    with pytest.raises(TypeError):
+        cairo.RecordingSurface()
+
+    surface = cairo.RecordingSurface(cairo.CONTENT_COLOR, None)
+    assert surface.ink_extents() == (0.0, 0.0, 0.0, 0.0)
index cd67d22..5bdabd8 100644 (file)
@@ -9,7 +9,6 @@ import pytest
 numpy = pytest.importorskip("numpy")
 
 
-@pytest.mark.skipif(not cairo.HAS_PNG_FUNCTIONS, reason="no png support")
 def test_image_surface_create_for_data_numpy_array():
     width, height = 255, 255
     data = numpy.ndarray(shape=(height, width), dtype=numpy.uint32)
@@ -31,7 +30,6 @@ def test_image_surface_create_for_data_numpy_array():
     os.unlink(filename)
 
 
-@pytest.mark.skipif(not cairo.HAS_PNG_FUNCTIONS, reason="no png support")
 def test_image_surface_get_data_to_numpy_array():
     w, h = 128, 128
     surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)