mirror of https://github.com/GOSTSec/sgminer
Con Kolivas
11 years ago
289 changed files with 17848 additions and 151 deletions
@ -0,0 +1,554 @@ |
|||||||
|
Version 2.5 |
||||||
|
=========== |
||||||
|
|
||||||
|
Released 2013-09-19 |
||||||
|
|
||||||
|
* New features: |
||||||
|
|
||||||
|
- `json_pack()` and friends: Add format specifiers ``s#``, ``+`` and |
||||||
|
``+#``. |
||||||
|
|
||||||
|
- Add ``JSON_DECODE_INT_AS_REAL`` decoding flag to treat all numbers |
||||||
|
as real in the decoder (#123). |
||||||
|
|
||||||
|
- Add `json_array_foreach()`, paralleling `json_object_foreach()` |
||||||
|
(#118). |
||||||
|
|
||||||
|
* Bug fixes: |
||||||
|
|
||||||
|
- `json_dumps()` and friends: Don't crash if json is *NULL* and |
||||||
|
``JSON_ENCODE_ANY`` is set. |
||||||
|
|
||||||
|
- Fix a theoretical integer overflow in `jsonp_strdup()`. |
||||||
|
|
||||||
|
- Fix `l_isxdigit()` macro (#97). |
||||||
|
|
||||||
|
- Fix an off-by-one error in `json_array_remove()`. |
||||||
|
|
||||||
|
* Build: |
||||||
|
|
||||||
|
- Support CMake in addition to GNU Autotools (#106, #107, #112, |
||||||
|
#115, #120, #127). |
||||||
|
|
||||||
|
- Support building for Android (#109). |
||||||
|
|
||||||
|
- Don't use ``-Werror`` by default. |
||||||
|
|
||||||
|
- Support building and testing with VPATH (#93). |
||||||
|
|
||||||
|
- Fix compilation when ``NDEBUG`` is defined (#128) |
||||||
|
|
||||||
|
* Tests: |
||||||
|
|
||||||
|
- Fix a refleak in ``test/bin/json_process.c``. |
||||||
|
|
||||||
|
* Documentation: |
||||||
|
|
||||||
|
- Clarify the return value of `json_load_callback_t`. |
||||||
|
|
||||||
|
- Document how to circumvent problems with separate heaps on Windows. |
||||||
|
|
||||||
|
- Fix memory leaks and warnings in ``github_commits.c``. |
||||||
|
|
||||||
|
- Use `json_decref()` properly in tutorial. |
||||||
|
|
||||||
|
* Other: |
||||||
|
|
||||||
|
- Make it possible to forward declare ``struct json_t``. |
||||||
|
|
||||||
|
|
||||||
|
Version 2.4 |
||||||
|
=========== |
||||||
|
|
||||||
|
Released 2012-09-23 |
||||||
|
|
||||||
|
* New features: |
||||||
|
|
||||||
|
- Add `json_boolean()` macro that returns the JSON true or false |
||||||
|
value based on its argument (#86). |
||||||
|
|
||||||
|
- Add `json_load_callback()` that calls a callback function |
||||||
|
repeatedly to read the JSON input (#57). |
||||||
|
|
||||||
|
- Add JSON_ESCAPE_SLASH encoding flag to escape all occurences of |
||||||
|
``/`` with ``\/``. |
||||||
|
|
||||||
|
* Bug fixes: |
||||||
|
|
||||||
|
- Check for and reject NaN and Inf values for reals. Encoding these |
||||||
|
values resulted in invalid JSON. |
||||||
|
|
||||||
|
- Fix `json_real_set()` to return -1 on error. |
||||||
|
|
||||||
|
* Build: |
||||||
|
|
||||||
|
- Jansson now builds on Windows with Visual Studio 2010, and |
||||||
|
includes solution and project files in ``win32/vs2010/`` |
||||||
|
directory. |
||||||
|
|
||||||
|
- Fix build warnings (#77, #78). |
||||||
|
|
||||||
|
- Add ``-no-undefined`` to LDFLAGS (#90). |
||||||
|
|
||||||
|
* Tests: |
||||||
|
|
||||||
|
- Fix the symbol exports test on Linux/PPC64 (#88). |
||||||
|
|
||||||
|
* Documentation: |
||||||
|
|
||||||
|
- Fix typos (#73, #84). |
||||||
|
|
||||||
|
|
||||||
|
Version 2.3.1 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2012-04-20 |
||||||
|
|
||||||
|
* Build issues: |
||||||
|
|
||||||
|
- Only use ``long long`` if ``strtoll()`` is also available. |
||||||
|
|
||||||
|
* Documentation: |
||||||
|
|
||||||
|
- Fix the names of library version constants in documentation. (#52) |
||||||
|
|
||||||
|
- Change the tutorial to use GitHub API v3. (#65) |
||||||
|
|
||||||
|
* Tests: |
||||||
|
|
||||||
|
- Make some tests locale independent. (#51) |
||||||
|
|
||||||
|
- Distribute the library exports test in the tarball. |
||||||
|
|
||||||
|
- Make test run on shells that don't support the ``export FOO=bar`` |
||||||
|
syntax. |
||||||
|
|
||||||
|
|
||||||
|
Version 2.3 |
||||||
|
=========== |
||||||
|
|
||||||
|
Released 2012-01-27 |
||||||
|
|
||||||
|
* New features: |
||||||
|
|
||||||
|
- `json_unpack()` and friends: Add support for optional object keys |
||||||
|
with the ``{s?o}`` syntax. |
||||||
|
|
||||||
|
- Add `json_object_update_existing()` and |
||||||
|
`json_object_update_missing()`, for updating only existing keys or |
||||||
|
only adding missing keys to an object. (#37) |
||||||
|
|
||||||
|
- Add `json_object_foreach()` for more convenient iteration over |
||||||
|
objects. (#45, #46) |
||||||
|
|
||||||
|
- When decoding JSON, write the number of bytes that were read from |
||||||
|
input to ``error.position`` also on success. This is handy with |
||||||
|
``JSON_DISABLE_EOF_CHECK``. |
||||||
|
|
||||||
|
- Add support for decoding any JSON value, not just arrays or |
||||||
|
objects. The support is enabled with the new ``JSON_DECODE_ANY`` |
||||||
|
flag. Patch by Andrea Marchesini. (#4) |
||||||
|
|
||||||
|
* Bug fixes |
||||||
|
|
||||||
|
- Avoid problems with object's serial number growing too big. (#40, |
||||||
|
#41) |
||||||
|
|
||||||
|
- Decoding functions now return NULL if the first argument is NULL. |
||||||
|
Patch by Andrea Marchesini. |
||||||
|
|
||||||
|
- Include ``jansson_config.h.win32`` in the distribution tarball. |
||||||
|
|
||||||
|
- Remove ``+`` and leading zeros from exponents in the encoder. |
||||||
|
(#39) |
||||||
|
|
||||||
|
- Make Jansson build and work on MinGW. (#39, #38) |
||||||
|
|
||||||
|
* Documentation |
||||||
|
|
||||||
|
- Note that the same JSON values must not be encoded in parallel by |
||||||
|
separate threads. (#42) |
||||||
|
|
||||||
|
- Document MinGW support. |
||||||
|
|
||||||
|
|
||||||
|
Version 2.2.1 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2011-10-06 |
||||||
|
|
||||||
|
* Bug fixes: |
||||||
|
|
||||||
|
- Fix real number encoding and decoding under non-C locales. (#32) |
||||||
|
|
||||||
|
- Fix identifier decoding under non-UTF-8 locales. (#35) |
||||||
|
|
||||||
|
- `json_load_file()`: Open the input file in binary mode for maximum |
||||||
|
compatiblity. |
||||||
|
|
||||||
|
* Documentation: |
||||||
|
|
||||||
|
- Clarify the lifecycle of the result of the ``s`` fromat of |
||||||
|
`json_unpack()`. (#31) |
||||||
|
|
||||||
|
- Add some portability info. (#36) |
||||||
|
|
||||||
|
- Little clarifications here and there. |
||||||
|
|
||||||
|
* Other: |
||||||
|
|
||||||
|
- Some style fixes, issues detected by static analyzers. |
||||||
|
|
||||||
|
|
||||||
|
Version 2.2 |
||||||
|
=========== |
||||||
|
|
||||||
|
Released 2011-09-03 |
||||||
|
|
||||||
|
* New features: |
||||||
|
|
||||||
|
- `json_dump_callback()`: Pass the encoder output to a callback |
||||||
|
function in chunks. |
||||||
|
|
||||||
|
* Bug fixes: |
||||||
|
|
||||||
|
- `json_string_set()`: Check that target is a string and value is |
||||||
|
not NULL. |
||||||
|
|
||||||
|
* Other: |
||||||
|
|
||||||
|
- Documentation typo fixes and clarifications. |
||||||
|
|
||||||
|
|
||||||
|
Version 2.1 |
||||||
|
=========== |
||||||
|
|
||||||
|
Released 2011-06-10 |
||||||
|
|
||||||
|
* New features: |
||||||
|
|
||||||
|
- `json_loadb()`: Decode a string with a given size, useful if the |
||||||
|
string is not null terminated. |
||||||
|
|
||||||
|
- Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON |
||||||
|
value. By default, only arrays and objects can be encoded. (#19) |
||||||
|
|
||||||
|
- Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding |
||||||
|
error if any JSON object in the input contins duplicate keys. (#3) |
||||||
|
|
||||||
|
- Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a |
||||||
|
valid JSON input. This allows other data after the JSON data. |
||||||
|
|
||||||
|
* Bug fixes: |
||||||
|
|
||||||
|
- Fix an additional memory leak when memory allocation fails in |
||||||
|
`json_object_set()` and friends. |
||||||
|
|
||||||
|
- Clear errno before calling `strtod()` for better portability. (#27) |
||||||
|
|
||||||
|
* Building: |
||||||
|
|
||||||
|
- Avoid set-but-not-used warning/error in a test. (#20) |
||||||
|
|
||||||
|
* Other: |
||||||
|
|
||||||
|
- Minor clarifications to documentation. |
||||||
|
|
||||||
|
|
||||||
|
Version 2.0.1 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2011-03-31 |
||||||
|
|
||||||
|
* Bug fixes: |
||||||
|
|
||||||
|
- Replace a few `malloc()` and `free()` calls with their |
||||||
|
counterparts that support custom memory management. |
||||||
|
|
||||||
|
- Fix object key hashing in json_unpack() strict checking mode. |
||||||
|
|
||||||
|
- Fix the parentheses in ``JANSSON_VERSION_HEX`` macro. |
||||||
|
|
||||||
|
- Fix `json_object_size()` return value. |
||||||
|
|
||||||
|
- Fix a few compilation issues. |
||||||
|
|
||||||
|
* Portability: |
||||||
|
|
||||||
|
- Enhance portability of `va_copy()`. |
||||||
|
|
||||||
|
- Test framework portability enhancements. |
||||||
|
|
||||||
|
* Documentation: |
||||||
|
|
||||||
|
- Distribute ``doc/upgrading.rst`` with the source tarball. |
||||||
|
|
||||||
|
- Build documentation in strict mode in ``make distcheck``. |
||||||
|
|
||||||
|
|
||||||
|
Version 2.0 |
||||||
|
=========== |
||||||
|
|
||||||
|
Released 2011-02-28 |
||||||
|
|
||||||
|
This release is backwards incompatible with the 1.x release series. |
||||||
|
See the chapter "Upgrading from older versions" in documentation for |
||||||
|
details. |
||||||
|
|
||||||
|
* Backwards incompatible changes: |
||||||
|
|
||||||
|
- Unify unsigned integer usage in the API: All occurences of |
||||||
|
unsigned int and unsigned long have been replaced with size_t. |
||||||
|
|
||||||
|
- Change JSON integer's underlying type to the widest signed integer |
||||||
|
type available, i.e. long long if it's supported, otherwise long. |
||||||
|
Add a typedef json_int_t that defines the type. |
||||||
|
|
||||||
|
- Change the maximum indentation depth to 31 spaces in encoder. This |
||||||
|
frees up bits from the flags parameter of encoding functions |
||||||
|
`json_dumpf()`, `json_dumps()` and `json_dump_file()`. |
||||||
|
|
||||||
|
- For future needs, add a flags parameter to all decoding functions |
||||||
|
`json_loadf()`, `json_loads()` and `json_load_file()`. |
||||||
|
|
||||||
|
* New features |
||||||
|
|
||||||
|
- `json_pack()`, `json_pack_ex()`, `json_vpack_ex()`: Create JSON |
||||||
|
values based on a format string. |
||||||
|
|
||||||
|
- `json_unpack()`, `json_unpack_ex()`, `json_vunpack_ex()`: Simple |
||||||
|
value extraction and validation functionality based on a format |
||||||
|
string. |
||||||
|
|
||||||
|
- Add column, position and source fields to the ``json_error_t`` |
||||||
|
struct. |
||||||
|
|
||||||
|
- Enhance error reporting in the decoder. |
||||||
|
|
||||||
|
- ``JANSSON_VERSION`` et al.: Preprocessor constants that define the |
||||||
|
library version. |
||||||
|
|
||||||
|
- `json_set_alloc_funcs()`: Set custom memory allocation functions. |
||||||
|
|
||||||
|
* Fix many portability issues, especially on Windows. |
||||||
|
|
||||||
|
* Configuration |
||||||
|
|
||||||
|
- Add file ``jansson_config.h`` that contains site specific |
||||||
|
configuration. It's created automatically by the configure script, |
||||||
|
or can be created by hand if the configure script cannot be used. |
||||||
|
The file ``jansson_config.h.win32`` can be used without |
||||||
|
modifications on Windows systems. |
||||||
|
|
||||||
|
- Add a section to documentation describing how to build Jansson on |
||||||
|
Windows. |
||||||
|
|
||||||
|
- Documentation now requires Sphinx 1.0 or newer. |
||||||
|
|
||||||
|
|
||||||
|
Version 1.3 |
||||||
|
=========== |
||||||
|
|
||||||
|
Released 2010-06-13 |
||||||
|
|
||||||
|
* New functions: |
||||||
|
|
||||||
|
- `json_object_iter_set()`, `json_object_iter_set_new()`: Change |
||||||
|
object contents while iterating over it. |
||||||
|
|
||||||
|
- `json_object_iter_at()`: Return an iterator that points to a |
||||||
|
specific object item. |
||||||
|
|
||||||
|
* New encoding flags: |
||||||
|
|
||||||
|
- ``JSON_PRESERVE_ORDER``: Preserve the insertion order of object |
||||||
|
keys. |
||||||
|
|
||||||
|
* Bug fixes: |
||||||
|
|
||||||
|
- Fix an error that occured when an array or object was first |
||||||
|
encoded as empty, then populated with some data, and then |
||||||
|
re-encoded |
||||||
|
|
||||||
|
- Fix the situation like above, but when the first encoding resulted |
||||||
|
in an error |
||||||
|
|
||||||
|
* Documentation: |
||||||
|
|
||||||
|
- Clarify the documentation on reference stealing, providing an |
||||||
|
example usage pattern |
||||||
|
|
||||||
|
|
||||||
|
Version 1.2.1 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2010-04-03 |
||||||
|
|
||||||
|
* Bug fixes: |
||||||
|
|
||||||
|
- Fix reference counting on ``true``, ``false`` and ``null`` |
||||||
|
- Estimate real number underflows in decoder with 0.0 instead of |
||||||
|
issuing an error |
||||||
|
|
||||||
|
* Portability: |
||||||
|
|
||||||
|
- Make ``int32_t`` available on all systems |
||||||
|
- Support compilers that don't have the ``inline`` keyword |
||||||
|
- Require Autoconf 2.60 (for ``int32_t``) |
||||||
|
|
||||||
|
* Tests: |
||||||
|
|
||||||
|
- Print test names correctly when ``VERBOSE=1`` |
||||||
|
- ``test/suites/api``: Fail when a test fails |
||||||
|
- Enhance tests for iterators |
||||||
|
- Enhance tests for decoding texts that contain null bytes |
||||||
|
|
||||||
|
* Documentation: |
||||||
|
|
||||||
|
- Don't remove ``changes.rst`` in ``make clean`` |
||||||
|
- Add a chapter on RFC conformance |
||||||
|
|
||||||
|
|
||||||
|
Version 1.2 |
||||||
|
=========== |
||||||
|
|
||||||
|
Released 2010-01-21 |
||||||
|
|
||||||
|
* New functions: |
||||||
|
|
||||||
|
- `json_equal()`: Test whether two JSON values are equal |
||||||
|
- `json_copy()` and `json_deep_copy()`: Make shallow and deep copies |
||||||
|
of JSON values |
||||||
|
- Add a version of all functions taking a string argument that |
||||||
|
doesn't check for valid UTF-8: `json_string_nocheck()`, |
||||||
|
`json_string_set_nocheck()`, `json_object_set_nocheck()`, |
||||||
|
`json_object_set_new_nocheck()` |
||||||
|
|
||||||
|
* New encoding flags: |
||||||
|
|
||||||
|
- ``JSON_SORT_KEYS``: Sort objects by key |
||||||
|
- ``JSON_ENSURE_ASCII``: Escape all non-ASCII Unicode characters |
||||||
|
- ``JSON_COMPACT``: Use a compact representation with all unneeded |
||||||
|
whitespace stripped |
||||||
|
|
||||||
|
* Bug fixes: |
||||||
|
|
||||||
|
- Revise and unify whitespace usage in encoder: Add spaces between |
||||||
|
array and object items, never append newline to output. |
||||||
|
- Remove const qualifier from the ``json_t`` parameter in |
||||||
|
`json_string_set()`, `json_integer_set()` and `json_real_set`. |
||||||
|
- Use ``int32_t`` internally for representing Unicode code points |
||||||
|
(int is not enough on all platforms) |
||||||
|
|
||||||
|
* Other changes: |
||||||
|
|
||||||
|
- Convert ``CHANGES`` (this file) to reStructured text and add it to |
||||||
|
HTML documentation |
||||||
|
- The test system has been refactored. Python is no longer required |
||||||
|
to run the tests. |
||||||
|
- Documentation can now be built by invoking ``make html`` |
||||||
|
- Support for pkg-config |
||||||
|
|
||||||
|
|
||||||
|
Version 1.1.3 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2009-12-18 |
||||||
|
|
||||||
|
* Encode reals correctly, so that first encoding and then decoding a |
||||||
|
real always produces the same value |
||||||
|
* Don't export private symbols in ``libjansson.so`` |
||||||
|
|
||||||
|
|
||||||
|
Version 1.1.2 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2009-11-08 |
||||||
|
|
||||||
|
* Fix a bug where an error message was not produced if the input file |
||||||
|
could not be opened in `json_load_file()` |
||||||
|
* Fix an assertion failure in decoder caused by a minus sign without a |
||||||
|
digit after it |
||||||
|
* Remove an unneeded include of ``stdint.h`` in ``jansson.h`` |
||||||
|
|
||||||
|
|
||||||
|
Version 1.1.1 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2009-10-26 |
||||||
|
|
||||||
|
* All documentation files were not distributed with v1.1; build |
||||||
|
documentation in make distcheck to prevent this in the future |
||||||
|
* Fix v1.1 release date in ``CHANGES`` |
||||||
|
|
||||||
|
|
||||||
|
Version 1.1 |
||||||
|
=========== |
||||||
|
|
||||||
|
Released 2009-10-20 |
||||||
|
|
||||||
|
* API additions and improvements: |
||||||
|
|
||||||
|
- Extend array and object APIs |
||||||
|
- Add functions to modify integer, real and string values |
||||||
|
- Improve argument validation |
||||||
|
- Use unsigned int instead of ``uint32_t`` for encoding flags |
||||||
|
|
||||||
|
* Enhance documentation |
||||||
|
|
||||||
|
- Add getting started guide and tutorial |
||||||
|
- Fix some typos |
||||||
|
- General clarifications and cleanup |
||||||
|
|
||||||
|
* Check for integer and real overflows and underflows in decoder |
||||||
|
* Make singleton values thread-safe (``true``, ``false`` and ``null``) |
||||||
|
* Enhance circular reference handling |
||||||
|
* Don't define ``-std=c99`` in ``AM_CFLAGS`` |
||||||
|
* Add C++ guards to ``jansson.h`` |
||||||
|
* Minor performance and portability improvements |
||||||
|
* Expand test coverage |
||||||
|
|
||||||
|
|
||||||
|
Version 1.0.4 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2009-10-11 |
||||||
|
|
||||||
|
* Relax Autoconf version requirement to 2.59 |
||||||
|
* Make Jansson compile on platforms where plain ``char`` is unsigned |
||||||
|
* Fix API tests for object |
||||||
|
|
||||||
|
|
||||||
|
Version 1.0.3 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2009-09-14 |
||||||
|
|
||||||
|
* Check for integer and real overflows and underflows in decoder |
||||||
|
* Use the Python json module for tests, or simplejson if the json |
||||||
|
module is not found |
||||||
|
* Distribute changelog (this file) |
||||||
|
|
||||||
|
|
||||||
|
Version 1.0.2 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2009-09-08 |
||||||
|
|
||||||
|
* Handle EOF correctly in decoder |
||||||
|
|
||||||
|
|
||||||
|
Version 1.0.1 |
||||||
|
============= |
||||||
|
|
||||||
|
Released 2009-09-04 |
||||||
|
|
||||||
|
* Fixed broken `json_is_boolean()` |
||||||
|
|
||||||
|
|
||||||
|
Version 1.0 |
||||||
|
=========== |
||||||
|
|
||||||
|
Released 2009-08-25 |
||||||
|
|
||||||
|
* Initial release |
@ -1,4 +1,4 @@ |
|||||||
Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org> |
Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
of this software and associated documentation files (the "Software"), to deal |
of this software and associated documentation files (the "Software"), to deal |
@ -0,0 +1,15 @@ |
|||||||
|
EXTRA_DIST = CHANGES LICENSE README.rst win32 |
||||||
|
SUBDIRS = doc src test |
||||||
|
|
||||||
|
# "make distcheck" builds the dvi target, so use it to check that the
|
||||||
|
# documentation is built correctly.
|
||||||
|
dvi: |
||||||
|
$(MAKE) SPHINXOPTS_EXTRA=-W html |
||||||
|
|
||||||
|
pkgconfigdir = $(libdir)/pkgconfig |
||||||
|
pkgconfig_DATA = jansson.pc |
||||||
|
|
||||||
|
if GCC |
||||||
|
# These flags are gcc specific
|
||||||
|
export AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement |
||||||
|
endif |
@ -0,0 +1,63 @@ |
|||||||
|
Jansson README |
||||||
|
============== |
||||||
|
|
||||||
|
.. image:: https://travis-ci.org/akheron/jansson.png |
||||||
|
:alt: Build status |
||||||
|
:target: https://travis-ci.org/akheron/jansson |
||||||
|
|
||||||
|
Jansson_ is a C library for encoding, decoding and manipulating JSON |
||||||
|
data. Its main features and design principles are: |
||||||
|
|
||||||
|
- Simple and intuitive API and data model |
||||||
|
|
||||||
|
- Comprehensive documentation |
||||||
|
|
||||||
|
- No dependencies on other libraries |
||||||
|
|
||||||
|
- Full Unicode support (UTF-8) |
||||||
|
|
||||||
|
- Extensive test suite |
||||||
|
|
||||||
|
Jansson is licensed under the `MIT license`_; see LICENSE in the |
||||||
|
source distribution for details. |
||||||
|
|
||||||
|
|
||||||
|
Compilation and Installation |
||||||
|
---------------------------- |
||||||
|
|
||||||
|
If you obtained a source tarball, just use the standard autotools |
||||||
|
commands:: |
||||||
|
|
||||||
|
$ ./configure |
||||||
|
$ make |
||||||
|
$ make install |
||||||
|
|
||||||
|
To run the test suite, invoke:: |
||||||
|
|
||||||
|
$ make check |
||||||
|
|
||||||
|
If the source has been checked out from a Git repository, the |
||||||
|
./configure script has to be generated first. The easiest way is to |
||||||
|
use autoreconf:: |
||||||
|
|
||||||
|
$ autoreconf -i |
||||||
|
|
||||||
|
|
||||||
|
Documentation |
||||||
|
------------- |
||||||
|
|
||||||
|
Prebuilt HTML documentation is available at |
||||||
|
http://www.digip.org/jansson/doc/. |
||||||
|
|
||||||
|
The documentation source is in the ``doc/`` subdirectory. To generate |
||||||
|
HTML documentation, invoke:: |
||||||
|
|
||||||
|
$ make html |
||||||
|
|
||||||
|
Then, point your browser to ``doc/_build/html/index.html``. Sphinx_ |
||||||
|
1.0 or newer is required to generate the documentation. |
||||||
|
|
||||||
|
|
||||||
|
.. _Jansson: http://www.digip.org/jansson/ |
||||||
|
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php |
||||||
|
.. _Sphinx: http://sphinx.pocoo.org/ |
@ -0,0 +1,57 @@ |
|||||||
|
AC_PREREQ([2.60]) |
||||||
|
AC_INIT([jansson], [2.5], [petri@digip.org]) |
||||||
|
|
||||||
|
AM_INIT_AUTOMAKE([1.10 foreign]) |
||||||
|
|
||||||
|
AC_CONFIG_SRCDIR([src/value.c]) |
||||||
|
AC_CONFIG_HEADERS([config.h]) |
||||||
|
|
||||||
|
# Checks for programs. |
||||||
|
AC_PROG_CC |
||||||
|
AC_PROG_LIBTOOL |
||||||
|
AM_CONDITIONAL([GCC], [test x$GCC = xyes]) |
||||||
|
|
||||||
|
# Checks for libraries. |
||||||
|
|
||||||
|
# Checks for header files. |
||||||
|
AC_CHECK_HEADERS([locale.h]) |
||||||
|
|
||||||
|
# Checks for typedefs, structures, and compiler characteristics. |
||||||
|
AC_TYPE_INT32_T |
||||||
|
AC_TYPE_LONG_LONG_INT |
||||||
|
|
||||||
|
AC_C_INLINE |
||||||
|
case $ac_cv_c_inline in |
||||||
|
yes) json_inline=inline;; |
||||||
|
no) json_inline=;; |
||||||
|
*) json_inline=$ac_cv_c_inline;; |
||||||
|
esac |
||||||
|
AC_SUBST([json_inline]) |
||||||
|
|
||||||
|
# Checks for library functions. |
||||||
|
AC_CHECK_FUNCS([strtoll localeconv]) |
||||||
|
|
||||||
|
case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in |
||||||
|
yesyes) json_have_long_long=1;; |
||||||
|
*) json_have_long_long=0;; |
||||||
|
esac |
||||||
|
AC_SUBST([json_have_long_long]) |
||||||
|
|
||||||
|
case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in |
||||||
|
yesyes) json_have_localeconv=1;; |
||||||
|
*) json_have_localeconv=0;; |
||||||
|
esac |
||||||
|
AC_SUBST([json_have_localeconv]) |
||||||
|
|
||||||
|
AC_CONFIG_FILES([ |
||||||
|
jansson.pc |
||||||
|
Makefile |
||||||
|
doc/Makefile |
||||||
|
src/Makefile |
||||||
|
src/jansson_config.h |
||||||
|
test/Makefile |
||||||
|
test/bin/Makefile |
||||||
|
test/suites/Makefile |
||||||
|
test/suites/api/Makefile |
||||||
|
]) |
||||||
|
AC_OUTPUT |
@ -0,0 +1,20 @@ |
|||||||
|
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
|
||||||
|
gettingstarted.rst github_commits.c index.rst portability.rst \
|
||||||
|
tutorial.rst upgrading.rst ext/refcounting.py |
||||||
|
|
||||||
|
SPHINXBUILD = sphinx-build |
||||||
|
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA) |
||||||
|
|
||||||
|
html-local: |
||||||
|
$(SPHINXBUILD) -b html $(SPHINXOPTS) $(srcdir) _build/html |
||||||
|
|
||||||
|
install-html-local: html |
||||||
|
mkdir -p $(DESTDIR)$(htmldir) |
||||||
|
cp -r _build/html $(DESTDIR)$(htmldir) |
||||||
|
|
||||||
|
uninstall-local: |
||||||
|
rm -rf $(DESTDIR)$(htmldir) |
||||||
|
|
||||||
|
clean-local: |
||||||
|
rm -rf _build |
||||||
|
rm -f ext/refcounting.pyc |
@ -0,0 +1,5 @@ |
|||||||
|
To build the documentation, invoke |
||||||
|
|
||||||
|
make html |
||||||
|
|
||||||
|
Then point your browser to _build/html/index.html. |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,5 @@ |
|||||||
|
****************** |
||||||
|
Changes in Jansson |
||||||
|
****************** |
||||||
|
|
||||||
|
.. include:: ../CHANGES |
@ -0,0 +1,217 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# |
||||||
|
# Jansson documentation build configuration file, created by |
||||||
|
# sphinx-quickstart on Sun Sep 5 21:47:20 2010. |
||||||
|
# |
||||||
|
# This file is execfile()d with the current directory set to its containing dir. |
||||||
|
# |
||||||
|
# Note that not all possible configuration values are present in this |
||||||
|
# autogenerated file. |
||||||
|
# |
||||||
|
# All configuration values have a default; values that are commented out |
||||||
|
# serve to show the default. |
||||||
|
|
||||||
|
import sys, os |
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory, |
||||||
|
# add these directories to sys.path here. If the directory is relative to the |
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here. |
||||||
|
sys.path.insert(0, os.path.abspath('ext')) |
||||||
|
|
||||||
|
# -- General configuration ----------------------------------------------------- |
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here. |
||||||
|
needs_sphinx = '1.0' |
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be extensions |
||||||
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. |
||||||
|
extensions = ['refcounting'] |
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory. |
||||||
|
templates_path = ['_templates'] |
||||||
|
|
||||||
|
# The suffix of source filenames. |
||||||
|
source_suffix = '.rst' |
||||||
|
|
||||||
|
# The encoding of source files. |
||||||
|
#source_encoding = 'utf-8-sig' |
||||||
|
|
||||||
|
# The master toctree document. |
||||||
|
master_doc = 'index' |
||||||
|
|
||||||
|
# General information about the project. |
||||||
|
project = u'Jansson' |
||||||
|
copyright = u'2009-2013, Petri Lehtinen' |
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for |
||||||
|
# |version| and |release|, also used in various other places throughout the |
||||||
|
# built documents. |
||||||
|
# |
||||||
|
# The short X.Y version. |
||||||
|
version = '2.5' |
||||||
|
# The full version, including alpha/beta/rc tags. |
||||||
|
release = version |
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation |
||||||
|
# for a list of supported languages. |
||||||
|
#language = None |
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to some |
||||||
|
# non-false value, then it is used: |
||||||
|
#today = '' |
||||||
|
# Else, today_fmt is used as the format for a strftime call. |
||||||
|
#today_fmt = '%B %d, %Y' |
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and |
||||||
|
# directories to ignore when looking for source files. |
||||||
|
exclude_patterns = ['_build'] |
||||||
|
|
||||||
|
# The reST default role (used for this markup: `text`) to use for all documents. |
||||||
|
default_role = 'c:func' |
||||||
|
primary_domain = 'c' |
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text. |
||||||
|
#add_function_parentheses = True |
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description |
||||||
|
# unit titles (such as .. function::). |
||||||
|
#add_module_names = True |
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the |
||||||
|
# output. They are ignored by default. |
||||||
|
#show_authors = False |
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use. |
||||||
|
pygments_style = 'sphinx' |
||||||
|
|
||||||
|
# A list of ignored prefixes for module index sorting. |
||||||
|
#modindex_common_prefix = [] |
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output --------------------------------------------------- |
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for |
||||||
|
# a list of builtin themes. |
||||||
|
#html_theme = 'default' |
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme |
||||||
|
# further. For a list of options available for each theme, see the |
||||||
|
# documentation. |
||||||
|
#html_theme_options = {} |
||||||
|
|
||||||
|
# Add any paths that contain custom themes here, relative to this directory. |
||||||
|
#html_theme_path = [] |
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to |
||||||
|
# "<project> v<release> documentation". |
||||||
|
#html_title = None |
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as html_title. |
||||||
|
#html_short_title = None |
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top |
||||||
|
# of the sidebar. |
||||||
|
#html_logo = None |
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of the |
||||||
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 |
||||||
|
# pixels large. |
||||||
|
#html_favicon = None |
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here, |
||||||
|
# relative to this directory. They are copied after the builtin static files, |
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css". |
||||||
|
#html_static_path = ['_static'] |
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, |
||||||
|
# using the given strftime format. |
||||||
|
#html_last_updated_fmt = '%b %d, %Y' |
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to |
||||||
|
# typographically correct entities. |
||||||
|
#html_use_smartypants = True |
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names. |
||||||
|
#html_sidebars = {} |
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to |
||||||
|
# template names. |
||||||
|
#html_additional_pages = {} |
||||||
|
|
||||||
|
# If false, no module index is generated. |
||||||
|
#html_domain_indices = True |
||||||
|
|
||||||
|
# If false, no index is generated. |
||||||
|
#html_use_index = True |
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter. |
||||||
|
#html_split_index = False |
||||||
|
|
||||||
|
# If true, links to the reST sources are added to the pages. |
||||||
|
#html_show_sourcelink = True |
||||||
|
|
||||||
|
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. |
||||||
|
#html_show_sphinx = True |
||||||
|
|
||||||
|
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. |
||||||
|
#html_show_copyright = True |
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages will |
||||||
|
# contain a <link> tag referring to it. The value of this option must be the |
||||||
|
# base URL from which the finished HTML is served. |
||||||
|
#html_use_opensearch = '' |
||||||
|
|
||||||
|
# This is the file name suffix for HTML files (e.g. ".xhtml"). |
||||||
|
#html_file_suffix = None |
||||||
|
|
||||||
|
# Output file base name for HTML help builder. |
||||||
|
htmlhelp_basename = 'Janssondoc' |
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output -------------------------------------------------- |
||||||
|
|
||||||
|
# The paper size ('letter' or 'a4'). |
||||||
|
#latex_paper_size = 'letter' |
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt'). |
||||||
|
#latex_font_size = '10pt' |
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples |
||||||
|
# (source start file, target name, title, author, documentclass [howto/manual]). |
||||||
|
latex_documents = [ |
||||||
|
('index', 'Jansson.tex', u'Jansson Documentation', |
||||||
|
u'Petri Lehtinen', 'manual'), |
||||||
|
] |
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top of |
||||||
|
# the title page. |
||||||
|
#latex_logo = None |
||||||
|
|
||||||
|
# For "manual" documents, if this is true, then toplevel headings are parts, |
||||||
|
# not chapters. |
||||||
|
#latex_use_parts = False |
||||||
|
|
||||||
|
# If true, show page references after internal links. |
||||||
|
#latex_show_pagerefs = False |
||||||
|
|
||||||
|
# If true, show URL addresses after external links. |
||||||
|
#latex_show_urls = False |
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble. |
||||||
|
#latex_preamble = '' |
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals. |
||||||
|
#latex_appendices = [] |
||||||
|
|
||||||
|
# If false, no module index is generated. |
||||||
|
#latex_domain_indices = True |
||||||
|
|
||||||
|
|
||||||
|
# -- Options for manual page output -------------------------------------------- |
||||||
|
|
||||||
|
# One entry per manual page. List of tuples |
||||||
|
# (source start file, name, description, authors, manual section). |
||||||
|
man_pages = [ |
||||||
|
('index', 'jansson', u'Jansson Documentation', |
||||||
|
[u'Petri Lehtinen'], 1) |
||||||
|
] |
@ -0,0 +1,114 @@ |
|||||||
|
.. _rfc-conformance: |
||||||
|
|
||||||
|
*************** |
||||||
|
RFC Conformance |
||||||
|
*************** |
||||||
|
|
||||||
|
JSON is specified in :rfc:`4627`, *"The application/json Media Type |
||||||
|
for JavaScript Object Notation (JSON)"*. |
||||||
|
|
||||||
|
Character Encoding |
||||||
|
================== |
||||||
|
|
||||||
|
Jansson only supports UTF-8 encoded JSON texts. It does not support or |
||||||
|
auto-detect any of the other encodings mentioned in the RFC, namely |
||||||
|
UTF-16LE, UTF-16BE, UTF-32LE or UTF-32BE. Pure ASCII is supported, as |
||||||
|
it's a subset of UTF-8. |
||||||
|
|
||||||
|
Strings |
||||||
|
======= |
||||||
|
|
||||||
|
JSON strings are mapped to C-style null-terminated character arrays, |
||||||
|
and UTF-8 encoding is used internally. Strings may not contain |
||||||
|
embedded null characters, not even escaped ones. |
||||||
|
|
||||||
|
For example, trying to decode the following JSON text leads to a parse |
||||||
|
error:: |
||||||
|
|
||||||
|
["this string contains the null character: \u0000"] |
||||||
|
|
||||||
|
All other Unicode codepoints U+0001 through U+10FFFF are allowed. |
||||||
|
|
||||||
|
Unicode normalization or any other transformation is never performed |
||||||
|
on any strings (string values or object keys). When checking for |
||||||
|
equivalence of strings or object keys, the comparison is performed |
||||||
|
byte by byte between the original UTF-8 representations of the |
||||||
|
strings. |
||||||
|
|
||||||
|
Numbers |
||||||
|
======= |
||||||
|
|
||||||
|
.. _real-vs-integer: |
||||||
|
|
||||||
|
Real vs. Integer |
||||||
|
---------------- |
||||||
|
|
||||||
|
JSON makes no distinction between real and integer numbers; Jansson |
||||||
|
does. Real numbers are mapped to the ``double`` type and integers to |
||||||
|
the ``json_int_t`` type, which is a typedef of ``long long`` or |
||||||
|
``long``, depending on whether ``long long`` is supported by your |
||||||
|
compiler or not. |
||||||
|
|
||||||
|
A JSON number is considered to be a real number if its lexical |
||||||
|
representation includes one of ``e``, ``E``, or ``.``; regardless if |
||||||
|
its actual numeric value is a true integer (e.g., all of ``1E6``, |
||||||
|
``3.0``, ``400E-2``, and ``3.14E3`` are mathematical integers, but |
||||||
|
will be treated as real values). With the ``JSON_DECODE_INT_AS_REAL`` |
||||||
|
decoder flag set all numbers are interpreted as real. |
||||||
|
|
||||||
|
All other JSON numbers are considered integers. |
||||||
|
|
||||||
|
When encoding to JSON, real values are always represented |
||||||
|
with a fractional part; e.g., the ``double`` value 3.0 will be |
||||||
|
represented in JSON as ``3.0``, not ``3``. |
||||||
|
|
||||||
|
Overflow, Underflow & Precision |
||||||
|
------------------------------- |
||||||
|
|
||||||
|
Real numbers whose absolute values are too small to be represented in |
||||||
|
a C ``double`` will be silently estimated with 0.0. Thus, depending on |
||||||
|
platform, JSON numbers very close to zero such as 1E-999 may result in |
||||||
|
0.0. |
||||||
|
|
||||||
|
Real numbers whose absolute values are too large to be represented in |
||||||
|
a C ``double`` will result in an overflow error (a JSON decoding |
||||||
|
error). Thus, depending on platform, JSON numbers like 1E+999 or |
||||||
|
-1E+999 may result in a parsing error. |
||||||
|
|
||||||
|
Likewise, integer numbers whose absolute values are too large to be |
||||||
|
represented in the ``json_int_t`` type (see above) will result in an |
||||||
|
overflow error (a JSON decoding error). Thus, depending on platform, |
||||||
|
JSON numbers like 1000000000000000 may result in parsing error. |
||||||
|
|
||||||
|
Parsing JSON real numbers may result in a loss of precision. As long |
||||||
|
as overflow does not occur (i.e. a total loss of precision), the |
||||||
|
rounded approximate value is silently used. Thus the JSON number |
||||||
|
1.000000000000000005 may, depending on platform, result in the |
||||||
|
``double`` value 1.0. |
||||||
|
|
||||||
|
Signed zeros |
||||||
|
------------ |
||||||
|
|
||||||
|
JSON makes no statement about what a number means; however Javascript |
||||||
|
(ECMAscript) does state that +0.0 and -0.0 must be treated as being |
||||||
|
distinct values, i.e. -0.0 |not-equal| 0.0. Jansson relies on the |
||||||
|
underlying floating point library in the C environment in which it is |
||||||
|
compiled. Therefore it is platform-dependent whether 0.0 and -0.0 will |
||||||
|
be distinct values. Most platforms that use the IEEE 754 |
||||||
|
floating-point standard will support signed zeros. |
||||||
|
|
||||||
|
Note that this only applies to floating-point; neither JSON, C, or |
||||||
|
IEEE support the concept of signed integer zeros. |
||||||
|
|
||||||
|
.. |not-equal| unicode:: U+2260 |
||||||
|
|
||||||
|
Types |
||||||
|
----- |
||||||
|
|
||||||
|
No support is provided in Jansson for any C numeric types other than |
||||||
|
``json_int_t`` and ``double``. This excludes things such as unsigned |
||||||
|
types, ``long double``, etc. Obviously, shorter types like ``short``, |
||||||
|
``int``, ``long`` (if ``json_int_t`` is ``long long``) and ``float`` |
||||||
|
are implicitly handled via the ordinary C type coercion rules (subject |
||||||
|
to overflow semantics). Also, no support or hooks are provided for any |
||||||
|
supplemental "bignum" type add-on packages. |
@ -0,0 +1,59 @@ |
|||||||
|
""" |
||||||
|
refcounting |
||||||
|
~~~~~~~~~~~ |
||||||
|
|
||||||
|
Reference count annotations for C API functions. Has the same |
||||||
|
result as the sphinx.ext.refcounting extension but works for all |
||||||
|
functions regardless of the signature, and the reference counting |
||||||
|
information is written inline with the documentation instead of a |
||||||
|
separate file. |
||||||
|
|
||||||
|
Adds a new directive "refcounting". The directive has no content |
||||||
|
and one required positional parameter:: "new" or "borrow". |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
.. cfunction:: json_t *json_object(void) |
||||||
|
|
||||||
|
.. refcounting:: new |
||||||
|
|
||||||
|
<description of the json_object function> |
||||||
|
|
||||||
|
:copyright: Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
:license: MIT, see LICENSE for details. |
||||||
|
""" |
||||||
|
|
||||||
|
from docutils import nodes |
||||||
|
|
||||||
|
class refcounting(nodes.emphasis): pass |
||||||
|
|
||||||
|
def visit(self, node): |
||||||
|
self.visit_emphasis(node) |
||||||
|
|
||||||
|
def depart(self, node): |
||||||
|
self.depart_emphasis(node) |
||||||
|
|
||||||
|
def html_visit(self, node): |
||||||
|
self.body.append(self.starttag(node, 'em', '', CLASS='refcount')) |
||||||
|
|
||||||
|
def html_depart(self, node): |
||||||
|
self.body.append('</em>') |
||||||
|
|
||||||
|
|
||||||
|
def refcounting_directive(name, arguments, options, content, lineno, |
||||||
|
content_offset, block_text, state, state_machine): |
||||||
|
if arguments[0] == 'borrow': |
||||||
|
text = 'Return value: Borrowed reference.' |
||||||
|
elif arguments[0] == 'new': |
||||||
|
text = 'Return value: New reference.' |
||||||
|
else: |
||||||
|
raise Error('Valid arguments: new, borrow') |
||||||
|
|
||||||
|
return [refcounting(text, text)] |
||||||
|
|
||||||
|
def setup(app): |
||||||
|
app.add_node(refcounting, |
||||||
|
html=(html_visit, html_depart), |
||||||
|
latex=(visit, depart), |
||||||
|
text=(visit, depart)) |
||||||
|
app.add_directive('refcounting', refcounting_directive, 0, (1, 0, 0)) |
@ -0,0 +1,237 @@ |
|||||||
|
*************** |
||||||
|
Getting Started |
||||||
|
*************** |
||||||
|
|
||||||
|
.. highlight:: c |
||||||
|
|
||||||
|
Compiling and Installing Jansson |
||||||
|
================================ |
||||||
|
|
||||||
|
The Jansson source is available at |
||||||
|
http://www.digip.org/jansson/releases/. |
||||||
|
|
||||||
|
Unix-like systems (including MinGW) |
||||||
|
----------------------------------- |
||||||
|
|
||||||
|
Unpack the source tarball and change to the source directory: |
||||||
|
|
||||||
|
.. parsed-literal:: |
||||||
|
|
||||||
|
bunzip2 -c jansson-|release|.tar.bz2 | tar xf - |
||||||
|
cd jansson-|release| |
||||||
|
|
||||||
|
The source uses GNU Autotools (autoconf_, automake_, libtool_), so |
||||||
|
compiling and installing is extremely simple:: |
||||||
|
|
||||||
|
./configure |
||||||
|
make |
||||||
|
make check |
||||||
|
make install |
||||||
|
|
||||||
|
To change the destination directory (``/usr/local`` by default), use |
||||||
|
the ``--prefix=DIR`` argument to ``./configure``. See ``./configure |
||||||
|
--help`` for the list of all possible installation options. (There are |
||||||
|
no options to customize the resulting Jansson binary.) |
||||||
|
|
||||||
|
The command ``make check`` runs the test suite distributed with |
||||||
|
Jansson. This step is not strictly necessary, but it may find possible |
||||||
|
problems that Jansson has on your platform. If any problems are found, |
||||||
|
please report them. |
||||||
|
|
||||||
|
If you obtained the source from a Git repository (or any other source |
||||||
|
control system), there's no ``./configure`` script as it's not kept in |
||||||
|
version control. To create the script, the build system needs to be |
||||||
|
bootstrapped. There are many ways to do this, but the easiest one is |
||||||
|
to use ``autoreconf``:: |
||||||
|
|
||||||
|
autoreconf -vi |
||||||
|
|
||||||
|
This command creates the ``./configure`` script, which can then be |
||||||
|
used as described above. |
||||||
|
|
||||||
|
.. _autoconf: http://www.gnu.org/software/autoconf/ |
||||||
|
.. _automake: http://www.gnu.org/software/automake/ |
||||||
|
.. _libtool: http://www.gnu.org/software/libtool/ |
||||||
|
|
||||||
|
|
||||||
|
.. _build-cmake: |
||||||
|
|
||||||
|
CMake (various platforms, including Windows) |
||||||
|
-------------------------------------------- |
||||||
|
|
||||||
|
Jansson can be built using CMake_. Create a build directory for an |
||||||
|
out-of-tree build, change to that directory, and run ``cmake`` (or ``ccmake``, |
||||||
|
``cmake-gui``, or similar) to configure the project. |
||||||
|
|
||||||
|
See the examples below for more detailed information. |
||||||
|
|
||||||
|
.. note:: In the below examples ``..`` is used as an argument for ``cmake``. |
||||||
|
This is simply the path to the jansson project root directory. |
||||||
|
In the example it is assumed you've created a sub-directory ``build`` |
||||||
|
and are using that. You could use any path you want. |
||||||
|
|
||||||
|
.. _build-cmake-unix: |
||||||
|
|
||||||
|
Unix (Make files) |
||||||
|
^^^^^^^^^^^^^^^^^ |
||||||
|
Generating make files on unix: |
||||||
|
|
||||||
|
.. parsed-literal:: |
||||||
|
|
||||||
|
bunzip2 -c jansson-|release|.tar.bz2 | tar xf - |
||||||
|
cd jansson-|release| |
||||||
|
|
||||||
|
mkdir build |
||||||
|
cd build |
||||||
|
cmake .. # or `ccmake ..` for a GUI. |
||||||
|
|
||||||
|
Then to build:: |
||||||
|
|
||||||
|
make |
||||||
|
make check |
||||||
|
make install |
||||||
|
|
||||||
|
Windows (Visual Studio) |
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^ |
||||||
|
Creating Visual Studio project files from the command line: |
||||||
|
|
||||||
|
.. parsed-literal:: |
||||||
|
|
||||||
|
<unpack> |
||||||
|
cd jansson-|release| |
||||||
|
|
||||||
|
md build |
||||||
|
cd build |
||||||
|
cmake -G "Visual Studio 10" .. |
||||||
|
|
||||||
|
You will now have a *Visual Studio Solution* in your build directory. |
||||||
|
To run the unit tests build the ``RUN_TESTS`` project. |
||||||
|
|
||||||
|
If you prefer a GUI the ``cmake`` line in the above example can |
||||||
|
be replaced with:: |
||||||
|
|
||||||
|
cmake-gui .. |
||||||
|
|
||||||
|
For command line help (including a list of available generators) |
||||||
|
for CMake_ simply run:: |
||||||
|
|
||||||
|
cmake |
||||||
|
|
||||||
|
To list available CMake_ settings (and what they are currently set to) |
||||||
|
for the project, run:: |
||||||
|
|
||||||
|
cmake -LH .. |
||||||
|
|
||||||
|
Mac OSX (Xcode) |
||||||
|
^^^^^^^^^^^^^^^ |
||||||
|
If you prefer using Xcode instead of make files on OSX, |
||||||
|
do the following. (Use the same steps as |
||||||
|
for :ref:`Unix <build-cmake-unix>`):: |
||||||
|
|
||||||
|
... |
||||||
|
cmake -G "Xcode" .. |
||||||
|
|
||||||
|
Additional CMake settings |
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||||
|
|
||||||
|
Shared library |
||||||
|
"""""""""""""" |
||||||
|
By default the CMake_ project will generate build files for building the |
||||||
|
static library. To build the shared version use:: |
||||||
|
|
||||||
|
... |
||||||
|
cmake -DBUILD_SHARED=1 .. |
||||||
|
|
||||||
|
Changing install directory (same as autoconf --prefix) |
||||||
|
"""""""""""""""""""""""""""""""""""""""""""""""""""""" |
||||||
|
Just as with the autoconf_ project you can change the destination directory |
||||||
|
for ``make install``. The equivalent for autoconfs ``./configure --prefix`` |
||||||
|
in CMake_ is:: |
||||||
|
|
||||||
|
... |
||||||
|
cmake -DCMAKE_INSTALL_PREFIX:PATH=/some/other/path .. |
||||||
|
make install |
||||||
|
|
||||||
|
.. _CMake: http://www.cmake.org |
||||||
|
|
||||||
|
Android |
||||||
|
------- |
||||||
|
|
||||||
|
Jansson can be built for Android platforms. Android.mk is in the |
||||||
|
source root directory. The configuration header file is located in the |
||||||
|
``android`` directory in the source distribution. |
||||||
|
|
||||||
|
|
||||||
|
Windows |
||||||
|
------- |
||||||
|
|
||||||
|
**This method is deprecated**. Using :ref:`CMake <build-cmake>` is now |
||||||
|
preferred. |
||||||
|
|
||||||
|
Jansson can be built with Visual Studio 2010 (and probably newer |
||||||
|
versions, too). The solution and project files are in the |
||||||
|
``win32/vs2010/`` directory in the source distribution. |
||||||
|
|
||||||
|
|
||||||
|
Other Systems |
||||||
|
------------- |
||||||
|
|
||||||
|
On non Unix-like systems, you may be unable to run the ``./configure`` |
||||||
|
script. In this case, follow these steps. All the files mentioned can |
||||||
|
be found in the ``src/`` directory. |
||||||
|
|
||||||
|
1. Create ``jansson_config.h`` (which has some platform-specific |
||||||
|
parameters that are normally filled in by the ``./configure`` |
||||||
|
script). Edit ``jansson_config.h.in``, replacing all ``@variable@`` |
||||||
|
placeholders, and rename the file to ``jansson_config.h``. |
||||||
|
|
||||||
|
2. Make ``jansson.h`` and ``jansson_config.h`` available to the |
||||||
|
compiler, so that they can be found when compiling programs that |
||||||
|
use Jansson. |
||||||
|
|
||||||
|
3. Compile all the ``.c`` files (in the ``src/`` directory) into a |
||||||
|
library file. Make the library available to the compiler, as in |
||||||
|
step 2. |
||||||
|
|
||||||
|
|
||||||
|
Building the Documentation |
||||||
|
-------------------------- |
||||||
|
|
||||||
|
(This subsection describes how to build the HTML documentation you are |
||||||
|
currently reading, so it can be safely skipped.) |
||||||
|
|
||||||
|
Documentation is in the ``doc/`` subdirectory. It's written in |
||||||
|
reStructuredText_ with Sphinx_ annotations. To generate the HTML |
||||||
|
documentation, invoke:: |
||||||
|
|
||||||
|
make html |
||||||
|
|
||||||
|
and point your browser to ``doc/_build/html/index.html``. Sphinx_ 1.0 |
||||||
|
or newer is required to generate the documentation. |
||||||
|
|
||||||
|
.. _reStructuredText: http://docutils.sourceforge.net/rst.html |
||||||
|
.. _Sphinx: http://sphinx.pocoo.org/ |
||||||
|
|
||||||
|
|
||||||
|
Compiling Programs that Use Jansson |
||||||
|
=================================== |
||||||
|
|
||||||
|
Jansson involves one C header file, :file:`jansson.h`, so it's enough |
||||||
|
to put the line |
||||||
|
|
||||||
|
:: |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
|
||||||
|
in the beginning of every source file that uses Jansson. |
||||||
|
|
||||||
|
There's also just one library to link with, ``libjansson``. Compile and |
||||||
|
link the program as follows:: |
||||||
|
|
||||||
|
cc -o prog prog.c -ljansson |
||||||
|
|
||||||
|
Starting from version 1.2, there's also support for pkg-config_:: |
||||||
|
|
||||||
|
cc -o prog prog.c `pkg-config --cflags --libs jansson` |
||||||
|
|
||||||
|
.. _pkg-config: http://pkg-config.freedesktop.org/ |
@ -0,0 +1,192 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
#include <curl/curl.h> |
||||||
|
|
||||||
|
#define BUFFER_SIZE (256 * 1024) /* 256 KB */ |
||||||
|
|
||||||
|
#define URL_FORMAT "https://api.github.com/repos/%s/%s/commits"
|
||||||
|
#define URL_SIZE 256 |
||||||
|
|
||||||
|
/* Return the offset of the first newline in text or the length of
|
||||||
|
text if there's no newline */ |
||||||
|
static int newline_offset(const char *text) |
||||||
|
{ |
||||||
|
const char *newline = strchr(text, '\n'); |
||||||
|
if(!newline) |
||||||
|
return strlen(text); |
||||||
|
else |
||||||
|
return (int)(newline - text); |
||||||
|
} |
||||||
|
|
||||||
|
struct write_result |
||||||
|
{ |
||||||
|
char *data; |
||||||
|
int pos; |
||||||
|
}; |
||||||
|
|
||||||
|
static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream) |
||||||
|
{ |
||||||
|
struct write_result *result = (struct write_result *)stream; |
||||||
|
|
||||||
|
if(result->pos + size * nmemb >= BUFFER_SIZE - 1) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: too small buffer\n"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
memcpy(result->data + result->pos, ptr, size * nmemb); |
||||||
|
result->pos += size * nmemb; |
||||||
|
|
||||||
|
return size * nmemb; |
||||||
|
} |
||||||
|
|
||||||
|
static char *request(const char *url) |
||||||
|
{ |
||||||
|
CURL *curl = NULL; |
||||||
|
CURLcode status; |
||||||
|
char *data = NULL; |
||||||
|
long code; |
||||||
|
|
||||||
|
curl_global_init(CURL_GLOBAL_ALL); |
||||||
|
curl = curl_easy_init(); |
||||||
|
if(!curl) |
||||||
|
goto error; |
||||||
|
|
||||||
|
data = malloc(BUFFER_SIZE); |
||||||
|
if(!data) |
||||||
|
goto error; |
||||||
|
|
||||||
|
struct write_result write_result = { |
||||||
|
.data = data, |
||||||
|
.pos = 0 |
||||||
|
}; |
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url); |
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response); |
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result); |
||||||
|
|
||||||
|
status = curl_easy_perform(curl); |
||||||
|
if(status != 0) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: unable to request data from %s:\n", url); |
||||||
|
fprintf(stderr, "%s\n", curl_easy_strerror(status)); |
||||||
|
goto error; |
||||||
|
} |
||||||
|
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); |
||||||
|
if(code != 200) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: server responded with code %ld\n", code); |
||||||
|
goto error; |
||||||
|
} |
||||||
|
|
||||||
|
curl_easy_cleanup(curl); |
||||||
|
curl_global_cleanup(); |
||||||
|
|
||||||
|
/* zero-terminate the result */ |
||||||
|
data[write_result.pos] = '\0'; |
||||||
|
|
||||||
|
return data; |
||||||
|
|
||||||
|
error: |
||||||
|
if(data) |
||||||
|
free(data); |
||||||
|
if(curl) |
||||||
|
curl_easy_cleanup(curl); |
||||||
|
curl_global_cleanup(); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char *argv[]) |
||||||
|
{ |
||||||
|
size_t i; |
||||||
|
char *text; |
||||||
|
char url[URL_SIZE]; |
||||||
|
|
||||||
|
json_t *root; |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
if(argc != 3) |
||||||
|
{ |
||||||
|
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]); |
||||||
|
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n"); |
||||||
|
return 2; |
||||||
|
} |
||||||
|
|
||||||
|
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]); |
||||||
|
|
||||||
|
text = request(url); |
||||||
|
if(!text) |
||||||
|
return 1; |
||||||
|
|
||||||
|
root = json_loads(text, 0, &error); |
||||||
|
free(text); |
||||||
|
|
||||||
|
if(!root) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
if(!json_is_array(root)) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: root is not an array\n"); |
||||||
|
json_decref(root); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
for(i = 0; i < json_array_size(root); i++) |
||||||
|
{ |
||||||
|
json_t *data, *sha, *commit, *message; |
||||||
|
const char *message_text; |
||||||
|
|
||||||
|
data = json_array_get(root, i); |
||||||
|
if(!json_is_object(data)) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: commit data %d is not an object\n", (int)(i + 1)); |
||||||
|
json_decref(root); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
sha = json_object_get(data, "sha"); |
||||||
|
if(!json_is_string(sha)) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: commit %d: sha is not a string\n", (int)(i + 1)); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
commit = json_object_get(data, "commit"); |
||||||
|
if(!json_is_object(commit)) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: commit %d: commit is not an object\n", (int)(i + 1)); |
||||||
|
json_decref(root); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
message = json_object_get(commit, "message"); |
||||||
|
if(!json_is_string(message)) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: commit %d: message is not a string\n", (int)(i + 1)); |
||||||
|
json_decref(root); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
message_text = json_string_value(message); |
||||||
|
printf("%.8s %.*s\n", |
||||||
|
json_string_value(sha), |
||||||
|
newline_offset(message_text), |
||||||
|
message_text); |
||||||
|
} |
||||||
|
|
||||||
|
json_decref(root); |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
Jansson Documentation |
||||||
|
===================== |
||||||
|
|
||||||
|
This is the documentation for Jansson_ |release|, last updated |today|. |
||||||
|
|
||||||
|
Introduction |
||||||
|
------------ |
||||||
|
|
||||||
|
Jansson_ is a C library for encoding, decoding and manipulating JSON |
||||||
|
data. Its main features and design principles are: |
||||||
|
|
||||||
|
- Simple and intuitive API and data model |
||||||
|
|
||||||
|
- Comprehensive documentation |
||||||
|
|
||||||
|
- No dependencies on other libraries |
||||||
|
|
||||||
|
- Full Unicode support (UTF-8) |
||||||
|
|
||||||
|
- Extensive test suite |
||||||
|
|
||||||
|
Jansson is licensed under the `MIT license`_; see LICENSE in the |
||||||
|
source distribution for details. |
||||||
|
|
||||||
|
Jansson is used in production and its API is stable. It works on |
||||||
|
numerous platforms, including numerous Unix like systems and Windows. |
||||||
|
It's suitable for use on any system, including desktop, server, and |
||||||
|
small embedded systems. |
||||||
|
|
||||||
|
|
||||||
|
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php |
||||||
|
.. _Jansson: http://www.digip.org/jansson/ |
||||||
|
|
||||||
|
Contents |
||||||
|
-------- |
||||||
|
|
||||||
|
.. toctree:: |
||||||
|
:maxdepth: 2 |
||||||
|
|
||||||
|
gettingstarted |
||||||
|
upgrading |
||||||
|
tutorial |
||||||
|
conformance |
||||||
|
portability |
||||||
|
apiref |
||||||
|
changes |
||||||
|
|
||||||
|
|
||||||
|
Indices and Tables |
||||||
|
================== |
||||||
|
|
||||||
|
* :ref:`genindex` |
||||||
|
* :ref:`search` |
@ -0,0 +1,52 @@ |
|||||||
|
*********** |
||||||
|
Portability |
||||||
|
*********** |
||||||
|
|
||||||
|
Thread safety |
||||||
|
------------- |
||||||
|
|
||||||
|
Jansson is thread safe and has no mutable global state. The only |
||||||
|
exception are the memory allocation functions, that should be set at |
||||||
|
most once, and only on program startup. See |
||||||
|
:ref:`apiref-custom-memory-allocation`. |
||||||
|
|
||||||
|
There's no locking performed inside Jansson's code, so a multithreaded |
||||||
|
program must perform its own locking if JSON values are shared by |
||||||
|
multiple threads. Jansson's reference counting semantics may make this |
||||||
|
a bit harder than it seems, as it's possible to have a reference to a |
||||||
|
value that's also stored inside a list or object. Modifying the |
||||||
|
container (adding or removing values) may trigger concurrent access to |
||||||
|
such values, as containers manage the reference count of their |
||||||
|
contained values. Bugs involving concurrent incrementing or |
||||||
|
decrementing of deference counts may be hard to track. |
||||||
|
|
||||||
|
The encoding functions (:func:`json_dumps()` and friends) track |
||||||
|
reference loops by modifying the internal state of objects and arrays. |
||||||
|
For this reason, encoding functions must not be run on the same JSON |
||||||
|
values in two separate threads at the same time. As already noted |
||||||
|
above, be especially careful if two arrays or objects share their |
||||||
|
contained values with another array or object. |
||||||
|
|
||||||
|
If you want to make sure that two JSON value hierarchies do not |
||||||
|
contain shared values, use :func:`json_deep_copy()` to make copies. |
||||||
|
|
||||||
|
Locale |
||||||
|
------ |
||||||
|
|
||||||
|
Jansson works fine under any locale. |
||||||
|
|
||||||
|
However, if the host program is multithreaded and uses ``setlocale()`` |
||||||
|
to switch the locale in one thread while Jansson is currently encoding |
||||||
|
or decoding JSON data in another thread, the result may be wrong or |
||||||
|
the program may even crash. |
||||||
|
|
||||||
|
Jansson uses locale specific functions for certain string conversions |
||||||
|
in the encoder and decoder, and then converts the locale specific |
||||||
|
values to/from the JSON representation. This fails if the locale |
||||||
|
changes between the string conversion and the locale-to-JSON |
||||||
|
conversion. This can only happen in multithreaded programs that use |
||||||
|
``setlocale()``, because ``setlocale()`` switches the locale for all |
||||||
|
running threads, not only the thread that calls ``setlocale()``. |
||||||
|
|
||||||
|
If your program uses ``setlocale()`` as described above, consider |
||||||
|
using the thread-safe ``uselocale()`` instead. |
@ -0,0 +1,286 @@ |
|||||||
|
.. _tutorial: |
||||||
|
|
||||||
|
******** |
||||||
|
Tutorial |
||||||
|
******** |
||||||
|
|
||||||
|
.. highlight:: c |
||||||
|
|
||||||
|
In this tutorial, we create a program that fetches the latest commits |
||||||
|
of a repository in GitHub_ over the web. `GitHub API`_ uses JSON, so |
||||||
|
the result can be parsed using Jansson. |
||||||
|
|
||||||
|
To stick to the the scope of this tutorial, we will only cover the the |
||||||
|
parts of the program related to handling JSON data. For the best user |
||||||
|
experience, the full source code is available: |
||||||
|
:download:`github_commits.c`. To compile it (on Unix-like systems with |
||||||
|
gcc), use the following command:: |
||||||
|
|
||||||
|
gcc -o github_commits github_commits.c -ljansson -lcurl |
||||||
|
|
||||||
|
libcurl_ is used to communicate over the web, so it is required to |
||||||
|
compile the program. |
||||||
|
|
||||||
|
The command line syntax is:: |
||||||
|
|
||||||
|
github_commits USER REPOSITORY |
||||||
|
|
||||||
|
``USER`` is a GitHub user ID and ``REPOSITORY`` is the repository |
||||||
|
name. Please note that the GitHub API is rate limited, so if you run |
||||||
|
the program too many times within a short period of time, the sever |
||||||
|
starts to respond with an error. |
||||||
|
|
||||||
|
.. _GitHub: https://github.com/ |
||||||
|
.. _GitHub API: http://developer.github.com/ |
||||||
|
.. _libcurl: http://curl.haxx.se/ |
||||||
|
|
||||||
|
|
||||||
|
.. _tutorial-github-commits-api: |
||||||
|
|
||||||
|
The GitHub Repo Commits API |
||||||
|
=========================== |
||||||
|
|
||||||
|
The `GitHub Repo Commits API`_ is used by sending HTTP requests to |
||||||
|
URLs like ``https://api.github.com/repos/USER/REPOSITORY/commits``, |
||||||
|
where ``USER`` and ``REPOSITORY`` are the GitHub user ID and the name |
||||||
|
of the repository whose commits are to be listed, respectively. |
||||||
|
|
||||||
|
GitHub responds with a JSON array of the following form: |
||||||
|
|
||||||
|
.. code-block:: none |
||||||
|
|
||||||
|
[ |
||||||
|
{ |
||||||
|
"sha": "<the commit ID>", |
||||||
|
"commit": { |
||||||
|
"message": "<the commit message>", |
||||||
|
<more fields, not important to this tutorial...> |
||||||
|
}, |
||||||
|
<more fields...> |
||||||
|
}, |
||||||
|
{ |
||||||
|
"sha": "<the commit ID>", |
||||||
|
"commit": { |
||||||
|
"message": "<the commit message>", |
||||||
|
<more fields...> |
||||||
|
}, |
||||||
|
<more fields...> |
||||||
|
}, |
||||||
|
<more commits...> |
||||||
|
] |
||||||
|
|
||||||
|
In our program, the HTTP request is sent using the following |
||||||
|
function:: |
||||||
|
|
||||||
|
static char *request(const char *url); |
||||||
|
|
||||||
|
It takes the URL as a parameter, preforms a HTTP GET request, and |
||||||
|
returns a newly allocated string that contains the response body. If |
||||||
|
the request fails, an error message is printed to stderr and the |
||||||
|
return value is *NULL*. For full details, refer to :download:`the code |
||||||
|
<github_commits.c>`, as the actual implementation is not important |
||||||
|
here. |
||||||
|
|
||||||
|
.. _GitHub Repo Commits API: http://developer.github.com/v3/repos/commits/ |
||||||
|
|
||||||
|
.. _tutorial-the-program: |
||||||
|
|
||||||
|
The Program |
||||||
|
=========== |
||||||
|
|
||||||
|
First the includes:: |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
#include <jansson.h> |
||||||
|
|
||||||
|
Like all the programs using Jansson, we need to include |
||||||
|
:file:`jansson.h`. |
||||||
|
|
||||||
|
The following definitions are used to build the GitHub API request |
||||||
|
URL:: |
||||||
|
|
||||||
|
#define URL_FORMAT "https://api.github.com/repos/%s/%s/commits" |
||||||
|
#define URL_SIZE 256 |
||||||
|
|
||||||
|
The following function is used when formatting the result to find the |
||||||
|
first newline in the commit message:: |
||||||
|
|
||||||
|
/* Return the offset of the first newline in text or the length of |
||||||
|
text if there's no newline */ |
||||||
|
static int newline_offset(const char *text) |
||||||
|
{ |
||||||
|
const char *newline = strchr(text, '\n'); |
||||||
|
if(!newline) |
||||||
|
return strlen(text); |
||||||
|
else |
||||||
|
return (int)(newline - text); |
||||||
|
} |
||||||
|
|
||||||
|
The main function follows. In the beginning, we first declare a bunch |
||||||
|
of variables and check the command line parameters:: |
||||||
|
|
||||||
|
int main(int argc, char *argv[]) |
||||||
|
{ |
||||||
|
size_t i; |
||||||
|
char *text; |
||||||
|
char url[URL_SIZE]; |
||||||
|
|
||||||
|
json_t *root; |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
if(argc != 3) |
||||||
|
{ |
||||||
|
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]); |
||||||
|
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n"); |
||||||
|
return 2; |
||||||
|
} |
||||||
|
|
||||||
|
Then we build the request URL using the user and repository names |
||||||
|
given as command line parameters:: |
||||||
|
|
||||||
|
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]); |
||||||
|
|
||||||
|
This uses the ``URL_SIZE`` and ``URL_FORMAT`` constants defined above. |
||||||
|
Now we're ready to actually request the JSON data over the web:: |
||||||
|
|
||||||
|
text = request(url); |
||||||
|
if(!text) |
||||||
|
return 1; |
||||||
|
|
||||||
|
If an error occurs, our function ``request`` prints the error and |
||||||
|
returns *NULL*, so it's enough to just return 1 from the main |
||||||
|
function. |
||||||
|
|
||||||
|
Next we'll call :func:`json_loads()` to decode the JSON text we got |
||||||
|
as a response:: |
||||||
|
|
||||||
|
root = json_loads(text, 0, &error); |
||||||
|
free(text); |
||||||
|
|
||||||
|
if(!root) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
We don't need the JSON text anymore, so we can free the ``text`` |
||||||
|
variable right after decoding it. If :func:`json_loads()` fails, it |
||||||
|
returns *NULL* and sets error information to the :type:`json_error_t` |
||||||
|
structure given as the second parameter. In this case, our program |
||||||
|
prints the error information out and returns 1 from the main function. |
||||||
|
|
||||||
|
Now we're ready to extract the data out of the decoded JSON response. |
||||||
|
The structure of the response JSON was explained in section |
||||||
|
:ref:`tutorial-github-commits-api`. |
||||||
|
|
||||||
|
We check that the returned value really is an array:: |
||||||
|
|
||||||
|
if(!json_is_array(root)) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: root is not an array\n"); |
||||||
|
json_decref(root); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
Then we proceed to loop over all the commits in the array:: |
||||||
|
|
||||||
|
for(i = 0; i < json_array_size(root); i++) |
||||||
|
{ |
||||||
|
json_t *data, *sha, *commit, *message; |
||||||
|
const char *message_text; |
||||||
|
|
||||||
|
data = json_array_get(root, i); |
||||||
|
if(!json_is_object(data)) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: commit data %d is not an object\n", i + 1); |
||||||
|
json_decref(root); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
... |
||||||
|
|
||||||
|
The function :func:`json_array_size()` returns the size of a JSON |
||||||
|
array. First, we again declare some variables and then extract the |
||||||
|
i'th element of the ``root`` array using :func:`json_array_get()`. |
||||||
|
We also check that the resulting value is a JSON object. |
||||||
|
|
||||||
|
Next we'll extract the commit ID (a hexadecimal SHA-1 sum), |
||||||
|
intermediate commit info object, and the commit message from that |
||||||
|
object. We also do proper type checks:: |
||||||
|
|
||||||
|
sha = json_object_get(data, "sha"); |
||||||
|
if(!json_is_string(sha)) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: commit %d: sha is not a string\n", i + 1); |
||||||
|
json_decref(root); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
commit = json_object_get(data, "commit"); |
||||||
|
if(!json_is_object(commit)) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: commit %d: commit is not an object\n", i + 1); |
||||||
|
json_decref(root); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
message = json_object_get(commit, "message"); |
||||||
|
if(!json_is_string(message)) |
||||||
|
{ |
||||||
|
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1); |
||||||
|
json_decref(root); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
... |
||||||
|
|
||||||
|
And finally, we'll print the first 8 characters of the commit ID and |
||||||
|
the first line of the commit message. A C-style string is extracted |
||||||
|
from a JSON string using :func:`json_string_value()`:: |
||||||
|
|
||||||
|
message_text = json_string_value(message); |
||||||
|
printf("%.8s %.*s\n", |
||||||
|
json_string_value(id), |
||||||
|
newline_offset(message_text), |
||||||
|
message_text); |
||||||
|
} |
||||||
|
|
||||||
|
After sending the HTTP request, we decoded the JSON text using |
||||||
|
:func:`json_loads()`, remember? It returns a *new reference* to the |
||||||
|
JSON value it decodes. When we're finished with the value, we'll need |
||||||
|
to decrease the reference count using :func:`json_decref()`. This way |
||||||
|
Jansson can release the resources:: |
||||||
|
|
||||||
|
json_decref(root); |
||||||
|
return 0; |
||||||
|
|
||||||
|
For a detailed explanation of reference counting in Jansson, see |
||||||
|
:ref:`apiref-reference-count` in :ref:`apiref`. |
||||||
|
|
||||||
|
The program's ready, let's test it and view the latest commits in |
||||||
|
Jansson's repository:: |
||||||
|
|
||||||
|
$ ./github_commits akheron jansson |
||||||
|
1581f26a Merge branch '2.3' |
||||||
|
aabfd493 load: Change buffer_pos to be a size_t |
||||||
|
bd72efbd load: Avoid unexpected behaviour in macro expansion |
||||||
|
e8fd3e30 Document and tweak json_load_callback() |
||||||
|
873eddaf Merge pull request #60 from rogerz/contrib |
||||||
|
bd2c0c73 Ignore the binary test_load_callback |
||||||
|
17a51a4b Merge branch '2.3' |
||||||
|
09c39adc Add json_load_callback to the list of exported symbols |
||||||
|
cbb80baf Merge pull request #57 from rogerz/contrib |
||||||
|
040bd7b0 Add json_load_callback() |
||||||
|
2637faa4 Make test stripping locale independent |
||||||
|
<...> |
||||||
|
|
||||||
|
|
||||||
|
Conclusion |
||||||
|
========== |
||||||
|
|
||||||
|
In this tutorial, we implemented a program that fetches the latest |
||||||
|
commits of a GitHub repository using the GitHub Repo Commits API. |
||||||
|
Jansson was used to decode the JSON response and to extract the commit |
||||||
|
data. |
||||||
|
|
||||||
|
This tutorial only covered a small part of Jansson. For example, we |
||||||
|
did not create or manipulate JSON values at all. Proceed to |
||||||
|
:ref:`apiref` to explore all features of Jansson. |
@ -0,0 +1,76 @@ |
|||||||
|
.. highlight:: c |
||||||
|
|
||||||
|
****************** |
||||||
|
Upgrading from 1.x |
||||||
|
****************** |
||||||
|
|
||||||
|
This chapter lists the backwards incompatible changes introduced in |
||||||
|
Jansson 2.0, and the steps that are needed for upgrading your code. |
||||||
|
|
||||||
|
**The incompatibilities are not dramatic.** The biggest change is that |
||||||
|
all decoding functions now require and extra parameter. Most programs |
||||||
|
can be modified to work with 2.0 by adding a ``0`` as the second |
||||||
|
parameter to all calls of :func:`json_loads()`, :func:`json_loadf()` |
||||||
|
and :func:`json_load_file()`. |
||||||
|
|
||||||
|
|
||||||
|
Compatibility |
||||||
|
============= |
||||||
|
|
||||||
|
Jansson 2.0 is backwards incompatible with the Jansson 1.x releases. |
||||||
|
It is ABI incompatible, i.e. all programs dynamically linking to the |
||||||
|
Jansson library need to be recompiled. It's also API incompatible, |
||||||
|
i.e. the source code of programs using Jansson 1.x may need |
||||||
|
modifications to make them compile against Jansson 2.0. |
||||||
|
|
||||||
|
All the 2.x releases are guaranteed to be backwards compatible for |
||||||
|
both ABI and API, so no recompilation or source changes are needed |
||||||
|
when upgrading from 2.x to 2.y. |
||||||
|
|
||||||
|
|
||||||
|
List of Incompatible Changes |
||||||
|
============================ |
||||||
|
|
||||||
|
**Decoding flags** |
||||||
|
For future needs, a ``flags`` parameter was added as the second |
||||||
|
parameter to all decoding functions, i.e. :func:`json_loads()`, |
||||||
|
:func:`json_loadf()` and :func:`json_load_file()`. All calls to |
||||||
|
these functions need to be changed by adding a ``0`` as the second |
||||||
|
argument. For example:: |
||||||
|
|
||||||
|
/* old code */ |
||||||
|
json_loads(input, &error); |
||||||
|
|
||||||
|
/* new code */ |
||||||
|
json_loads(input, 0, &error); |
||||||
|
|
||||||
|
|
||||||
|
**Underlying type of JSON integers** |
||||||
|
The underlying C type of JSON integers has been changed from |
||||||
|
:type:`int` to the widest available signed integer type, i.e. |
||||||
|
:type:`long long` or :type:`long`, depending on whether |
||||||
|
:type:`long long` is supported on your system or not. This makes |
||||||
|
the whole 64-bit integer range available on most modern systems. |
||||||
|
|
||||||
|
``jansson.h`` has a typedef :type:`json_int_t` to the underlying |
||||||
|
integer type. :type:`int` should still be used in most cases when |
||||||
|
dealing with smallish JSON integers, as the compiler handles |
||||||
|
implicit type coercion. Only when the full 64-bit range is needed, |
||||||
|
:type:`json_int_t` should be explicitly used. |
||||||
|
|
||||||
|
|
||||||
|
**Maximum encoder indentation depth** |
||||||
|
The maximum argument of the ``JSON_INDENT()`` macro has been |
||||||
|
changed from 255 to 31, to free up bits from the ``flags`` |
||||||
|
parameter of :func:`json_dumps()`, :func:`json_dumpf()` and |
||||||
|
:func:`json_dump_file()`. If your code uses a bigger indentation |
||||||
|
than 31, it needs to be changed. |
||||||
|
|
||||||
|
|
||||||
|
**Unsigned integers in API functions** |
||||||
|
Version 2.0 unifies unsigned integer usage in the API. All uses of |
||||||
|
:type:`unsigned int` and :type:`unsigned long` have been replaced |
||||||
|
with :type:`size_t`. This includes flags, container sizes, etc. |
||||||
|
This should not require source code changes, as both |
||||||
|
:type:`unsigned int` and :type:`unsigned long` are usually |
||||||
|
compatible with :type:`size_t`. |
@ -0,0 +1,10 @@ |
|||||||
|
prefix=@prefix@ |
||||||
|
exec_prefix=@exec_prefix@ |
||||||
|
libdir=@libdir@ |
||||||
|
includedir=${prefix}/include |
||||||
|
|
||||||
|
Name: Jansson |
||||||
|
Description: Library for encoding, decoding and manipulating JSON data |
||||||
|
Version: @VERSION@ |
||||||
|
Libs: -L${libdir} -ljansson |
||||||
|
Cflags: -I${includedir} |
@ -0,0 +1,24 @@ |
|||||||
|
EXTRA_DIST = jansson.def |
||||||
|
|
||||||
|
include_HEADERS = jansson.h jansson_config.h |
||||||
|
|
||||||
|
lib_LTLIBRARIES = libjansson.la |
||||||
|
libjansson_la_SOURCES = \
|
||||||
|
dump.c \
|
||||||
|
error.c \
|
||||||
|
hashtable.c \
|
||||||
|
hashtable.h \
|
||||||
|
jansson_private.h \
|
||||||
|
load.c \
|
||||||
|
memory.c \
|
||||||
|
pack_unpack.c \
|
||||||
|
strbuffer.c \
|
||||||
|
strbuffer.h \
|
||||||
|
strconv.c \
|
||||||
|
utf.c \
|
||||||
|
utf.h \
|
||||||
|
value.c |
||||||
|
libjansson_la_LDFLAGS = \
|
||||||
|
-no-undefined \
|
||||||
|
-export-symbols-regex '^json_' \
|
||||||
|
-version-info 9:0:5 |
@ -1,5 +1,5 @@ |
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> |
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
* |
* |
||||||
* This library is free software; you can redistribute it and/or modify |
* This library is free software; you can redistribute it and/or modify |
||||||
* it under the terms of the MIT license. See LICENSE for details. |
* it under the terms of the MIT license. See LICENSE for details. |
@ -1,5 +1,3 @@ |
|||||||
LIBRARY "jansson" |
|
||||||
|
|
||||||
EXPORTS |
EXPORTS |
||||||
json_delete |
json_delete |
||||||
json_true |
json_true |
@ -1,5 +1,5 @@ |
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2010-2012 Petri Lehtinen <petri@digip.org> |
* Copyright (c) 2010-2013 Petri Lehtinen <petri@digip.org> |
||||||
* |
* |
||||||
* Jansson is free software; you can redistribute it and/or modify |
* Jansson is free software; you can redistribute it and/or modify |
||||||
* it under the terms of the MIT license. See LICENSE for details. |
* it under the terms of the MIT license. See LICENSE for details. |
@ -1,5 +1,5 @@ |
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> |
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
* |
* |
||||||
* Jansson is free software; you can redistribute it and/or modify |
* Jansson is free software; you can redistribute it and/or modify |
||||||
* it under the terms of the MIT license. See LICENSE for details. |
* it under the terms of the MIT license. See LICENSE for details. |
@ -1,5 +1,5 @@ |
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> |
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
* |
* |
||||||
* Jansson is free software; you can redistribute it and/or modify |
* Jansson is free software; you can redistribute it and/or modify |
||||||
* it under the terms of the MIT license. See LICENSE for details. |
* it under the terms of the MIT license. See LICENSE for details. |
@ -0,0 +1,10 @@ |
|||||||
|
SUBDIRS = bin suites |
||||||
|
EXTRA_DIST = scripts run-suites |
||||||
|
|
||||||
|
TESTS = run-suites |
||||||
|
TESTS_ENVIRONMENT = \
|
||||||
|
top_srcdir=$(top_srcdir) \
|
||||||
|
top_builddir=$(top_builddir) |
||||||
|
|
||||||
|
clean-local: |
||||||
|
rm -rf logs |
@ -0,0 +1,5 @@ |
|||||||
|
check_PROGRAMS = json_process |
||||||
|
|
||||||
|
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src |
||||||
|
LDFLAGS = -static # for speed and Valgrind |
||||||
|
LDADD = $(top_builddir)/src/libjansson.la |
@ -0,0 +1,349 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H |
||||||
|
#include <config.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <ctype.h> |
||||||
|
#include <jansson.h> |
||||||
|
|
||||||
|
#ifdef HAVE_LOCALE_H |
||||||
|
#include <locale.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#if _WIN32 |
||||||
|
#include <io.h> /* for _setmode() */ |
||||||
|
#include <fcntl.h> /* for _O_BINARY */ |
||||||
|
|
||||||
|
static const char dir_sep = '\\'; |
||||||
|
#else |
||||||
|
static const char dir_sep = '/'; |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
struct config { |
||||||
|
int indent; |
||||||
|
int compact; |
||||||
|
int preserve_order; |
||||||
|
int ensure_ascii; |
||||||
|
int sort_keys; |
||||||
|
int strip; |
||||||
|
int use_env; |
||||||
|
} conf; |
||||||
|
|
||||||
|
#define l_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t') |
||||||
|
|
||||||
|
/* Return a pointer to the first non-whitespace character of str.
|
||||||
|
Modifies str so that all trailing whitespace characters are |
||||||
|
replaced by '\0'. */ |
||||||
|
static const char *strip(char *str) |
||||||
|
{ |
||||||
|
size_t length; |
||||||
|
char *result = str; |
||||||
|
while (*result && l_isspace(*result)) |
||||||
|
result++; |
||||||
|
|
||||||
|
length = strlen(result); |
||||||
|
if (length == 0) |
||||||
|
return result; |
||||||
|
|
||||||
|
while (l_isspace(result[length - 1])) |
||||||
|
result[--length] = '\0'; |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static char *loadfile(FILE *file) |
||||||
|
{ |
||||||
|
long fsize, ret; |
||||||
|
char *buf; |
||||||
|
|
||||||
|
fseek(file, 0, SEEK_END); |
||||||
|
fsize = ftell(file); |
||||||
|
fseek(file, 0, SEEK_SET); |
||||||
|
|
||||||
|
buf = malloc(fsize+1); |
||||||
|
ret = fread(buf, 1, fsize, file); |
||||||
|
if (ret != fsize) |
||||||
|
exit(1); |
||||||
|
buf[fsize] = '\0'; |
||||||
|
|
||||||
|
return buf; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static void read_conf(FILE *conffile) |
||||||
|
{ |
||||||
|
char *buffer, *line, *val; |
||||||
|
|
||||||
|
buffer = loadfile(conffile); |
||||||
|
line = strtok(buffer, "\r\n"); |
||||||
|
while (line) { |
||||||
|
val = strchr(line, '='); |
||||||
|
if (!val) { |
||||||
|
printf("invalid configuration line\n"); |
||||||
|
break; |
||||||
|
} |
||||||
|
*val++ = '\0'; |
||||||
|
|
||||||
|
if (!strcmp(line, "JSON_INDENT")) |
||||||
|
conf.indent = atoi(val); |
||||||
|
if (!strcmp(line, "JSON_COMPACT")) |
||||||
|
conf.compact = atoi(val); |
||||||
|
if (!strcmp(line, "JSON_ENSURE_ASCII")) |
||||||
|
conf.ensure_ascii = atoi(val); |
||||||
|
if (!strcmp(line, "JSON_PRESERVE_ORDER")) |
||||||
|
conf.preserve_order = atoi(val); |
||||||
|
if (!strcmp(line, "JSON_SORT_KEYS")) |
||||||
|
conf.sort_keys = atoi(val); |
||||||
|
if (!strcmp(line, "STRIP")) |
||||||
|
conf.strip = atoi(val); |
||||||
|
|
||||||
|
line = strtok(NULL, "\r\n"); |
||||||
|
} |
||||||
|
|
||||||
|
free(buffer); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static int cmpfile(const char *str, const char *path, const char *fname) |
||||||
|
{ |
||||||
|
char filename[1024], *buffer; |
||||||
|
int ret; |
||||||
|
FILE *file; |
||||||
|
|
||||||
|
sprintf(filename, "%s%c%s", path, dir_sep, fname); |
||||||
|
file = fopen(filename, "rb"); |
||||||
|
if (!file) { |
||||||
|
if (conf.strip) |
||||||
|
strcat(filename, ".strip"); |
||||||
|
else |
||||||
|
strcat(filename, ".normal"); |
||||||
|
file = fopen(filename, "rb"); |
||||||
|
} |
||||||
|
if (!file) { |
||||||
|
printf("Error: test result file could not be opened.\n"); |
||||||
|
exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
buffer = loadfile(file); |
||||||
|
if (strcmp(buffer, str) != 0) |
||||||
|
ret = 1; |
||||||
|
else |
||||||
|
ret = 0; |
||||||
|
free(buffer); |
||||||
|
fclose(file); |
||||||
|
|
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
int use_conf(char *test_path) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
size_t flags = 0; |
||||||
|
char filename[1024], errstr[1024]; |
||||||
|
char *buffer; |
||||||
|
FILE *infile, *conffile; |
||||||
|
json_t *json; |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
sprintf(filename, "%s%cinput", test_path, dir_sep); |
||||||
|
if (!(infile = fopen(filename, "rb"))) { |
||||||
|
fprintf(stderr, "Could not open \"%s\"\n", filename); |
||||||
|
return 2; |
||||||
|
} |
||||||
|
|
||||||
|
sprintf(filename, "%s%cenv", test_path, dir_sep); |
||||||
|
conffile = fopen(filename, "rb"); |
||||||
|
if (conffile) { |
||||||
|
read_conf(conffile); |
||||||
|
fclose(conffile); |
||||||
|
} |
||||||
|
|
||||||
|
if (conf.indent < 0 || conf.indent > 255) { |
||||||
|
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", conf.indent); |
||||||
|
return 2; |
||||||
|
} |
||||||
|
|
||||||
|
if (conf.indent) |
||||||
|
flags |= JSON_INDENT(conf.indent); |
||||||
|
|
||||||
|
if (conf.compact) |
||||||
|
flags |= JSON_COMPACT; |
||||||
|
|
||||||
|
if (conf.ensure_ascii) |
||||||
|
flags |= JSON_ENSURE_ASCII; |
||||||
|
|
||||||
|
if (conf.preserve_order) |
||||||
|
flags |= JSON_PRESERVE_ORDER; |
||||||
|
|
||||||
|
if (conf.sort_keys) |
||||||
|
flags |= JSON_SORT_KEYS; |
||||||
|
|
||||||
|
if (conf.strip) { |
||||||
|
/* Load to memory, strip leading and trailing whitespace */ |
||||||
|
buffer = loadfile(infile); |
||||||
|
json = json_loads(strip(buffer), 0, &error); |
||||||
|
free(buffer); |
||||||
|
} |
||||||
|
else |
||||||
|
json = json_loadf(infile, 0, &error); |
||||||
|
|
||||||
|
fclose(infile); |
||||||
|
|
||||||
|
if (!json) { |
||||||
|
sprintf(errstr, "%d %d %d\n%s\n", |
||||||
|
error.line, error.column, error.position, |
||||||
|
error.text); |
||||||
|
|
||||||
|
ret = cmpfile(errstr, test_path, "error"); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
buffer = json_dumps(json, flags); |
||||||
|
ret = cmpfile(buffer, test_path, "output"); |
||||||
|
free(buffer); |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
static int getenv_int(const char *name) |
||||||
|
{ |
||||||
|
char *value, *end; |
||||||
|
long result; |
||||||
|
|
||||||
|
value = getenv(name); |
||||||
|
if(!value) |
||||||
|
return 0; |
||||||
|
|
||||||
|
result = strtol(value, &end, 10); |
||||||
|
if(*end != '\0') |
||||||
|
return 0; |
||||||
|
|
||||||
|
return (int)result; |
||||||
|
} |
||||||
|
|
||||||
|
int use_env() |
||||||
|
{ |
||||||
|
int indent; |
||||||
|
size_t flags = 0; |
||||||
|
json_t *json; |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
#ifdef _WIN32 |
||||||
|
/* On Windows, set stdout and stderr to binary mode to avoid
|
||||||
|
outputting DOS line terminators */ |
||||||
|
_setmode(_fileno(stdout), _O_BINARY); |
||||||
|
_setmode(_fileno(stderr), _O_BINARY); |
||||||
|
#endif |
||||||
|
|
||||||
|
indent = getenv_int("JSON_INDENT"); |
||||||
|
if(indent < 0 || indent > 255) { |
||||||
|
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent); |
||||||
|
return 2; |
||||||
|
} |
||||||
|
|
||||||
|
if(indent > 0) |
||||||
|
flags |= JSON_INDENT(indent); |
||||||
|
|
||||||
|
if(getenv_int("JSON_COMPACT") > 0) |
||||||
|
flags |= JSON_COMPACT; |
||||||
|
|
||||||
|
if(getenv_int("JSON_ENSURE_ASCII")) |
||||||
|
flags |= JSON_ENSURE_ASCII; |
||||||
|
|
||||||
|
if(getenv_int("JSON_PRESERVE_ORDER")) |
||||||
|
flags |= JSON_PRESERVE_ORDER; |
||||||
|
|
||||||
|
if(getenv_int("JSON_SORT_KEYS")) |
||||||
|
flags |= JSON_SORT_KEYS; |
||||||
|
|
||||||
|
if(getenv_int("STRIP")) { |
||||||
|
/* Load to memory, strip leading and trailing whitespace */ |
||||||
|
size_t size = 0, used = 0; |
||||||
|
char *buffer = NULL; |
||||||
|
|
||||||
|
while(1) { |
||||||
|
size_t count; |
||||||
|
|
||||||
|
size = (size == 0 ? 128 : size * 2); |
||||||
|
buffer = realloc(buffer, size); |
||||||
|
if(!buffer) { |
||||||
|
fprintf(stderr, "Unable to allocate %d bytes\n", (int)size); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
count = fread(buffer + used, 1, size - used, stdin); |
||||||
|
if(count < size - used) { |
||||||
|
buffer[used + count] = '\0'; |
||||||
|
break; |
||||||
|
} |
||||||
|
used += count; |
||||||
|
} |
||||||
|
|
||||||
|
json = json_loads(strip(buffer), 0, &error); |
||||||
|
free(buffer); |
||||||
|
} |
||||||
|
else |
||||||
|
json = json_loadf(stdin, 0, &error); |
||||||
|
|
||||||
|
if(!json) { |
||||||
|
fprintf(stderr, "%d %d %d\n%s\n", |
||||||
|
error.line, error.column, |
||||||
|
error.position, error.text); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
json_dumpf(json, stdout, flags); |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char *argv[]) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
char *test_path = NULL; |
||||||
|
|
||||||
|
#ifdef HAVE_SETLOCALE |
||||||
|
setlocale(LC_ALL, ""); |
||||||
|
#endif |
||||||
|
|
||||||
|
if (argc < 2) { |
||||||
|
goto usage; |
||||||
|
} |
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) { |
||||||
|
if (!strcmp(argv[i], "--strip")) |
||||||
|
conf.strip = 1; |
||||||
|
else if (!strcmp(argv[i], "--env")) |
||||||
|
conf.use_env = 1; |
||||||
|
else |
||||||
|
test_path = argv[i]; |
||||||
|
} |
||||||
|
|
||||||
|
if (conf.use_env) |
||||||
|
return use_env(); |
||||||
|
else |
||||||
|
{ |
||||||
|
if (!test_path) |
||||||
|
goto usage; |
||||||
|
|
||||||
|
return use_conf(test_path); |
||||||
|
} |
||||||
|
|
||||||
|
usage: |
||||||
|
fprintf(stderr, "argc =%d\n", argc); |
||||||
|
fprintf(stderr, "usage: %s [--strip] [--env] test_dir\n", argv[0]); |
||||||
|
return 2; |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
while [ -n "$1" ]; do |
||||||
|
suite=$1 |
||||||
|
if [ -x $top_srcdir/test/suites/$suite/run ]; then |
||||||
|
SUITES="$SUITES $suite" |
||||||
|
else |
||||||
|
echo "No such suite: $suite" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
shift |
||||||
|
done |
||||||
|
|
||||||
|
if [ -z "$SUITES" ]; then |
||||||
|
suitedirs=$top_srcdir/test/suites/* |
||||||
|
for suitedir in $suitedirs; do |
||||||
|
if [ -d $suitedir ]; then |
||||||
|
SUITES="$SUITES `basename $suitedir`" |
||||||
|
fi |
||||||
|
done |
||||||
|
fi |
||||||
|
|
||||||
|
[ -z "$STOP" ] && STOP=0 |
||||||
|
|
||||||
|
suites_srcdir=$top_srcdir/test/suites |
||||||
|
suites_builddir=suites |
||||||
|
scriptdir=$top_srcdir/test/scripts |
||||||
|
logdir=logs |
||||||
|
bindir=bin |
||||||
|
export suites_srcdir suites_builddir scriptdir logdir bindir |
||||||
|
|
||||||
|
passed=0 |
||||||
|
failed=0 |
||||||
|
for suite in $SUITES; do |
||||||
|
echo "Suite: $suite" |
||||||
|
if $suites_srcdir/$suite/run $suite; then |
||||||
|
passed=$(($passed+1)) |
||||||
|
else |
||||||
|
failed=$(($failed+1)) |
||||||
|
[ $STOP -eq 1 ] && break |
||||||
|
fi |
||||||
|
done |
||||||
|
|
||||||
|
if [ $failed -gt 0 ]; then |
||||||
|
echo "$failed of $((passed+failed)) test suites failed" |
||||||
|
exit 1 |
||||||
|
else |
||||||
|
echo "$passed test suites passed" |
||||||
|
rm -rf $logdir |
||||||
|
fi |
@ -0,0 +1,100 @@ |
|||||||
|
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
# |
||||||
|
# Jansson is free software; you can redistribute it and/or modify |
||||||
|
# it under the terms of the MIT license. See LICENSE for details. |
||||||
|
|
||||||
|
die() { |
||||||
|
echo "$1" >&2 |
||||||
|
exit 1 |
||||||
|
} |
||||||
|
|
||||||
|
[ -n "$1" ] || die "Usage: $0 suite-name" |
||||||
|
[ -n "$bindir" ] || die "Set bindir" |
||||||
|
[ -n "$logdir" ] || die "Set logdir" |
||||||
|
[ -n "$scriptdir" ] || die "Set scriptdir" |
||||||
|
[ -n "$suites_srcdir" ] || die "Set suites_srcdir" |
||||||
|
[ -n "$suites_builddir" ] || die "Set suites_builddir" |
||||||
|
|
||||||
|
json_process=$bindir/json_process |
||||||
|
|
||||||
|
suite_name=$1 |
||||||
|
suite_srcdir=$suites_srcdir/$suite_name |
||||||
|
suite_builddir=$suites_builddir/$suite_name |
||||||
|
suite_log=$logdir/$suite_name |
||||||
|
|
||||||
|
[ -z "$VERBOSE" ] && VERBOSE=0 |
||||||
|
[ -z "$STOP" ] && STOP=0 |
||||||
|
|
||||||
|
. $scriptdir/valgrind.sh |
||||||
|
|
||||||
|
rm -rf $suite_log |
||||||
|
mkdir -p $suite_log |
||||||
|
|
||||||
|
for test_path in $suite_srcdir/*; do |
||||||
|
test_name=$(basename $test_path) |
||||||
|
test_builddir=$suite_builddir/$test_name |
||||||
|
test_log=$suite_log/$test_name |
||||||
|
|
||||||
|
[ "$test_name" = "run" ] && continue |
||||||
|
is_test || continue |
||||||
|
|
||||||
|
rm -rf $test_log |
||||||
|
mkdir -p $test_log |
||||||
|
if [ $VERBOSE -eq 1 ]; then |
||||||
|
printf '%s... ' "$test_name" |
||||||
|
fi |
||||||
|
|
||||||
|
run_test |
||||||
|
case $? in |
||||||
|
0) |
||||||
|
# Success |
||||||
|
if [ $VERBOSE -eq 1 ]; then |
||||||
|
printf 'ok\n' |
||||||
|
else |
||||||
|
printf '.' |
||||||
|
fi |
||||||
|
rm -rf $test_log |
||||||
|
;; |
||||||
|
|
||||||
|
77) |
||||||
|
# Skip |
||||||
|
if [ $VERBOSE -eq 1 ]; then |
||||||
|
printf 'skipped\n' |
||||||
|
else |
||||||
|
printf 'S' |
||||||
|
fi |
||||||
|
rm -rf $test_log |
||||||
|
;; |
||||||
|
|
||||||
|
*) |
||||||
|
# Failure |
||||||
|
if [ $VERBOSE -eq 1 ]; then |
||||||
|
printf 'FAILED\n' |
||||||
|
else |
||||||
|
printf 'F' |
||||||
|
fi |
||||||
|
|
||||||
|
[ $STOP -eq 1 ] && break |
||||||
|
;; |
||||||
|
esac |
||||||
|
done |
||||||
|
|
||||||
|
if [ $VERBOSE -eq 0 ]; then |
||||||
|
printf '\n' |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -n "$(ls -A $suite_log)" ]; then |
||||||
|
for test_log in $suite_log/*; do |
||||||
|
test_name=$(basename $test_log) |
||||||
|
test_path=$suite_srcdir/$test_name |
||||||
|
echo "=================================================================" |
||||||
|
echo "$suite_name/$test_name" |
||||||
|
echo "=================================================================" |
||||||
|
show_error |
||||||
|
echo |
||||||
|
done |
||||||
|
echo "=================================================================" |
||||||
|
exit 1 |
||||||
|
else |
||||||
|
rm -rf $suite_log |
||||||
|
fi |
@ -0,0 +1,35 @@ |
|||||||
|
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
# |
||||||
|
# Jansson is free software; you can redistribute it and/or modify |
||||||
|
# it under the terms of the MIT license. See LICENSE for details. |
||||||
|
|
||||||
|
[ -z "$VALGRIND" ] && VALGRIND=0 |
||||||
|
|
||||||
|
VALGRIND_CMDLINE="valgrind --leak-check=full --show-reachable=yes --track-origins=yes -q" |
||||||
|
|
||||||
|
if [ $VALGRIND -eq 1 ]; then |
||||||
|
test_runner="$VALGRIND_CMDLINE" |
||||||
|
json_process="$VALGRIND_CMDLINE $json_process" |
||||||
|
else |
||||||
|
test_runner="" |
||||||
|
fi |
||||||
|
|
||||||
|
valgrind_check() { |
||||||
|
if [ $VALGRIND -eq 1 ]; then |
||||||
|
# Check for Valgrind error output. The valgrind option |
||||||
|
# --error-exitcode is not enough because Valgrind doesn't |
||||||
|
# think unfreed allocs are errors. |
||||||
|
if grep -E -q '^==[0-9]+== ' $1; then |
||||||
|
touch $test_log/valgrind_error |
||||||
|
return 1 |
||||||
|
fi |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
valgrind_show_error() { |
||||||
|
if [ $VALGRIND -eq 1 -a -f $test_log/valgrind_error ]; then |
||||||
|
echo "valgrind detected an error" |
||||||
|
return 0 |
||||||
|
fi |
||||||
|
return 1 |
||||||
|
} |
@ -0,0 +1,2 @@ |
|||||||
|
SUBDIRS = api |
||||||
|
EXTRA_DIST = invalid invalid-unicode valid |
@ -0,0 +1,34 @@ |
|||||||
|
EXTRA_DIST = run check-exports |
||||||
|
|
||||||
|
check_PROGRAMS = \
|
||||||
|
test_array \
|
||||||
|
test_copy \
|
||||||
|
test_dump \
|
||||||
|
test_dump_callback \
|
||||||
|
test_equal \
|
||||||
|
test_load \
|
||||||
|
test_loadb \
|
||||||
|
test_load_callback \
|
||||||
|
test_memory_funcs \
|
||||||
|
test_number \
|
||||||
|
test_object \
|
||||||
|
test_pack \
|
||||||
|
test_simple \
|
||||||
|
test_unpack |
||||||
|
|
||||||
|
test_array_SOURCES = test_array.c util.h |
||||||
|
test_copy_SOURCES = test_copy.c util.h |
||||||
|
test_dump_SOURCES = test_dump.c util.h |
||||||
|
test_dump_callback_SOURCES = test_dump_callback.c util.h |
||||||
|
test_load_SOURCES = test_load.c util.h |
||||||
|
test_loadb_SOURCES = test_loadb.c util.h |
||||||
|
test_memory_funcs_SOURCES = test_memory_funcs.c util.h |
||||||
|
test_number_SOURCES = test_number.c util.h |
||||||
|
test_object_SOURCES = test_object.c util.h |
||||||
|
test_pack_SOURCES = test_pack.c util.h |
||||||
|
test_simple_SOURCES = test_simple.c util.h |
||||||
|
test_unpack_SOURCES = test_unpack.c util.h |
||||||
|
|
||||||
|
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src |
||||||
|
LDFLAGS = -static # for speed and Valgrind |
||||||
|
LDADD = $(top_builddir)/src/libjansson.la |
@ -0,0 +1,23 @@ |
|||||||
|
#!/bin/sh |
||||||
|
# |
||||||
|
# This test checks that libjansson.so exports the correct symbols. |
||||||
|
# |
||||||
|
|
||||||
|
SOFILE="../src/.libs/libjansson.so" |
||||||
|
|
||||||
|
# The list of symbols, which the shared object should export, is read |
||||||
|
# from the def file, which is used in Windows builds |
||||||
|
grep 'json_' $top_srcdir/src/jansson.def \ |
||||||
|
| sed -e 's/ //g' \ |
||||||
|
| sort \ |
||||||
|
>$test_log/exports |
||||||
|
|
||||||
|
nm -D $SOFILE >/dev/null >$test_log/symbols 2>/dev/null \ |
||||||
|
|| exit 77 # Skip if "nm -D" doesn't seem to work |
||||||
|
|
||||||
|
grep ' [DT] ' $test_log/symbols | cut -d' ' -f3 | grep -v '^_' | sort >$test_log/output |
||||||
|
|
||||||
|
if ! cmp -s $test_log/exports $test_log/output; then |
||||||
|
diff -u $test_log/exports $test_log/output >&2 |
||||||
|
exit 1 |
||||||
|
fi |
@ -0,0 +1,36 @@ |
|||||||
|
#!/bin/sh |
||||||
|
# |
||||||
|
# Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
# |
||||||
|
# Jansson is free software; you can redistribute it and/or modify |
||||||
|
# it under the terms of the MIT license. See LICENSE for details. |
||||||
|
|
||||||
|
is_test() { |
||||||
|
case "$test_name" in |
||||||
|
*.c|check-exports) |
||||||
|
return 0 |
||||||
|
;; |
||||||
|
*) |
||||||
|
return 1 |
||||||
|
;; |
||||||
|
esac |
||||||
|
} |
||||||
|
|
||||||
|
run_test() { |
||||||
|
if [ "$test_name" = "check-exports" ]; then |
||||||
|
test_log=$test_log $test_path >$test_log/stdout 2>$test_log/stderr |
||||||
|
else |
||||||
|
$test_runner $suite_builddir/${test_name%.c} \ |
||||||
|
>$test_log/stdout \ |
||||||
|
2>$test_log/stderr \ |
||||||
|
|| return 1 |
||||||
|
valgrind_check $test_log/stderr || return 1 |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
show_error() { |
||||||
|
valgrind_show_error && return |
||||||
|
cat $test_log/stderr |
||||||
|
} |
||||||
|
|
||||||
|
. $top_srcdir/test/scripts/run-tests.sh |
@ -0,0 +1,432 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static void test_misc(void) |
||||||
|
{ |
||||||
|
json_t *array, *five, *seven, *value; |
||||||
|
size_t i; |
||||||
|
|
||||||
|
array = json_array(); |
||||||
|
five = json_integer(5); |
||||||
|
seven = json_integer(7); |
||||||
|
|
||||||
|
if(!array) |
||||||
|
fail("unable to create array"); |
||||||
|
if(!five || !seven) |
||||||
|
fail("unable to create integer"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 0) |
||||||
|
fail("empty array has nonzero size"); |
||||||
|
|
||||||
|
if(!json_array_append(array, NULL)) |
||||||
|
fail("able to append NULL"); |
||||||
|
|
||||||
|
if(json_array_append(array, five)) |
||||||
|
fail("unable to append"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 1) |
||||||
|
fail("wrong array size"); |
||||||
|
|
||||||
|
value = json_array_get(array, 0); |
||||||
|
if(!value) |
||||||
|
fail("unable to get item"); |
||||||
|
if(value != five) |
||||||
|
fail("got wrong value"); |
||||||
|
|
||||||
|
if(json_array_append(array, seven)) |
||||||
|
fail("unable to append value"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 2) |
||||||
|
fail("wrong array size"); |
||||||
|
|
||||||
|
value = json_array_get(array, 1); |
||||||
|
if(!value) |
||||||
|
fail("unable to get item"); |
||||||
|
if(value != seven) |
||||||
|
fail("got wrong value"); |
||||||
|
|
||||||
|
if(json_array_set(array, 0, seven)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
if(!json_array_set(array, 0, NULL)) |
||||||
|
fail("able to set NULL"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 2) |
||||||
|
fail("wrong array size"); |
||||||
|
|
||||||
|
value = json_array_get(array, 0); |
||||||
|
if(!value) |
||||||
|
fail("unable to get item"); |
||||||
|
if(value != seven) |
||||||
|
fail("got wrong value"); |
||||||
|
|
||||||
|
if(json_array_get(array, 2) != NULL) |
||||||
|
fail("able to get value out of bounds"); |
||||||
|
|
||||||
|
if(!json_array_set(array, 2, seven)) |
||||||
|
fail("able to set value out of bounds"); |
||||||
|
|
||||||
|
for(i = 2; i < 30; i++) { |
||||||
|
if(json_array_append(array, seven)) |
||||||
|
fail("unable to append value"); |
||||||
|
|
||||||
|
if(json_array_size(array) != i + 1) |
||||||
|
fail("wrong array size"); |
||||||
|
} |
||||||
|
|
||||||
|
for(i = 0; i < 30; i++) { |
||||||
|
value = json_array_get(array, i); |
||||||
|
if(!value) |
||||||
|
fail("unable to get item"); |
||||||
|
if(value != seven) |
||||||
|
fail("got wrong value"); |
||||||
|
} |
||||||
|
|
||||||
|
if(json_array_set_new(array, 15, json_integer(123))) |
||||||
|
fail("unable to set new value"); |
||||||
|
|
||||||
|
value = json_array_get(array, 15); |
||||||
|
if(!json_is_integer(value) || json_integer_value(value) != 123) |
||||||
|
fail("json_array_set_new works incorrectly"); |
||||||
|
|
||||||
|
if(!json_array_set_new(array, 15, NULL)) |
||||||
|
fail("able to set_new NULL value"); |
||||||
|
|
||||||
|
if(json_array_append_new(array, json_integer(321))) |
||||||
|
fail("unable to append new value"); |
||||||
|
|
||||||
|
value = json_array_get(array, json_array_size(array) - 1); |
||||||
|
if(!json_is_integer(value) || json_integer_value(value) != 321) |
||||||
|
fail("json_array_append_new works incorrectly"); |
||||||
|
|
||||||
|
if(!json_array_append_new(array, NULL)) |
||||||
|
fail("able to append_new NULL value"); |
||||||
|
|
||||||
|
json_decref(five); |
||||||
|
json_decref(seven); |
||||||
|
json_decref(array); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_insert(void) |
||||||
|
{ |
||||||
|
json_t *array, *five, *seven, *eleven, *value; |
||||||
|
int i; |
||||||
|
|
||||||
|
array = json_array(); |
||||||
|
five = json_integer(5); |
||||||
|
seven = json_integer(7); |
||||||
|
eleven = json_integer(11); |
||||||
|
|
||||||
|
if(!array) |
||||||
|
fail("unable to create array"); |
||||||
|
if(!five || !seven || !eleven) |
||||||
|
fail("unable to create integer"); |
||||||
|
|
||||||
|
|
||||||
|
if(!json_array_insert(array, 1, five)) |
||||||
|
fail("able to insert value out of bounds"); |
||||||
|
|
||||||
|
|
||||||
|
if(json_array_insert(array, 0, five)) |
||||||
|
fail("unable to insert value in an empty array"); |
||||||
|
|
||||||
|
if(json_array_get(array, 0) != five) |
||||||
|
fail("json_array_insert works incorrectly"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 1) |
||||||
|
fail("array size is invalid after insertion"); |
||||||
|
|
||||||
|
|
||||||
|
if(json_array_insert(array, 1, seven)) |
||||||
|
fail("unable to insert value at the end of an array"); |
||||||
|
|
||||||
|
if(json_array_get(array, 0) != five) |
||||||
|
fail("json_array_insert works incorrectly"); |
||||||
|
|
||||||
|
if(json_array_get(array, 1) != seven) |
||||||
|
fail("json_array_insert works incorrectly"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 2) |
||||||
|
fail("array size is invalid after insertion"); |
||||||
|
|
||||||
|
|
||||||
|
if(json_array_insert(array, 1, eleven)) |
||||||
|
fail("unable to insert value in the middle of an array"); |
||||||
|
|
||||||
|
if(json_array_get(array, 0) != five) |
||||||
|
fail("json_array_insert works incorrectly"); |
||||||
|
|
||||||
|
if(json_array_get(array, 1) != eleven) |
||||||
|
fail("json_array_insert works incorrectly"); |
||||||
|
|
||||||
|
if(json_array_get(array, 2) != seven) |
||||||
|
fail("json_array_insert works incorrectly"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 3) |
||||||
|
fail("array size is invalid after insertion"); |
||||||
|
|
||||||
|
|
||||||
|
if(json_array_insert_new(array, 2, json_integer(123))) |
||||||
|
fail("unable to insert value in the middle of an array"); |
||||||
|
|
||||||
|
value = json_array_get(array, 2); |
||||||
|
if(!json_is_integer(value) || json_integer_value(value) != 123) |
||||||
|
fail("json_array_insert_new works incorrectly"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 4) |
||||||
|
fail("array size is invalid after insertion"); |
||||||
|
|
||||||
|
|
||||||
|
for(i = 0; i < 20; i++) { |
||||||
|
if(json_array_insert(array, 0, seven)) |
||||||
|
fail("unable to insert value at the begining of an array"); |
||||||
|
} |
||||||
|
|
||||||
|
for(i = 0; i < 20; i++) { |
||||||
|
if(json_array_get(array, i) != seven) |
||||||
|
fail("json_aray_insert works incorrectly"); |
||||||
|
} |
||||||
|
|
||||||
|
if(json_array_size(array) != 24) |
||||||
|
fail("array size is invalid after loop insertion"); |
||||||
|
|
||||||
|
json_decref(five); |
||||||
|
json_decref(seven); |
||||||
|
json_decref(eleven); |
||||||
|
json_decref(array); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_remove(void) |
||||||
|
{ |
||||||
|
json_t *array, *five, *seven; |
||||||
|
int i; |
||||||
|
|
||||||
|
array = json_array(); |
||||||
|
five = json_integer(5); |
||||||
|
seven = json_integer(7); |
||||||
|
|
||||||
|
if(!array) |
||||||
|
fail("unable to create array"); |
||||||
|
if(!five) |
||||||
|
fail("unable to create integer"); |
||||||
|
if(!seven) |
||||||
|
fail("unable to create integer"); |
||||||
|
|
||||||
|
|
||||||
|
if(!json_array_remove(array, 0)) |
||||||
|
fail("able to remove an unexisting index"); |
||||||
|
|
||||||
|
|
||||||
|
if(json_array_append(array, five)) |
||||||
|
fail("unable to append"); |
||||||
|
|
||||||
|
if(!json_array_remove(array, 1)) |
||||||
|
fail("able to remove an unexisting index"); |
||||||
|
|
||||||
|
if(json_array_remove(array, 0)) |
||||||
|
fail("unable to remove"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 0) |
||||||
|
fail("array size is invalid after removing"); |
||||||
|
|
||||||
|
|
||||||
|
if(json_array_append(array, five) || |
||||||
|
json_array_append(array, seven) || |
||||||
|
json_array_append(array, five) || |
||||||
|
json_array_append(array, seven)) |
||||||
|
fail("unable to append"); |
||||||
|
|
||||||
|
if(json_array_remove(array, 2)) |
||||||
|
fail("unable to remove"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 3) |
||||||
|
fail("array size is invalid after removing"); |
||||||
|
|
||||||
|
if(json_array_get(array, 0) != five || |
||||||
|
json_array_get(array, 1) != seven || |
||||||
|
json_array_get(array, 2) != seven) |
||||||
|
fail("remove works incorrectly"); |
||||||
|
|
||||||
|
json_decref(array); |
||||||
|
|
||||||
|
array = json_array(); |
||||||
|
for(i = 0; i < 4; i++) { |
||||||
|
json_array_append(array, five); |
||||||
|
json_array_append(array, seven); |
||||||
|
} |
||||||
|
if(json_array_size(array) != 8) |
||||||
|
fail("unable to append 8 items to array"); |
||||||
|
|
||||||
|
/* Remove an element from a "full" array. */ |
||||||
|
json_array_remove(array, 5); |
||||||
|
|
||||||
|
json_decref(five); |
||||||
|
json_decref(seven); |
||||||
|
json_decref(array); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_clear(void) |
||||||
|
{ |
||||||
|
json_t *array, *five, *seven; |
||||||
|
int i; |
||||||
|
|
||||||
|
array = json_array(); |
||||||
|
five = json_integer(5); |
||||||
|
seven = json_integer(7); |
||||||
|
|
||||||
|
if(!array) |
||||||
|
fail("unable to create array"); |
||||||
|
if(!five || !seven) |
||||||
|
fail("unable to create integer"); |
||||||
|
|
||||||
|
for(i = 0; i < 10; i++) { |
||||||
|
if(json_array_append(array, five)) |
||||||
|
fail("unable to append"); |
||||||
|
} |
||||||
|
for(i = 0; i < 10; i++) { |
||||||
|
if(json_array_append(array, seven)) |
||||||
|
fail("unable to append"); |
||||||
|
} |
||||||
|
|
||||||
|
if(json_array_size(array) != 20) |
||||||
|
fail("array size is invalid after appending"); |
||||||
|
|
||||||
|
if(json_array_clear(array)) |
||||||
|
fail("unable to clear"); |
||||||
|
|
||||||
|
if(json_array_size(array) != 0) |
||||||
|
fail("array size is invalid after clearing"); |
||||||
|
|
||||||
|
json_decref(five); |
||||||
|
json_decref(seven); |
||||||
|
json_decref(array); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_extend(void) |
||||||
|
{ |
||||||
|
json_t *array1, *array2, *five, *seven; |
||||||
|
int i; |
||||||
|
|
||||||
|
array1 = json_array(); |
||||||
|
array2 = json_array(); |
||||||
|
five = json_integer(5); |
||||||
|
seven = json_integer(7); |
||||||
|
|
||||||
|
if(!array1 || !array2) |
||||||
|
fail("unable to create array"); |
||||||
|
if(!five || !seven) |
||||||
|
fail("unable to create integer"); |
||||||
|
|
||||||
|
for(i = 0; i < 10; i++) { |
||||||
|
if(json_array_append(array1, five)) |
||||||
|
fail("unable to append"); |
||||||
|
} |
||||||
|
for(i = 0; i < 10; i++) { |
||||||
|
if(json_array_append(array2, seven)) |
||||||
|
fail("unable to append"); |
||||||
|
} |
||||||
|
|
||||||
|
if(json_array_size(array1) != 10 || json_array_size(array2) != 10) |
||||||
|
fail("array size is invalid after appending"); |
||||||
|
|
||||||
|
if(json_array_extend(array1, array2)) |
||||||
|
fail("unable to extend"); |
||||||
|
|
||||||
|
for(i = 0; i < 10; i++) { |
||||||
|
if(json_array_get(array1, i) != five) |
||||||
|
fail("invalid array contents after extending"); |
||||||
|
} |
||||||
|
for(i = 10; i < 20; i++) { |
||||||
|
if(json_array_get(array1, i) != seven) |
||||||
|
fail("invalid array contents after extending"); |
||||||
|
} |
||||||
|
|
||||||
|
json_decref(five); |
||||||
|
json_decref(seven); |
||||||
|
json_decref(array1); |
||||||
|
json_decref(array2); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_circular() |
||||||
|
{ |
||||||
|
json_t *array1, *array2; |
||||||
|
|
||||||
|
/* the simple cases are checked */ |
||||||
|
|
||||||
|
array1 = json_array(); |
||||||
|
if(!array1) |
||||||
|
fail("unable to create array"); |
||||||
|
|
||||||
|
if(json_array_append(array1, array1) == 0) |
||||||
|
fail("able to append self"); |
||||||
|
|
||||||
|
if(json_array_insert(array1, 0, array1) == 0) |
||||||
|
fail("able to insert self"); |
||||||
|
|
||||||
|
if(json_array_append_new(array1, json_true())) |
||||||
|
fail("failed to append true"); |
||||||
|
|
||||||
|
if(json_array_set(array1, 0, array1) == 0) |
||||||
|
fail("able to set self"); |
||||||
|
|
||||||
|
json_decref(array1); |
||||||
|
|
||||||
|
|
||||||
|
/* create circular references */ |
||||||
|
|
||||||
|
array1 = json_array(); |
||||||
|
array2 = json_array(); |
||||||
|
if(!array1 || !array2) |
||||||
|
fail("unable to create array"); |
||||||
|
|
||||||
|
if(json_array_append(array1, array2) || |
||||||
|
json_array_append(array2, array1)) |
||||||
|
fail("unable to append"); |
||||||
|
|
||||||
|
/* circularity is detected when dumping */ |
||||||
|
if(json_dumps(array1, 0) != NULL) |
||||||
|
fail("able to dump circulars"); |
||||||
|
|
||||||
|
/* decref twice to deal with the circular references */ |
||||||
|
json_decref(array1); |
||||||
|
json_decref(array2); |
||||||
|
json_decref(array1); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_array_foreach() |
||||||
|
{ |
||||||
|
size_t index; |
||||||
|
json_t *array1, *array2, *value; |
||||||
|
|
||||||
|
array1 = json_pack("[sisisi]", "foo", 1, "bar", 2, "baz", 3); |
||||||
|
array2 = json_array(); |
||||||
|
|
||||||
|
json_array_foreach(array1, index, value) { |
||||||
|
json_array_append(array2, value); |
||||||
|
} |
||||||
|
|
||||||
|
if(!json_equal(array1, array2)) |
||||||
|
fail("json_array_foreach failed to iterate all elements"); |
||||||
|
|
||||||
|
json_decref(array1); |
||||||
|
json_decref(array2); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
test_misc(); |
||||||
|
test_insert(); |
||||||
|
test_remove(); |
||||||
|
test_clear(); |
||||||
|
test_extend(); |
||||||
|
test_circular(); |
||||||
|
test_array_foreach(); |
||||||
|
} |
@ -0,0 +1,318 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
#include <jansson.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static void test_copy_simple(void) |
||||||
|
{ |
||||||
|
json_t *value, *copy; |
||||||
|
|
||||||
|
if(json_copy(NULL)) |
||||||
|
fail("copying NULL doesn't return NULL"); |
||||||
|
|
||||||
|
/* true */ |
||||||
|
value = json_true(); |
||||||
|
copy = json_copy(value); |
||||||
|
if(value != copy) |
||||||
|
fail("copying true failed"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
|
||||||
|
/* false */ |
||||||
|
value = json_false(); |
||||||
|
copy = json_copy(value); |
||||||
|
if(value != copy) |
||||||
|
fail("copying false failed"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
|
||||||
|
/* null */ |
||||||
|
value = json_null(); |
||||||
|
copy = json_copy(value); |
||||||
|
if(value != copy) |
||||||
|
fail("copying null failed"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
|
||||||
|
/* string */ |
||||||
|
value = json_string("foo"); |
||||||
|
if(!value) |
||||||
|
fail("unable to create a string"); |
||||||
|
copy = json_copy(value); |
||||||
|
if(!copy) |
||||||
|
fail("unable to copy a string"); |
||||||
|
if(copy == value) |
||||||
|
fail("copying a string doesn't copy"); |
||||||
|
if(!json_equal(copy, value)) |
||||||
|
fail("copying a string produces an inequal copy"); |
||||||
|
if(value->refcount != 1 || copy->refcount != 1) |
||||||
|
fail("invalid refcounts"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
|
||||||
|
/* integer */ |
||||||
|
value = json_integer(543); |
||||||
|
if(!value) |
||||||
|
fail("unable to create an integer"); |
||||||
|
copy = json_copy(value); |
||||||
|
if(!copy) |
||||||
|
fail("unable to copy an integer"); |
||||||
|
if(copy == value) |
||||||
|
fail("copying an integer doesn't copy"); |
||||||
|
if(!json_equal(copy, value)) |
||||||
|
fail("copying an integer produces an inequal copy"); |
||||||
|
if(value->refcount != 1 || copy->refcount != 1) |
||||||
|
fail("invalid refcounts"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
|
||||||
|
/* real */ |
||||||
|
value = json_real(123e9); |
||||||
|
if(!value) |
||||||
|
fail("unable to create a real"); |
||||||
|
copy = json_copy(value); |
||||||
|
if(!copy) |
||||||
|
fail("unable to copy a real"); |
||||||
|
if(copy == value) |
||||||
|
fail("copying a real doesn't copy"); |
||||||
|
if(!json_equal(copy, value)) |
||||||
|
fail("copying a real produces an inequal copy"); |
||||||
|
if(value->refcount != 1 || copy->refcount != 1) |
||||||
|
fail("invalid refcounts"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_deep_copy_simple(void) |
||||||
|
{ |
||||||
|
json_t *value, *copy; |
||||||
|
|
||||||
|
if(json_deep_copy(NULL)) |
||||||
|
fail("deep copying NULL doesn't return NULL"); |
||||||
|
|
||||||
|
/* true */ |
||||||
|
value = json_true(); |
||||||
|
copy = json_deep_copy(value); |
||||||
|
if(value != copy) |
||||||
|
fail("deep copying true failed"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
|
||||||
|
/* false */ |
||||||
|
value = json_false(); |
||||||
|
copy = json_deep_copy(value); |
||||||
|
if(value != copy) |
||||||
|
fail("deep copying false failed"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
|
||||||
|
/* null */ |
||||||
|
value = json_null(); |
||||||
|
copy = json_deep_copy(value); |
||||||
|
if(value != copy) |
||||||
|
fail("deep copying null failed"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
|
||||||
|
/* string */ |
||||||
|
value = json_string("foo"); |
||||||
|
if(!value) |
||||||
|
fail("unable to create a string"); |
||||||
|
copy = json_deep_copy(value); |
||||||
|
if(!copy) |
||||||
|
fail("unable to deep copy a string"); |
||||||
|
if(copy == value) |
||||||
|
fail("deep copying a string doesn't copy"); |
||||||
|
if(!json_equal(copy, value)) |
||||||
|
fail("deep copying a string produces an inequal copy"); |
||||||
|
if(value->refcount != 1 || copy->refcount != 1) |
||||||
|
fail("invalid refcounts"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
|
||||||
|
/* integer */ |
||||||
|
value = json_integer(543); |
||||||
|
if(!value) |
||||||
|
fail("unable to create an integer"); |
||||||
|
copy = json_deep_copy(value); |
||||||
|
if(!copy) |
||||||
|
fail("unable to deep copy an integer"); |
||||||
|
if(copy == value) |
||||||
|
fail("deep copying an integer doesn't copy"); |
||||||
|
if(!json_equal(copy, value)) |
||||||
|
fail("deep copying an integer produces an inequal copy"); |
||||||
|
if(value->refcount != 1 || copy->refcount != 1) |
||||||
|
fail("invalid refcounts"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
|
||||||
|
/* real */ |
||||||
|
value = json_real(123e9); |
||||||
|
if(!value) |
||||||
|
fail("unable to create a real"); |
||||||
|
copy = json_deep_copy(value); |
||||||
|
if(!copy) |
||||||
|
fail("unable to deep copy a real"); |
||||||
|
if(copy == value) |
||||||
|
fail("deep copying a real doesn't copy"); |
||||||
|
if(!json_equal(copy, value)) |
||||||
|
fail("deep copying a real produces an inequal copy"); |
||||||
|
if(value->refcount != 1 || copy->refcount != 1) |
||||||
|
fail("invalid refcounts"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(copy); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_copy_array(void) |
||||||
|
{ |
||||||
|
const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]"; |
||||||
|
|
||||||
|
json_t *array, *copy; |
||||||
|
size_t i; |
||||||
|
|
||||||
|
array = json_loads(json_array_text, 0, NULL); |
||||||
|
if(!array) |
||||||
|
fail("unable to parse an array"); |
||||||
|
|
||||||
|
copy = json_copy(array); |
||||||
|
if(!copy) |
||||||
|
fail("unable to copy an array"); |
||||||
|
if(copy == array) |
||||||
|
fail("copying an array doesn't copy"); |
||||||
|
if(!json_equal(copy, array)) |
||||||
|
fail("copying an array produces an inequal copy"); |
||||||
|
|
||||||
|
for(i = 0; i < json_array_size(copy); i++) |
||||||
|
{ |
||||||
|
if(json_array_get(array, i) != json_array_get(copy, i)) |
||||||
|
fail("copying an array modifies its elements"); |
||||||
|
} |
||||||
|
|
||||||
|
json_decref(array); |
||||||
|
json_decref(copy); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_deep_copy_array(void) |
||||||
|
{ |
||||||
|
const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]"; |
||||||
|
|
||||||
|
json_t *array, *copy; |
||||||
|
size_t i; |
||||||
|
|
||||||
|
array = json_loads(json_array_text, 0, NULL); |
||||||
|
if(!array) |
||||||
|
fail("unable to parse an array"); |
||||||
|
|
||||||
|
copy = json_deep_copy(array); |
||||||
|
if(!copy) |
||||||
|
fail("unable to deep copy an array"); |
||||||
|
if(copy == array) |
||||||
|
fail("deep copying an array doesn't copy"); |
||||||
|
if(!json_equal(copy, array)) |
||||||
|
fail("deep copying an array produces an inequal copy"); |
||||||
|
|
||||||
|
for(i = 0; i < json_array_size(copy); i++) |
||||||
|
{ |
||||||
|
if(json_array_get(array, i) == json_array_get(copy, i)) |
||||||
|
fail("deep copying an array doesn't copy its elements"); |
||||||
|
} |
||||||
|
|
||||||
|
json_decref(array); |
||||||
|
json_decref(copy); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_copy_object(void) |
||||||
|
{ |
||||||
|
const char *json_object_text = |
||||||
|
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}"; |
||||||
|
|
||||||
|
json_t *object, *copy; |
||||||
|
void *iter; |
||||||
|
|
||||||
|
object = json_loads(json_object_text, 0, NULL); |
||||||
|
if(!object) |
||||||
|
fail("unable to parse an object"); |
||||||
|
|
||||||
|
copy = json_copy(object); |
||||||
|
if(!copy) |
||||||
|
fail("unable to copy an object"); |
||||||
|
if(copy == object) |
||||||
|
fail("copying an object doesn't copy"); |
||||||
|
if(!json_equal(copy, object)) |
||||||
|
fail("copying an object produces an inequal copy"); |
||||||
|
|
||||||
|
iter = json_object_iter(object); |
||||||
|
while(iter) |
||||||
|
{ |
||||||
|
const char *key; |
||||||
|
json_t *value1, *value2; |
||||||
|
|
||||||
|
key = json_object_iter_key(iter); |
||||||
|
value1 = json_object_iter_value(iter); |
||||||
|
value2 = json_object_get(copy, key); |
||||||
|
|
||||||
|
if(value1 != value2) |
||||||
|
fail("deep copying an object modifies its items"); |
||||||
|
|
||||||
|
iter = json_object_iter_next(object, iter); |
||||||
|
} |
||||||
|
|
||||||
|
json_decref(object); |
||||||
|
json_decref(copy); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_deep_copy_object(void) |
||||||
|
{ |
||||||
|
const char *json_object_text = |
||||||
|
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}"; |
||||||
|
|
||||||
|
json_t *object, *copy; |
||||||
|
void *iter; |
||||||
|
|
||||||
|
object = json_loads(json_object_text, 0, NULL); |
||||||
|
if(!object) |
||||||
|
fail("unable to parse an object"); |
||||||
|
|
||||||
|
copy = json_deep_copy(object); |
||||||
|
if(!copy) |
||||||
|
fail("unable to deep copy an object"); |
||||||
|
if(copy == object) |
||||||
|
fail("deep copying an object doesn't copy"); |
||||||
|
if(!json_equal(copy, object)) |
||||||
|
fail("deep copying an object produces an inequal copy"); |
||||||
|
|
||||||
|
iter = json_object_iter(object); |
||||||
|
while(iter) |
||||||
|
{ |
||||||
|
const char *key; |
||||||
|
json_t *value1, *value2; |
||||||
|
|
||||||
|
key = json_object_iter_key(iter); |
||||||
|
value1 = json_object_iter_value(iter); |
||||||
|
value2 = json_object_get(copy, key); |
||||||
|
|
||||||
|
if(value1 == value2) |
||||||
|
fail("deep copying an object doesn't copy its items"); |
||||||
|
|
||||||
|
iter = json_object_iter_next(object, iter); |
||||||
|
} |
||||||
|
|
||||||
|
json_decref(object); |
||||||
|
json_decref(copy); |
||||||
|
} |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
test_copy_simple(); |
||||||
|
test_deep_copy_simple(); |
||||||
|
test_copy_array(); |
||||||
|
test_deep_copy_array(); |
||||||
|
test_copy_object(); |
||||||
|
test_deep_copy_object(); |
||||||
|
} |
@ -0,0 +1,190 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
#include <string.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static int encode_null_callback(const char *buffer, size_t size, void *data) |
||||||
|
{ |
||||||
|
(void)buffer; |
||||||
|
(void)size; |
||||||
|
(void)data; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void encode_null() |
||||||
|
{ |
||||||
|
if(json_dumps(NULL, JSON_ENCODE_ANY) != NULL) |
||||||
|
fail("json_dumps didn't fail for NULL"); |
||||||
|
|
||||||
|
if(json_dumpf(NULL, stderr, JSON_ENCODE_ANY) != -1) |
||||||
|
fail("json_dumpf didn't fail for NULL"); |
||||||
|
|
||||||
|
/* Don't test json_dump_file to avoid creating a file */ |
||||||
|
|
||||||
|
if(json_dump_callback(NULL, encode_null_callback, NULL, JSON_ENCODE_ANY) != -1) |
||||||
|
fail("json_dump_callback didn't fail for NULL"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static void encode_twice() |
||||||
|
{ |
||||||
|
/* Encode an empty object/array, add an item, encode again */ |
||||||
|
|
||||||
|
json_t *json; |
||||||
|
char *result; |
||||||
|
|
||||||
|
json = json_object(); |
||||||
|
result = json_dumps(json, 0); |
||||||
|
if(!result || strcmp(result, "{}")) |
||||||
|
fail("json_dumps failed"); |
||||||
|
free(result); |
||||||
|
|
||||||
|
json_object_set_new(json, "foo", json_integer(5)); |
||||||
|
result = json_dumps(json, 0); |
||||||
|
if(!result || strcmp(result, "{\"foo\": 5}")) |
||||||
|
fail("json_dumps failed"); |
||||||
|
free(result); |
||||||
|
|
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
json = json_array(); |
||||||
|
result = json_dumps(json, 0); |
||||||
|
if(!result || strcmp(result, "[]")) |
||||||
|
fail("json_dumps failed"); |
||||||
|
free(result); |
||||||
|
|
||||||
|
json_array_append_new(json, json_integer(5)); |
||||||
|
result = json_dumps(json, 0); |
||||||
|
if(!result || strcmp(result, "[5]")) |
||||||
|
fail("json_dumps failed"); |
||||||
|
free(result); |
||||||
|
|
||||||
|
json_decref(json); |
||||||
|
} |
||||||
|
|
||||||
|
static void circular_references() |
||||||
|
{ |
||||||
|
/* Construct a JSON object/array with a circular reference:
|
||||||
|
|
||||||
|
object: {"a": {"b": {"c": <circular reference to $.a>}}} |
||||||
|
array: [[[<circular reference to the $[0] array>]]] |
||||||
|
|
||||||
|
Encode it, remove the circular reference and encode again. |
||||||
|
*/ |
||||||
|
|
||||||
|
json_t *json; |
||||||
|
char *result; |
||||||
|
|
||||||
|
json = json_object(); |
||||||
|
json_object_set_new(json, "a", json_object()); |
||||||
|
json_object_set_new(json_object_get(json, "a"), "b", json_object()); |
||||||
|
json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c", |
||||||
|
json_object_get(json, "a")); |
||||||
|
|
||||||
|
if(json_dumps(json, 0)) |
||||||
|
fail("json_dumps encoded a circular reference!"); |
||||||
|
|
||||||
|
json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c"); |
||||||
|
|
||||||
|
result = json_dumps(json, 0); |
||||||
|
if(!result || strcmp(result, "{\"a\": {\"b\": {}}}")) |
||||||
|
fail("json_dumps failed!"); |
||||||
|
free(result); |
||||||
|
|
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
json = json_array(); |
||||||
|
json_array_append_new(json, json_array()); |
||||||
|
json_array_append_new(json_array_get(json, 0), json_array()); |
||||||
|
json_array_append(json_array_get(json_array_get(json, 0), 0), |
||||||
|
json_array_get(json, 0)); |
||||||
|
|
||||||
|
if(json_dumps(json, 0)) |
||||||
|
fail("json_dumps encoded a circular reference!"); |
||||||
|
|
||||||
|
json_array_remove(json_array_get(json_array_get(json, 0), 0), 0); |
||||||
|
|
||||||
|
result = json_dumps(json, 0); |
||||||
|
if(!result || strcmp(result, "[[[]]]")) |
||||||
|
fail("json_dumps failed!"); |
||||||
|
free(result); |
||||||
|
|
||||||
|
json_decref(json); |
||||||
|
} |
||||||
|
|
||||||
|
static void encode_other_than_array_or_object() |
||||||
|
{ |
||||||
|
/* Encoding anything other than array or object should only
|
||||||
|
* succeed if the JSON_ENCODE_ANY flag is used */ |
||||||
|
|
||||||
|
json_t *json; |
||||||
|
FILE *fp = NULL; |
||||||
|
char *result; |
||||||
|
|
||||||
|
json = json_string("foo"); |
||||||
|
if(json_dumps(json, 0) != NULL) |
||||||
|
fail("json_dumps encoded a string!"); |
||||||
|
if(json_dumpf(json, fp, 0) == 0) |
||||||
|
fail("json_dumpf encoded a string!"); |
||||||
|
|
||||||
|
result = json_dumps(json, JSON_ENCODE_ANY); |
||||||
|
if(!result || strcmp(result, "\"foo\"") != 0) |
||||||
|
fail("json_dumps failed to encode a string with JSON_ENCODE_ANY"); |
||||||
|
|
||||||
|
free(result); |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
json = json_integer(42); |
||||||
|
if(json_dumps(json, 0) != NULL) |
||||||
|
fail("json_dumps encoded an integer!"); |
||||||
|
if(json_dumpf(json, fp, 0) == 0) |
||||||
|
fail("json_dumpf encoded an integer!"); |
||||||
|
|
||||||
|
result = json_dumps(json, JSON_ENCODE_ANY); |
||||||
|
if(!result || strcmp(result, "42") != 0) |
||||||
|
fail("json_dumps failed to encode an integer with JSON_ENCODE_ANY"); |
||||||
|
|
||||||
|
free(result); |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
static void escape_slashes() |
||||||
|
{ |
||||||
|
/* Test dump escaping slashes */ |
||||||
|
|
||||||
|
json_t *json; |
||||||
|
char *result; |
||||||
|
|
||||||
|
json = json_object(); |
||||||
|
json_object_set_new(json, "url", json_string("https://github.com/akheron/jansson")); |
||||||
|
|
||||||
|
result = json_dumps(json, 0); |
||||||
|
if(!result || strcmp(result, "{\"url\": \"https://github.com/akheron/jansson\"}")) |
||||||
|
fail("json_dumps failed to not escape slashes"); |
||||||
|
|
||||||
|
free(result); |
||||||
|
|
||||||
|
result = json_dumps(json, JSON_ESCAPE_SLASH); |
||||||
|
if(!result || strcmp(result, "{\"url\": \"https:\\/\\/github.com\\/akheron\\/jansson\"}")) |
||||||
|
fail("json_dumps failed to escape slashes"); |
||||||
|
|
||||||
|
free(result); |
||||||
|
json_decref(json); |
||||||
|
} |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
encode_null(); |
||||||
|
encode_twice(); |
||||||
|
circular_references(); |
||||||
|
encode_other_than_array_or_object(); |
||||||
|
escape_slashes(); |
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
#include <string.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
struct my_sink { |
||||||
|
char *buf; |
||||||
|
size_t off; |
||||||
|
size_t cap; |
||||||
|
}; |
||||||
|
|
||||||
|
static int my_writer(const char *buffer, size_t len, void *data) { |
||||||
|
struct my_sink *s = data; |
||||||
|
if (len > s->cap - s->off) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
memcpy(s->buf + s->off, buffer, len); |
||||||
|
s->off += len; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
struct my_sink s; |
||||||
|
json_t *json; |
||||||
|
const char str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]"; |
||||||
|
char *dumped_to_string; |
||||||
|
|
||||||
|
json = json_loads(str, 0, NULL); |
||||||
|
if(!json) { |
||||||
|
fail("json_loads failed"); |
||||||
|
} |
||||||
|
|
||||||
|
dumped_to_string = json_dumps(json, 0); |
||||||
|
if (!dumped_to_string) { |
||||||
|
json_decref(json); |
||||||
|
fail("json_dumps failed"); |
||||||
|
} |
||||||
|
|
||||||
|
s.off = 0; |
||||||
|
s.cap = strlen(dumped_to_string); |
||||||
|
s.buf = malloc(s.cap); |
||||||
|
if (!s.buf) { |
||||||
|
json_decref(json); |
||||||
|
free(dumped_to_string); |
||||||
|
fail("malloc failed"); |
||||||
|
} |
||||||
|
|
||||||
|
if (json_dump_callback(json, my_writer, &s, 0) == -1) { |
||||||
|
json_decref(json); |
||||||
|
free(dumped_to_string); |
||||||
|
free(s.buf); |
||||||
|
fail("json_dump_callback failed on an exact-length sink buffer"); |
||||||
|
} |
||||||
|
|
||||||
|
if (strncmp(dumped_to_string, s.buf, s.off) != 0) { |
||||||
|
json_decref(json); |
||||||
|
free(dumped_to_string); |
||||||
|
free(s.buf); |
||||||
|
fail("json_dump_callback and json_dumps did not produce identical output"); |
||||||
|
} |
||||||
|
|
||||||
|
s.off = 1; |
||||||
|
if (json_dump_callback(json, my_writer, &s, 0) != -1) { |
||||||
|
json_decref(json); |
||||||
|
free(dumped_to_string); |
||||||
|
free(s.buf); |
||||||
|
fail("json_dump_callback succeeded on a short buffer when it should have failed"); |
||||||
|
} |
||||||
|
|
||||||
|
json_decref(json); |
||||||
|
free(dumped_to_string); |
||||||
|
free(s.buf); |
||||||
|
} |
@ -0,0 +1,189 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static void test_equal_simple() |
||||||
|
{ |
||||||
|
json_t *value1, *value2; |
||||||
|
|
||||||
|
if(json_equal(NULL, NULL)) |
||||||
|
fail("json_equal fails for two NULLs"); |
||||||
|
|
||||||
|
value1 = json_true(); |
||||||
|
if(json_equal(value1, NULL) || json_equal(NULL, value1)) |
||||||
|
fail("json_equal fails for NULL"); |
||||||
|
|
||||||
|
/* this covers true, false and null as they are singletons */ |
||||||
|
if(!json_equal(value1, value1)) |
||||||
|
fail("identical objects are not equal"); |
||||||
|
json_decref(value1); |
||||||
|
|
||||||
|
/* integer */ |
||||||
|
value1 = json_integer(1); |
||||||
|
value2 = json_integer(1); |
||||||
|
if(!value1 || !value2) |
||||||
|
fail("unable to create integers"); |
||||||
|
if(!json_equal(value1, value2)) |
||||||
|
fail("json_equal fails for two equal integers"); |
||||||
|
json_decref(value2); |
||||||
|
|
||||||
|
value2 = json_integer(2); |
||||||
|
if(!value2) |
||||||
|
fail("unable to create an integer"); |
||||||
|
if(json_equal(value1, value2)) |
||||||
|
fail("json_equal fails for two inequal integers"); |
||||||
|
|
||||||
|
json_decref(value1); |
||||||
|
json_decref(value2); |
||||||
|
|
||||||
|
/* real */ |
||||||
|
value1 = json_real(1.2); |
||||||
|
value2 = json_real(1.2); |
||||||
|
if(!value1 || !value2) |
||||||
|
fail("unable to create reals"); |
||||||
|
if(!json_equal(value1, value2)) |
||||||
|
fail("json_equal fails for two equal reals"); |
||||||
|
json_decref(value2); |
||||||
|
|
||||||
|
value2 = json_real(3.141592); |
||||||
|
if(!value2) |
||||||
|
fail("unable to create an real"); |
||||||
|
if(json_equal(value1, value2)) |
||||||
|
fail("json_equal fails for two inequal reals"); |
||||||
|
|
||||||
|
json_decref(value1); |
||||||
|
json_decref(value2); |
||||||
|
|
||||||
|
/* string */ |
||||||
|
value1 = json_string("foo"); |
||||||
|
value2 = json_string("foo"); |
||||||
|
if(!value1 || !value2) |
||||||
|
fail("unable to create strings"); |
||||||
|
if(!json_equal(value1, value2)) |
||||||
|
fail("json_equal fails for two equal strings"); |
||||||
|
json_decref(value2); |
||||||
|
|
||||||
|
value2 = json_string("bar"); |
||||||
|
if(!value2) |
||||||
|
fail("unable to create an string"); |
||||||
|
if(json_equal(value1, value2)) |
||||||
|
fail("json_equal fails for two inequal strings"); |
||||||
|
|
||||||
|
json_decref(value1); |
||||||
|
json_decref(value2); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_equal_array() |
||||||
|
{ |
||||||
|
json_t *array1, *array2; |
||||||
|
|
||||||
|
array1 = json_array(); |
||||||
|
array2 = json_array(); |
||||||
|
if(!array1 || !array2) |
||||||
|
fail("unable to create arrays"); |
||||||
|
|
||||||
|
if(!json_equal(array1, array2)) |
||||||
|
fail("json_equal fails for two empty arrays"); |
||||||
|
|
||||||
|
json_array_append_new(array1, json_integer(1)); |
||||||
|
json_array_append_new(array2, json_integer(1)); |
||||||
|
json_array_append_new(array1, json_string("foo")); |
||||||
|
json_array_append_new(array2, json_string("foo")); |
||||||
|
json_array_append_new(array1, json_integer(2)); |
||||||
|
json_array_append_new(array2, json_integer(2)); |
||||||
|
if(!json_equal(array1, array2)) |
||||||
|
fail("json_equal fails for two equal arrays"); |
||||||
|
|
||||||
|
json_array_remove(array2, 2); |
||||||
|
if(json_equal(array1, array2)) |
||||||
|
fail("json_equal fails for two inequal arrays"); |
||||||
|
|
||||||
|
json_array_append_new(array2, json_integer(3)); |
||||||
|
if(json_equal(array1, array2)) |
||||||
|
fail("json_equal fails for two inequal arrays"); |
||||||
|
|
||||||
|
json_decref(array1); |
||||||
|
json_decref(array2); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_equal_object() |
||||||
|
{ |
||||||
|
json_t *object1, *object2; |
||||||
|
|
||||||
|
object1 = json_object(); |
||||||
|
object2 = json_object(); |
||||||
|
if(!object1 || !object2) |
||||||
|
fail("unable to create objects"); |
||||||
|
|
||||||
|
if(!json_equal(object1, object2)) |
||||||
|
fail("json_equal fails for two empty objects"); |
||||||
|
|
||||||
|
json_object_set_new(object1, "a", json_integer(1)); |
||||||
|
json_object_set_new(object2, "a", json_integer(1)); |
||||||
|
json_object_set_new(object1, "b", json_string("foo")); |
||||||
|
json_object_set_new(object2, "b", json_string("foo")); |
||||||
|
json_object_set_new(object1, "c", json_integer(2)); |
||||||
|
json_object_set_new(object2, "c", json_integer(2)); |
||||||
|
if(!json_equal(object1, object2)) |
||||||
|
fail("json_equal fails for two equal objects"); |
||||||
|
|
||||||
|
json_object_del(object2, "c"); |
||||||
|
if(json_equal(object1, object2)) |
||||||
|
fail("json_equal fails for two inequal objects"); |
||||||
|
|
||||||
|
json_object_set_new(object2, "c", json_integer(3)); |
||||||
|
if(json_equal(object1, object2)) |
||||||
|
fail("json_equal fails for two inequal objects"); |
||||||
|
|
||||||
|
json_object_del(object2, "c"); |
||||||
|
json_object_set_new(object2, "d", json_integer(2)); |
||||||
|
if(json_equal(object1, object2)) |
||||||
|
fail("json_equal fails for two inequal objects"); |
||||||
|
|
||||||
|
json_decref(object1); |
||||||
|
json_decref(object2); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_equal_complex() |
||||||
|
{ |
||||||
|
json_t *value1, *value2; |
||||||
|
|
||||||
|
const char *complex_json = |
||||||
|
"{" |
||||||
|
" \"integer\": 1, " |
||||||
|
" \"real\": 3.141592, " |
||||||
|
" \"string\": \"foobar\", " |
||||||
|
" \"true\": true, " |
||||||
|
" \"object\": {" |
||||||
|
" \"array-in-object\": [1,true,\"foo\",{}]," |
||||||
|
" \"object-in-object\": {\"foo\": \"bar\"}" |
||||||
|
" }," |
||||||
|
" \"array\": [\"foo\", false, null, 1.234]" |
||||||
|
"}"; |
||||||
|
|
||||||
|
value1 = json_loads(complex_json, 0, NULL); |
||||||
|
value2 = json_loads(complex_json, 0, NULL); |
||||||
|
if(!value1 || !value2) |
||||||
|
fail("unable to parse JSON"); |
||||||
|
if(!json_equal(value1, value2)) |
||||||
|
fail("json_equal fails for two inequal strings"); |
||||||
|
|
||||||
|
json_decref(value1); |
||||||
|
json_decref(value2); |
||||||
|
|
||||||
|
/* TODO: There's no negative test case here */ |
||||||
|
} |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
test_equal_simple(); |
||||||
|
test_equal_array(); |
||||||
|
test_equal_object(); |
||||||
|
test_equal_complex(); |
||||||
|
} |
@ -0,0 +1,166 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
#include <string.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static void file_not_found() |
||||||
|
{ |
||||||
|
json_t *json; |
||||||
|
json_error_t error; |
||||||
|
char *pos; |
||||||
|
|
||||||
|
json = json_load_file("/path/to/nonexistent/file.json", 0, &error); |
||||||
|
if(json) |
||||||
|
fail("json_load_file returned non-NULL for a nonexistent file"); |
||||||
|
if(error.line != -1) |
||||||
|
fail("json_load_file returned an invalid line number"); |
||||||
|
|
||||||
|
/* The error message is locale specific, only check the beginning
|
||||||
|
of the error message. */ |
||||||
|
|
||||||
|
pos = strchr(error.text, ':'); |
||||||
|
if(!pos) |
||||||
|
fail("json_load_file returne an invalid error message"); |
||||||
|
|
||||||
|
*pos = '\0'; |
||||||
|
|
||||||
|
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json") != 0) |
||||||
|
fail("json_load_file returned an invalid error message"); |
||||||
|
} |
||||||
|
|
||||||
|
static void reject_duplicates() |
||||||
|
{ |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error)) |
||||||
|
fail("json_loads did not detect a duplicate key"); |
||||||
|
check_error("duplicate object key near '\"foo\"'", "<string>", 1, 16, 16); |
||||||
|
} |
||||||
|
|
||||||
|
static void disable_eof_check() |
||||||
|
{ |
||||||
|
json_error_t error; |
||||||
|
json_t *json; |
||||||
|
|
||||||
|
const char *text = "{\"foo\": 1} garbage"; |
||||||
|
|
||||||
|
if(json_loads(text, 0, &error)) |
||||||
|
fail("json_loads did not detect garbage after JSON text"); |
||||||
|
check_error("end of file expected near 'garbage'", "<string>", 1, 18, 18); |
||||||
|
|
||||||
|
json = json_loads(text, JSON_DISABLE_EOF_CHECK, &error); |
||||||
|
if(!json) |
||||||
|
fail("json_loads failed with JSON_DISABLE_EOF_CHECK"); |
||||||
|
|
||||||
|
json_decref(json); |
||||||
|
} |
||||||
|
|
||||||
|
static void decode_any() |
||||||
|
{ |
||||||
|
json_t *json; |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
json = json_loads("\"foo\"", JSON_DECODE_ANY, &error); |
||||||
|
if (!json || !json_is_string(json)) |
||||||
|
fail("json_load decoded any failed - string"); |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
json = json_loads("42", JSON_DECODE_ANY, &error); |
||||||
|
if (!json || !json_is_integer(json)) |
||||||
|
fail("json_load decoded any failed - integer"); |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
json = json_loads("true", JSON_DECODE_ANY, &error); |
||||||
|
if (!json || !json_is_true(json)) |
||||||
|
fail("json_load decoded any failed - boolean"); |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
json = json_loads("null", JSON_DECODE_ANY, &error); |
||||||
|
if (!json || !json_is_null(json)) |
||||||
|
fail("json_load decoded any failed - null"); |
||||||
|
json_decref(json); |
||||||
|
} |
||||||
|
|
||||||
|
static void decode_int_as_real() |
||||||
|
{ |
||||||
|
json_t *json; |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
#if JSON_INTEGER_IS_LONG_LONG |
||||||
|
const char *imprecise; |
||||||
|
json_int_t expected; |
||||||
|
#endif |
||||||
|
|
||||||
|
json = json_loads("42", JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error); |
||||||
|
if (!json || !json_is_real(json) || json_real_value(json) != 42.0) |
||||||
|
fail("json_load decode int as real failed - int"); |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
#if JSON_INTEGER_IS_LONG_LONG |
||||||
|
/* This number cannot be represented exactly by a double */ |
||||||
|
imprecise = "9007199254740993"; |
||||||
|
expected = 9007199254740992ll; |
||||||
|
|
||||||
|
json = json_loads(imprecise, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, |
||||||
|
&error); |
||||||
|
if (!json || !json_is_real(json) || expected != (json_int_t)json_real_value(json)) |
||||||
|
fail("json_load decode int as real failed - expected imprecision"); |
||||||
|
json_decref(json); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
static void load_wrong_args() |
||||||
|
{ |
||||||
|
json_t *json; |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
json = json_loads(NULL, 0, &error); |
||||||
|
if (json) |
||||||
|
fail("json_loads should return NULL if the first argument is NULL"); |
||||||
|
|
||||||
|
json = json_loadb(NULL, 0, 0, &error); |
||||||
|
if (json) |
||||||
|
fail("json_loadb should return NULL if the first argument is NULL"); |
||||||
|
|
||||||
|
json = json_loadf(NULL, 0, &error); |
||||||
|
if (json) |
||||||
|
fail("json_loadf should return NULL if the first argument is NULL"); |
||||||
|
|
||||||
|
json = json_load_file(NULL, 0, &error); |
||||||
|
if (json) |
||||||
|
fail("json_loadf should return NULL if the first argument is NULL"); |
||||||
|
} |
||||||
|
|
||||||
|
static void position() |
||||||
|
{ |
||||||
|
json_t *json; |
||||||
|
size_t flags = JSON_DISABLE_EOF_CHECK; |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
json = json_loads("{\"foo\": \"bar\"}", 0, &error); |
||||||
|
if(error.position != 14) |
||||||
|
fail("json_loads returned a wrong position"); |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
json = json_loads("{\"foo\": \"bar\"} baz quux", flags, &error); |
||||||
|
if(error.position != 14) |
||||||
|
fail("json_loads returned a wrong position"); |
||||||
|
json_decref(json); |
||||||
|
} |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
file_not_found(); |
||||||
|
reject_duplicates(); |
||||||
|
disable_eof_check(); |
||||||
|
decode_any(); |
||||||
|
decode_int_as_real(); |
||||||
|
load_wrong_args(); |
||||||
|
position(); |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
#include <string.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
struct my_source { |
||||||
|
const char *buf; |
||||||
|
size_t off; |
||||||
|
size_t cap; |
||||||
|
}; |
||||||
|
|
||||||
|
static const char my_str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]"; |
||||||
|
|
||||||
|
static size_t greedy_reader(void *buf, size_t buflen, void *arg) |
||||||
|
{ |
||||||
|
struct my_source *s = arg; |
||||||
|
if (buflen > s->cap - s->off) |
||||||
|
buflen = s->cap - s->off; |
||||||
|
if (buflen > 0) { |
||||||
|
memcpy(buf, s->buf + s->off, buflen); |
||||||
|
s->off += buflen; |
||||||
|
return buflen; |
||||||
|
} else { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
struct my_source s; |
||||||
|
json_t *json; |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
s.off = 0; |
||||||
|
s.cap = strlen(my_str); |
||||||
|
s.buf = my_str; |
||||||
|
|
||||||
|
json = json_load_callback(greedy_reader, &s, 0, &error); |
||||||
|
|
||||||
|
if (!json) |
||||||
|
fail("json_load_callback failed on a valid callback"); |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
s.off = 0; |
||||||
|
s.cap = strlen(my_str) - 1; |
||||||
|
s.buf = my_str; |
||||||
|
|
||||||
|
json = json_load_callback(greedy_reader, &s, 0, &error); |
||||||
|
if (json) { |
||||||
|
json_decref(json); |
||||||
|
fail("json_load_callback should have failed on an incomplete stream, but it didn't"); |
||||||
|
} |
||||||
|
if (strcmp(error.source, "<callback>") != 0) { |
||||||
|
fail("json_load_callback returned an invalid error source"); |
||||||
|
} |
||||||
|
if (strcmp(error.text, "']' expected near end of file") != 0) { |
||||||
|
fail("json_load_callback returned an invalid error message for an unclosed top-level array"); |
||||||
|
} |
||||||
|
|
||||||
|
json = json_load_callback(NULL, NULL, 0, &error); |
||||||
|
if (json) { |
||||||
|
json_decref(json); |
||||||
|
fail("json_load_callback should have failed on NULL load callback, but it didn't"); |
||||||
|
} |
||||||
|
if (strcmp(error.text, "wrong arguments") != 0) { |
||||||
|
fail("json_load_callback returned an invalid error message for a NULL load callback"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
#include <string.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
json_t *json; |
||||||
|
json_error_t error; |
||||||
|
const char str[] = "[\"A\", {\"B\": \"C\"}, 1, 2, 3]garbage"; |
||||||
|
size_t len = strlen(str) - strlen("garbage"); |
||||||
|
|
||||||
|
json = json_loadb(str, len, 0, &error); |
||||||
|
if(!json) { |
||||||
|
fail("json_loadb failed on a valid JSON buffer"); |
||||||
|
} |
||||||
|
json_decref(json); |
||||||
|
|
||||||
|
json = json_loadb(str, len - 1, 0, &error); |
||||||
|
if (json) { |
||||||
|
json_decref(json); |
||||||
|
fail("json_loadb should have failed on an incomplete buffer, but it didn't"); |
||||||
|
} |
||||||
|
if(error.line != 1) { |
||||||
|
fail("json_loadb returned an invalid line number on fail"); |
||||||
|
} |
||||||
|
if(strcmp(error.text, "']' expected near end of file") != 0) { |
||||||
|
fail("json_loadb returned an invalid error message for an unclosed top-level array"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
#include <string.h> |
||||||
|
#include <jansson.h> |
||||||
|
|
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static int malloc_called = 0; |
||||||
|
static int free_called = 0; |
||||||
|
|
||||||
|
/* helper */ |
||||||
|
static void create_and_free_complex_object() |
||||||
|
{ |
||||||
|
json_t *obj; |
||||||
|
|
||||||
|
obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]", |
||||||
|
"foo", 42, |
||||||
|
"bar", |
||||||
|
"baz", 1, |
||||||
|
"qux", 0, |
||||||
|
"alice", "bar", "baz", |
||||||
|
"bob", 9, 8, 7); |
||||||
|
|
||||||
|
json_decref(obj); |
||||||
|
} |
||||||
|
|
||||||
|
static void *my_malloc(size_t size) |
||||||
|
{ |
||||||
|
malloc_called += 1; |
||||||
|
return malloc(size); |
||||||
|
} |
||||||
|
|
||||||
|
static void my_free(void *ptr) |
||||||
|
{ |
||||||
|
free_called += 1; |
||||||
|
free(ptr); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_simple() |
||||||
|
{ |
||||||
|
json_set_alloc_funcs(my_malloc, my_free); |
||||||
|
create_and_free_complex_object(); |
||||||
|
|
||||||
|
if(malloc_called != 20 || free_called != 20) |
||||||
|
fail("Custom allocation failed"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test the secure memory functions code given in the API reference |
||||||
|
documentation, but by using plain memset instead of |
||||||
|
guaranteed_memset(). |
||||||
|
*/ |
||||||
|
|
||||||
|
static void *secure_malloc(size_t size) |
||||||
|
{ |
||||||
|
/* Store the memory area size in the beginning of the block */ |
||||||
|
void *ptr = malloc(size + 8); |
||||||
|
*((size_t *)ptr) = size; |
||||||
|
return (char *)ptr + 8; |
||||||
|
} |
||||||
|
|
||||||
|
static void secure_free(void *ptr) |
||||||
|
{ |
||||||
|
size_t size; |
||||||
|
|
||||||
|
ptr = (char *)ptr - 8; |
||||||
|
size = *((size_t *)ptr); |
||||||
|
|
||||||
|
/*guaranteed_*/memset(ptr, 0, size); |
||||||
|
free(ptr); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_secure_funcs(void) |
||||||
|
{ |
||||||
|
json_set_alloc_funcs(secure_malloc, secure_free); |
||||||
|
create_and_free_complex_object(); |
||||||
|
} |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
test_simple(); |
||||||
|
test_secure_funcs(); |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <math.h> |
||||||
|
#include <jansson.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
json_t *integer, *real; |
||||||
|
json_int_t i; |
||||||
|
double d; |
||||||
|
|
||||||
|
integer = json_integer(5); |
||||||
|
real = json_real(100.1); |
||||||
|
|
||||||
|
if(!integer) |
||||||
|
fail("unable to create integer"); |
||||||
|
if(!real) |
||||||
|
fail("unable to create real"); |
||||||
|
|
||||||
|
i = json_integer_value(integer); |
||||||
|
if(i != 5) |
||||||
|
fail("wrong integer value"); |
||||||
|
|
||||||
|
d = json_real_value(real); |
||||||
|
if(d != 100.1) |
||||||
|
fail("wrong real value"); |
||||||
|
|
||||||
|
d = json_number_value(integer); |
||||||
|
if(d != 5.0) |
||||||
|
fail("wrong number value"); |
||||||
|
d = json_number_value(real); |
||||||
|
if(d != 100.1) |
||||||
|
fail("wrong number value"); |
||||||
|
|
||||||
|
json_decref(integer); |
||||||
|
json_decref(real); |
||||||
|
|
||||||
|
#ifdef NAN |
||||||
|
real = json_real(NAN); |
||||||
|
if(real != NULL) |
||||||
|
fail("could construct a real from NaN"); |
||||||
|
|
||||||
|
real = json_real(1.0); |
||||||
|
if(json_real_set(real, NAN) != -1) |
||||||
|
fail("could set a real to NaN"); |
||||||
|
|
||||||
|
if(json_real_value(real) != 1.0) |
||||||
|
fail("real value changed unexpectedly"); |
||||||
|
|
||||||
|
json_decref(real); |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef INFINITY |
||||||
|
real = json_real(INFINITY); |
||||||
|
if(real != NULL) |
||||||
|
fail("could construct a real from Inf"); |
||||||
|
|
||||||
|
real = json_real(1.0); |
||||||
|
if(json_real_set(real, INFINITY) != -1) |
||||||
|
fail("could set a real to Inf"); |
||||||
|
|
||||||
|
if(json_real_value(real) != 1.0) |
||||||
|
fail("real value changed unexpectedly"); |
||||||
|
|
||||||
|
json_decref(real); |
||||||
|
#endif |
||||||
|
} |
@ -0,0 +1,511 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
#include <string.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static void test_clear() |
||||||
|
{ |
||||||
|
json_t *object, *ten; |
||||||
|
|
||||||
|
object = json_object(); |
||||||
|
ten = json_integer(10); |
||||||
|
|
||||||
|
if(!object) |
||||||
|
fail("unable to create object"); |
||||||
|
if(!ten) |
||||||
|
fail("unable to create integer"); |
||||||
|
|
||||||
|
if(json_object_set(object, "a", ten) || |
||||||
|
json_object_set(object, "b", ten) || |
||||||
|
json_object_set(object, "c", ten) || |
||||||
|
json_object_set(object, "d", ten) || |
||||||
|
json_object_set(object, "e", ten)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
if(json_object_size(object) != 5) |
||||||
|
fail("invalid size"); |
||||||
|
|
||||||
|
json_object_clear(object); |
||||||
|
|
||||||
|
if(json_object_size(object) != 0) |
||||||
|
fail("invalid size after clear"); |
||||||
|
|
||||||
|
json_decref(ten); |
||||||
|
json_decref(object); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_update() |
||||||
|
{ |
||||||
|
json_t *object, *other, *nine, *ten; |
||||||
|
|
||||||
|
object = json_object(); |
||||||
|
other = json_object(); |
||||||
|
|
||||||
|
nine = json_integer(9); |
||||||
|
ten = json_integer(10); |
||||||
|
|
||||||
|
if(!object || !other) |
||||||
|
fail("unable to create object"); |
||||||
|
if(!nine || !ten) |
||||||
|
fail("unable to create integer"); |
||||||
|
|
||||||
|
|
||||||
|
/* update an empty object with an empty object */ |
||||||
|
|
||||||
|
if(json_object_update(object, other)) |
||||||
|
fail("unable to update an emtpy object with an empty object"); |
||||||
|
|
||||||
|
if(json_object_size(object) != 0) |
||||||
|
fail("invalid size after update"); |
||||||
|
|
||||||
|
if(json_object_size(other) != 0) |
||||||
|
fail("invalid size for updater after update"); |
||||||
|
|
||||||
|
|
||||||
|
/* update an empty object with a nonempty object */ |
||||||
|
|
||||||
|
if(json_object_set(other, "a", ten) || |
||||||
|
json_object_set(other, "b", ten) || |
||||||
|
json_object_set(other, "c", ten) || |
||||||
|
json_object_set(other, "d", ten) || |
||||||
|
json_object_set(other, "e", ten)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
if(json_object_update(object, other)) |
||||||
|
fail("unable to update an empty object"); |
||||||
|
|
||||||
|
if(json_object_size(object) != 5) |
||||||
|
fail("invalid size after update"); |
||||||
|
|
||||||
|
if(json_object_get(object, "a") != ten || |
||||||
|
json_object_get(object, "b") != ten || |
||||||
|
json_object_get(object, "c") != ten || |
||||||
|
json_object_get(object, "d") != ten || |
||||||
|
json_object_get(object, "e") != ten) |
||||||
|
fail("update works incorrectly"); |
||||||
|
|
||||||
|
|
||||||
|
/* perform the same update again */ |
||||||
|
|
||||||
|
if(json_object_update(object, other)) |
||||||
|
fail("unable to update a non-empty object"); |
||||||
|
|
||||||
|
if(json_object_size(object) != 5) |
||||||
|
fail("invalid size after update"); |
||||||
|
|
||||||
|
if(json_object_get(object, "a") != ten || |
||||||
|
json_object_get(object, "b") != ten || |
||||||
|
json_object_get(object, "c") != ten || |
||||||
|
json_object_get(object, "d") != ten || |
||||||
|
json_object_get(object, "e") != ten) |
||||||
|
fail("update works incorrectly"); |
||||||
|
|
||||||
|
|
||||||
|
/* update a nonempty object with a nonempty object with both old
|
||||||
|
and new keys */ |
||||||
|
|
||||||
|
if(json_object_clear(other)) |
||||||
|
fail("clear failed"); |
||||||
|
|
||||||
|
if(json_object_set(other, "a", nine) || |
||||||
|
json_object_set(other, "b", nine) || |
||||||
|
json_object_set(other, "f", nine) || |
||||||
|
json_object_set(other, "g", nine) || |
||||||
|
json_object_set(other, "h", nine)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
if(json_object_update(object, other)) |
||||||
|
fail("unable to update a nonempty object"); |
||||||
|
|
||||||
|
if(json_object_size(object) != 8) |
||||||
|
fail("invalid size after update"); |
||||||
|
|
||||||
|
if(json_object_get(object, "a") != nine || |
||||||
|
json_object_get(object, "b") != nine || |
||||||
|
json_object_get(object, "f") != nine || |
||||||
|
json_object_get(object, "g") != nine || |
||||||
|
json_object_get(object, "h") != nine) |
||||||
|
fail("update works incorrectly"); |
||||||
|
|
||||||
|
json_decref(nine); |
||||||
|
json_decref(ten); |
||||||
|
json_decref(other); |
||||||
|
json_decref(object); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_conditional_updates() |
||||||
|
{ |
||||||
|
json_t *object, *other; |
||||||
|
|
||||||
|
object = json_pack("{sisi}", "foo", 1, "bar", 2); |
||||||
|
other = json_pack("{sisi}", "foo", 3, "baz", 4); |
||||||
|
|
||||||
|
if(json_object_update_existing(object, other)) |
||||||
|
fail("json_object_update_existing failed"); |
||||||
|
|
||||||
|
if(json_object_size(object) != 2) |
||||||
|
fail("json_object_update_existing added new items"); |
||||||
|
|
||||||
|
if(json_integer_value(json_object_get(object, "foo")) != 3) |
||||||
|
fail("json_object_update_existing failed to update existing key"); |
||||||
|
|
||||||
|
if(json_integer_value(json_object_get(object, "bar")) != 2) |
||||||
|
fail("json_object_update_existing updated wrong key"); |
||||||
|
|
||||||
|
json_decref(object); |
||||||
|
|
||||||
|
object = json_pack("{sisi}", "foo", 1, "bar", 2); |
||||||
|
|
||||||
|
if(json_object_update_missing(object, other)) |
||||||
|
fail("json_object_update_missing failed"); |
||||||
|
|
||||||
|
if(json_object_size(object) != 3) |
||||||
|
fail("json_object_update_missing didn't add new items"); |
||||||
|
|
||||||
|
if(json_integer_value(json_object_get(object, "foo")) != 1) |
||||||
|
fail("json_object_update_missing updated existing key"); |
||||||
|
|
||||||
|
if(json_integer_value(json_object_get(object, "bar")) != 2) |
||||||
|
fail("json_object_update_missing updated wrong key"); |
||||||
|
|
||||||
|
if(json_integer_value(json_object_get(object, "baz")) != 4) |
||||||
|
fail("json_object_update_missing didn't add new items"); |
||||||
|
|
||||||
|
json_decref(object); |
||||||
|
json_decref(other); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_circular() |
||||||
|
{ |
||||||
|
json_t *object1, *object2; |
||||||
|
|
||||||
|
object1 = json_object(); |
||||||
|
object2 = json_object(); |
||||||
|
if(!object1 || !object2) |
||||||
|
fail("unable to create object"); |
||||||
|
|
||||||
|
/* the simple case is checked */ |
||||||
|
if(json_object_set(object1, "a", object1) == 0) |
||||||
|
fail("able to set self"); |
||||||
|
|
||||||
|
/* create circular references */ |
||||||
|
if(json_object_set(object1, "a", object2) || |
||||||
|
json_object_set(object2, "a", object1)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
/* circularity is detected when dumping */ |
||||||
|
if(json_dumps(object1, 0) != NULL) |
||||||
|
fail("able to dump circulars"); |
||||||
|
|
||||||
|
/* decref twice to deal with the circular references */ |
||||||
|
json_decref(object1); |
||||||
|
json_decref(object2); |
||||||
|
json_decref(object1); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_set_nocheck() |
||||||
|
{ |
||||||
|
json_t *object, *string; |
||||||
|
|
||||||
|
object = json_object(); |
||||||
|
string = json_string("bar"); |
||||||
|
|
||||||
|
if(!object) |
||||||
|
fail("unable to create object"); |
||||||
|
if(!string) |
||||||
|
fail("unable to create string"); |
||||||
|
|
||||||
|
if(json_object_set_nocheck(object, "foo", string)) |
||||||
|
fail("json_object_set_nocheck failed"); |
||||||
|
if(json_object_get(object, "foo") != string) |
||||||
|
fail("json_object_get after json_object_set_nocheck failed"); |
||||||
|
|
||||||
|
/* invalid UTF-8 in key */ |
||||||
|
if(json_object_set_nocheck(object, "a\xefz", string)) |
||||||
|
fail("json_object_set_nocheck failed for invalid UTF-8"); |
||||||
|
if(json_object_get(object, "a\xefz") != string) |
||||||
|
fail("json_object_get after json_object_set_nocheck failed"); |
||||||
|
|
||||||
|
if(json_object_set_new_nocheck(object, "bax", json_integer(123))) |
||||||
|
fail("json_object_set_new_nocheck failed"); |
||||||
|
if(json_integer_value(json_object_get(object, "bax")) != 123) |
||||||
|
fail("json_object_get after json_object_set_new_nocheck failed"); |
||||||
|
|
||||||
|
/* invalid UTF-8 in key */ |
||||||
|
if(json_object_set_new_nocheck(object, "asdf\xfe", json_integer(321))) |
||||||
|
fail("json_object_set_new_nocheck failed for invalid UTF-8"); |
||||||
|
if(json_integer_value(json_object_get(object, "asdf\xfe")) != 321) |
||||||
|
fail("json_object_get after json_object_set_new_nocheck failed"); |
||||||
|
|
||||||
|
json_decref(string); |
||||||
|
json_decref(object); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_iterators() |
||||||
|
{ |
||||||
|
json_t *object, *foo, *bar, *baz; |
||||||
|
void *iter; |
||||||
|
|
||||||
|
if(json_object_iter(NULL)) |
||||||
|
fail("able to iterate over NULL"); |
||||||
|
|
||||||
|
if(json_object_iter_next(NULL, NULL)) |
||||||
|
fail("able to increment an iterator on a NULL object"); |
||||||
|
|
||||||
|
object = json_object(); |
||||||
|
foo = json_string("foo"); |
||||||
|
bar = json_string("bar"); |
||||||
|
baz = json_string("baz"); |
||||||
|
if(!object || !foo || !bar || !bar) |
||||||
|
fail("unable to create values"); |
||||||
|
|
||||||
|
if(json_object_iter_next(object, NULL)) |
||||||
|
fail("able to increment a NULL iterator"); |
||||||
|
|
||||||
|
if(json_object_set(object, "a", foo) || |
||||||
|
json_object_set(object, "b", bar) || |
||||||
|
json_object_set(object, "c", baz)) |
||||||
|
fail("unable to populate object"); |
||||||
|
|
||||||
|
iter = json_object_iter(object); |
||||||
|
if(!iter) |
||||||
|
fail("unable to get iterator"); |
||||||
|
if(strcmp(json_object_iter_key(iter), "a")) |
||||||
|
fail("iterating failed: wrong key"); |
||||||
|
if(json_object_iter_value(iter) != foo) |
||||||
|
fail("iterating failed: wrong value"); |
||||||
|
|
||||||
|
iter = json_object_iter_next(object, iter); |
||||||
|
if(!iter) |
||||||
|
fail("unable to increment iterator"); |
||||||
|
if(strcmp(json_object_iter_key(iter), "b")) |
||||||
|
fail("iterating failed: wrong key"); |
||||||
|
if(json_object_iter_value(iter) != bar) |
||||||
|
fail("iterating failed: wrong value"); |
||||||
|
|
||||||
|
iter = json_object_iter_next(object, iter); |
||||||
|
if(!iter) |
||||||
|
fail("unable to increment iterator"); |
||||||
|
if(strcmp(json_object_iter_key(iter), "c")) |
||||||
|
fail("iterating failed: wrong key"); |
||||||
|
if(json_object_iter_value(iter) != baz) |
||||||
|
fail("iterating failed: wrong value"); |
||||||
|
|
||||||
|
if(json_object_iter_next(object, iter) != NULL) |
||||||
|
fail("able to iterate over the end"); |
||||||
|
|
||||||
|
if(json_object_iter_at(object, "foo")) |
||||||
|
fail("json_object_iter_at() succeeds for non-existent key"); |
||||||
|
|
||||||
|
iter = json_object_iter_at(object, "b"); |
||||||
|
if(!iter) |
||||||
|
fail("json_object_iter_at() fails for an existing key"); |
||||||
|
|
||||||
|
if(strcmp(json_object_iter_key(iter), "b")) |
||||||
|
fail("iterating failed: wrong key"); |
||||||
|
if(json_object_iter_value(iter) != bar) |
||||||
|
fail("iterating failed: wrong value"); |
||||||
|
|
||||||
|
iter = json_object_iter_next(object, iter); |
||||||
|
if(!iter) |
||||||
|
fail("unable to increment iterator"); |
||||||
|
if(strcmp(json_object_iter_key(iter), "c")) |
||||||
|
fail("iterating failed: wrong key"); |
||||||
|
if(json_object_iter_value(iter) != baz) |
||||||
|
fail("iterating failed: wrong value"); |
||||||
|
|
||||||
|
if(json_object_iter_set(object, iter, bar)) |
||||||
|
fail("unable to set value at iterator"); |
||||||
|
|
||||||
|
if(strcmp(json_object_iter_key(iter), "c")) |
||||||
|
fail("json_object_iter_key() fails after json_object_iter_set()"); |
||||||
|
if(json_object_iter_value(iter) != bar) |
||||||
|
fail("json_object_iter_value() fails after json_object_iter_set()"); |
||||||
|
if(json_object_get(object, "c") != bar) |
||||||
|
fail("json_object_get() fails after json_object_iter_set()"); |
||||||
|
|
||||||
|
json_decref(object); |
||||||
|
json_decref(foo); |
||||||
|
json_decref(bar); |
||||||
|
json_decref(baz); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_misc() |
||||||
|
{ |
||||||
|
json_t *object, *string, *other_string, *value; |
||||||
|
|
||||||
|
object = json_object(); |
||||||
|
string = json_string("test"); |
||||||
|
other_string = json_string("other"); |
||||||
|
|
||||||
|
if(!object) |
||||||
|
fail("unable to create object"); |
||||||
|
if(!string || !other_string) |
||||||
|
fail("unable to create string"); |
||||||
|
|
||||||
|
if(json_object_get(object, "a")) |
||||||
|
fail("value for nonexisting key"); |
||||||
|
|
||||||
|
if(json_object_set(object, "a", string)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
if(!json_object_set(object, NULL, string)) |
||||||
|
fail("able to set NULL key"); |
||||||
|
|
||||||
|
if(!json_object_set(object, "a", NULL)) |
||||||
|
fail("able to set NULL value"); |
||||||
|
|
||||||
|
/* invalid UTF-8 in key */ |
||||||
|
if(!json_object_set(object, "a\xefz", string)) |
||||||
|
fail("able to set invalid unicode key"); |
||||||
|
|
||||||
|
value = json_object_get(object, "a"); |
||||||
|
if(!value) |
||||||
|
fail("no value for existing key"); |
||||||
|
if(value != string) |
||||||
|
fail("got different value than what was added"); |
||||||
|
|
||||||
|
/* "a", "lp" and "px" collide in a five-bucket hashtable */ |
||||||
|
if(json_object_set(object, "b", string) || |
||||||
|
json_object_set(object, "lp", string) || |
||||||
|
json_object_set(object, "px", string)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
value = json_object_get(object, "a"); |
||||||
|
if(!value) |
||||||
|
fail("no value for existing key"); |
||||||
|
if(value != string) |
||||||
|
fail("got different value than what was added"); |
||||||
|
|
||||||
|
if(json_object_set(object, "a", other_string)) |
||||||
|
fail("unable to replace an existing key"); |
||||||
|
|
||||||
|
value = json_object_get(object, "a"); |
||||||
|
if(!value) |
||||||
|
fail("no value for existing key"); |
||||||
|
if(value != other_string) |
||||||
|
fail("got different value than what was set"); |
||||||
|
|
||||||
|
if(!json_object_del(object, "nonexisting")) |
||||||
|
fail("able to delete a nonexisting key"); |
||||||
|
|
||||||
|
if(json_object_del(object, "px")) |
||||||
|
fail("unable to delete an existing key"); |
||||||
|
|
||||||
|
if(json_object_del(object, "a")) |
||||||
|
fail("unable to delete an existing key"); |
||||||
|
|
||||||
|
if(json_object_del(object, "lp")) |
||||||
|
fail("unable to delete an existing key"); |
||||||
|
|
||||||
|
|
||||||
|
/* add many keys to initiate rehashing */ |
||||||
|
|
||||||
|
if(json_object_set(object, "a", string)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
if(json_object_set(object, "lp", string)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
if(json_object_set(object, "px", string)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
if(json_object_set(object, "c", string)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
if(json_object_set(object, "d", string)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
if(json_object_set(object, "e", string)) |
||||||
|
fail("unable to set value"); |
||||||
|
|
||||||
|
|
||||||
|
if(json_object_set_new(object, "foo", json_integer(123))) |
||||||
|
fail("unable to set new value"); |
||||||
|
|
||||||
|
value = json_object_get(object, "foo"); |
||||||
|
if(!json_is_integer(value) || json_integer_value(value) != 123) |
||||||
|
fail("json_object_set_new works incorrectly"); |
||||||
|
|
||||||
|
if(!json_object_set_new(object, NULL, json_integer(432))) |
||||||
|
fail("able to set_new NULL key"); |
||||||
|
|
||||||
|
if(!json_object_set_new(object, "foo", NULL)) |
||||||
|
fail("able to set_new NULL value"); |
||||||
|
|
||||||
|
json_decref(string); |
||||||
|
json_decref(other_string); |
||||||
|
json_decref(object); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_preserve_order() |
||||||
|
{ |
||||||
|
json_t *object; |
||||||
|
char *result; |
||||||
|
|
||||||
|
const char *expected = "{\"foobar\": 1, \"bazquux\": 6, \"lorem ipsum\": 3, \"sit amet\": 5, \"helicopter\": 7}"; |
||||||
|
|
||||||
|
object = json_object(); |
||||||
|
|
||||||
|
json_object_set_new(object, "foobar", json_integer(1)); |
||||||
|
json_object_set_new(object, "bazquux", json_integer(2)); |
||||||
|
json_object_set_new(object, "lorem ipsum", json_integer(3)); |
||||||
|
json_object_set_new(object, "dolor", json_integer(4)); |
||||||
|
json_object_set_new(object, "sit amet", json_integer(5)); |
||||||
|
|
||||||
|
/* changing a value should preserve the order */ |
||||||
|
json_object_set_new(object, "bazquux", json_integer(6)); |
||||||
|
|
||||||
|
/* deletion shouldn't change the order of others */ |
||||||
|
json_object_del(object, "dolor"); |
||||||
|
|
||||||
|
/* add a new item just to make sure */ |
||||||
|
json_object_set_new(object, "helicopter", json_integer(7)); |
||||||
|
|
||||||
|
result = json_dumps(object, JSON_PRESERVE_ORDER); |
||||||
|
|
||||||
|
if(strcmp(expected, result) != 0) { |
||||||
|
fprintf(stderr, "%s != %s", expected, result); |
||||||
|
fail("JSON_PRESERVE_ORDER doesn't work"); |
||||||
|
} |
||||||
|
|
||||||
|
free(result); |
||||||
|
json_decref(object); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_object_foreach() |
||||||
|
{ |
||||||
|
const char *key; |
||||||
|
json_t *object1, *object2, *value; |
||||||
|
|
||||||
|
object1 = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3); |
||||||
|
object2 = json_object(); |
||||||
|
|
||||||
|
json_object_foreach(object1, key, value) |
||||||
|
json_object_set(object2, key, value); |
||||||
|
|
||||||
|
if(!json_equal(object1, object2)) |
||||||
|
fail("json_object_foreach failed to iterate all key-value pairs"); |
||||||
|
|
||||||
|
json_decref(object1); |
||||||
|
json_decref(object2); |
||||||
|
} |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
test_misc(); |
||||||
|
test_clear(); |
||||||
|
test_update(); |
||||||
|
test_conditional_updates(); |
||||||
|
test_circular(); |
||||||
|
test_set_nocheck(); |
||||||
|
test_iterators(); |
||||||
|
test_preserve_order(); |
||||||
|
test_object_foreach(); |
||||||
|
} |
@ -0,0 +1,283 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* Copyright (c) 2010-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H |
||||||
|
#include <config.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <jansson_config.h> |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
#include <jansson.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
json_t *value; |
||||||
|
int i; |
||||||
|
char buffer[4] = {'t', 'e', 's', 't'}; |
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple, valid json_pack cases |
||||||
|
*/ |
||||||
|
/* true */ |
||||||
|
value = json_pack("b", 1); |
||||||
|
if(!json_is_true(value)) |
||||||
|
fail("json_pack boolean failed"); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("json_pack boolean refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* false */ |
||||||
|
value = json_pack("b", 0); |
||||||
|
if(!json_is_false(value)) |
||||||
|
fail("json_pack boolean failed"); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("json_pack boolean refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* null */ |
||||||
|
value = json_pack("n"); |
||||||
|
if(!json_is_null(value)) |
||||||
|
fail("json_pack null failed"); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("json_pack null refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* integer */ |
||||||
|
value = json_pack("i", 1); |
||||||
|
if(!json_is_integer(value) || json_integer_value(value) != 1) |
||||||
|
fail("json_pack integer failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack integer refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* integer from json_int_t */ |
||||||
|
value = json_pack("I", (json_int_t)555555); |
||||||
|
if(!json_is_integer(value) || json_integer_value(value) != 555555) |
||||||
|
fail("json_pack json_int_t failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack integer refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* real */ |
||||||
|
value = json_pack("f", 1.0); |
||||||
|
if(!json_is_real(value) || json_real_value(value) != 1.0) |
||||||
|
fail("json_pack real failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack real refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* string */ |
||||||
|
value = json_pack("s", "test"); |
||||||
|
if(!json_is_string(value) || strcmp("test", json_string_value(value))) |
||||||
|
fail("json_pack string failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack string refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* string and length */ |
||||||
|
value = json_pack("s#", "test asdf", 4); |
||||||
|
if(!json_is_string(value) || strcmp("test", json_string_value(value))) |
||||||
|
fail("json_pack string and length failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack string and length refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* string and length, non-NUL terminated string */ |
||||||
|
value = json_pack("s#", buffer, 4); |
||||||
|
if(!json_is_string(value) || strcmp("test", json_string_value(value))) |
||||||
|
fail("json_pack string and length failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack string and length refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* string concatenation */ |
||||||
|
value = json_pack("s++", "te", "st", "ing"); |
||||||
|
if(!json_is_string(value) || strcmp("testing", json_string_value(value))) |
||||||
|
fail("json_pack string concatenation failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack string concatenation refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* string concatenation and length */ |
||||||
|
value = json_pack("s#+#+", "test", 1, "test", 2, "test"); |
||||||
|
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value))) |
||||||
|
fail("json_pack string concatenation and length failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack string concatenation and length refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* empty object */ |
||||||
|
value = json_pack("{}", 1.0); |
||||||
|
if(!json_is_object(value) || json_object_size(value) != 0) |
||||||
|
fail("json_pack empty object failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack empty object refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* empty list */ |
||||||
|
value = json_pack("[]", 1.0); |
||||||
|
if(!json_is_array(value) || json_array_size(value) != 0) |
||||||
|
fail("json_pack empty list failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack empty list failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* non-incref'd object */ |
||||||
|
value = json_pack("o", json_integer(1)); |
||||||
|
if(!json_is_integer(value) || json_integer_value(value) != 1) |
||||||
|
fail("json_pack object failed"); |
||||||
|
if(value->refcount != (size_t)1) |
||||||
|
fail("json_pack integer refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* incref'd object */ |
||||||
|
value = json_pack("O", json_integer(1)); |
||||||
|
if(!json_is_integer(value) || json_integer_value(value) != 1) |
||||||
|
fail("json_pack object failed"); |
||||||
|
if(value->refcount != (size_t)2) |
||||||
|
fail("json_pack integer refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* simple object */ |
||||||
|
value = json_pack("{s:[]}", "foo"); |
||||||
|
if(!json_is_object(value) || json_object_size(value) != 1) |
||||||
|
fail("json_pack array failed"); |
||||||
|
if(!json_is_array(json_object_get(value, "foo"))) |
||||||
|
fail("json_pack array failed"); |
||||||
|
if(json_object_get(value, "foo")->refcount != (size_t)1) |
||||||
|
fail("json_pack object refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* object with complex key */ |
||||||
|
value = json_pack("{s+#+: []}", "foo", "barbar", 3, "baz"); |
||||||
|
if(!json_is_object(value) || json_object_size(value) != 1) |
||||||
|
fail("json_pack array failed"); |
||||||
|
if(!json_is_array(json_object_get(value, "foobarbaz"))) |
||||||
|
fail("json_pack array failed"); |
||||||
|
if(json_object_get(value, "foobarbaz")->refcount != (size_t)1) |
||||||
|
fail("json_pack object refcount failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* simple array */ |
||||||
|
value = json_pack("[i,i,i]", 0, 1, 2); |
||||||
|
if(!json_is_array(value) || json_array_size(value) != 3) |
||||||
|
fail("json_pack object failed"); |
||||||
|
for(i=0; i<3; i++) |
||||||
|
{ |
||||||
|
if(!json_is_integer(json_array_get(value, i)) || |
||||||
|
json_integer_value(json_array_get(value, i)) != i) |
||||||
|
|
||||||
|
fail("json_pack integer array failed"); |
||||||
|
} |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* Whitespace; regular string */ |
||||||
|
value = json_pack(" s ", "test"); |
||||||
|
if(!json_is_string(value) || strcmp("test", json_string_value(value))) |
||||||
|
fail("json_pack string (with whitespace) failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* Whitespace; empty array */ |
||||||
|
value = json_pack("[ ]"); |
||||||
|
if(!json_is_array(value) || json_array_size(value) != 0) |
||||||
|
fail("json_pack empty array (with whitespace) failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* Whitespace; array */ |
||||||
|
value = json_pack("[ i , i, i ] ", 1, 2, 3); |
||||||
|
if(!json_is_array(value) || json_array_size(value) != 3) |
||||||
|
fail("json_pack array (with whitespace) failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Invalid cases |
||||||
|
*/ |
||||||
|
|
||||||
|
/* newline in format string */ |
||||||
|
if(json_pack_ex(&error, 0, "{\n\n1")) |
||||||
|
fail("json_pack failed to catch invalid format '1'"); |
||||||
|
check_error("Expected format 's', got '1'", "<format>", 3, 1, 4); |
||||||
|
|
||||||
|
/* mismatched open/close array/object */ |
||||||
|
if(json_pack_ex(&error, 0, "[}")) |
||||||
|
fail("json_pack failed to catch mismatched '}'"); |
||||||
|
check_error("Unexpected format character '}'", "<format>", 1, 2, 2); |
||||||
|
|
||||||
|
if(json_pack_ex(&error, 0, "{]")) |
||||||
|
fail("json_pack failed to catch mismatched ']'"); |
||||||
|
check_error("Expected format 's', got ']'", "<format>", 1, 2, 2); |
||||||
|
|
||||||
|
/* missing close array */ |
||||||
|
if(json_pack_ex(&error, 0, "[")) |
||||||
|
fail("json_pack failed to catch missing ']'"); |
||||||
|
check_error("Unexpected end of format string", "<format>", 1, 2, 2); |
||||||
|
|
||||||
|
/* missing close object */ |
||||||
|
if(json_pack_ex(&error, 0, "{")) |
||||||
|
fail("json_pack failed to catch missing '}'"); |
||||||
|
check_error("Unexpected end of format string", "<format>", 1, 2, 2); |
||||||
|
|
||||||
|
/* garbage after format string */ |
||||||
|
if(json_pack_ex(&error, 0, "[i]a", 42)) |
||||||
|
fail("json_pack failed to catch garbage after format string"); |
||||||
|
check_error("Garbage after format string", "<format>", 1, 4, 4); |
||||||
|
|
||||||
|
if(json_pack_ex(&error, 0, "ia", 42)) |
||||||
|
fail("json_pack failed to catch garbage after format string"); |
||||||
|
check_error("Garbage after format string", "<format>", 1, 2, 2); |
||||||
|
|
||||||
|
/* NULL string */ |
||||||
|
if(json_pack_ex(&error, 0, "s", NULL)) |
||||||
|
fail("json_pack failed to catch null argument string"); |
||||||
|
check_error("NULL string argument", "<args>", 1, 1, 1); |
||||||
|
|
||||||
|
/* + on its own */ |
||||||
|
if(json_pack_ex(&error, 0, "+", NULL)) |
||||||
|
fail("json_pack failed to a lone +"); |
||||||
|
check_error("Unexpected format character '+'", "<format>", 1, 1, 1); |
||||||
|
|
||||||
|
/* NULL format */ |
||||||
|
if(json_pack_ex(&error, 0, NULL)) |
||||||
|
fail("json_pack failed to catch NULL format string"); |
||||||
|
check_error("NULL or empty format string", "<format>", -1, -1, 0); |
||||||
|
|
||||||
|
/* NULL key */ |
||||||
|
if(json_pack_ex(&error, 0, "{s:i}", NULL, 1)) |
||||||
|
fail("json_pack failed to catch NULL key"); |
||||||
|
check_error("NULL string argument", "<args>", 1, 2, 2); |
||||||
|
|
||||||
|
/* More complicated checks for row/columns */ |
||||||
|
if(json_pack_ex(&error, 0, "{ {}: s }", "foo")) |
||||||
|
fail("json_pack failed to catch object as key"); |
||||||
|
check_error("Expected format 's', got '{'", "<format>", 1, 3, 3); |
||||||
|
|
||||||
|
/* Complex object */ |
||||||
|
if(json_pack_ex(&error, 0, "{ s: {}, s:[ii{} }", "foo", "bar", 12, 13)) |
||||||
|
fail("json_pack failed to catch missing ]"); |
||||||
|
check_error("Unexpected format character '}'", "<format>", 1, 19, 19); |
||||||
|
|
||||||
|
/* Complex array */ |
||||||
|
if(json_pack_ex(&error, 0, "[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]")) |
||||||
|
fail("json_pack failed to catch extra }"); |
||||||
|
check_error("Unexpected format character '}'", "<format>", 1, 21, 21); |
||||||
|
|
||||||
|
/* Invalid UTF-8 in object key */ |
||||||
|
if(json_pack_ex(&error, 0, "{s:i}", "\xff\xff", 42)) |
||||||
|
fail("json_pack failed to catch invalid UTF-8 in an object key"); |
||||||
|
check_error("Invalid UTF-8 object key", "<args>", 1, 2, 2); |
||||||
|
|
||||||
|
/* Invalid UTF-8 in a string */ |
||||||
|
if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff")) |
||||||
|
fail("json_pack failed to catch invalid UTF-8 in a string"); |
||||||
|
check_error("Invalid UTF-8 string", "<args>", 1, 4, 4); |
||||||
|
} |
@ -0,0 +1,199 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
#include <jansson.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
/* Call the simple functions not covered by other tests of the public API */ |
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
json_t *value; |
||||||
|
|
||||||
|
value = json_boolean(1); |
||||||
|
if(!json_is_true(value)) |
||||||
|
fail("json_boolean(1) failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
value = json_boolean(-123); |
||||||
|
if(!json_is_true(value)) |
||||||
|
fail("json_boolean(-123) failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
value = json_boolean(0); |
||||||
|
if(!json_is_false(value)) |
||||||
|
fail("json_boolean(0) failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
|
||||||
|
value = json_integer(1); |
||||||
|
if(json_typeof(value) != JSON_INTEGER) |
||||||
|
fail("json_typeof failed"); |
||||||
|
|
||||||
|
if(json_is_object(value)) |
||||||
|
fail("json_is_object failed"); |
||||||
|
|
||||||
|
if(json_is_array(value)) |
||||||
|
fail("json_is_array failed"); |
||||||
|
|
||||||
|
if(json_is_string(value)) |
||||||
|
fail("json_is_string failed"); |
||||||
|
|
||||||
|
if(!json_is_integer(value)) |
||||||
|
fail("json_is_integer failed"); |
||||||
|
|
||||||
|
if(json_is_real(value)) |
||||||
|
fail("json_is_real failed"); |
||||||
|
|
||||||
|
if(!json_is_number(value)) |
||||||
|
fail("json_is_number failed"); |
||||||
|
|
||||||
|
if(json_is_true(value)) |
||||||
|
fail("json_is_true failed"); |
||||||
|
|
||||||
|
if(json_is_false(value)) |
||||||
|
fail("json_is_false failed"); |
||||||
|
|
||||||
|
if(json_is_boolean(value)) |
||||||
|
fail("json_is_boolean failed"); |
||||||
|
|
||||||
|
if(json_is_null(value)) |
||||||
|
fail("json_is_null failed"); |
||||||
|
|
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
|
||||||
|
value = json_string("foo"); |
||||||
|
if(!value) |
||||||
|
fail("json_string failed"); |
||||||
|
if(strcmp(json_string_value(value), "foo")) |
||||||
|
fail("invalid string value"); |
||||||
|
|
||||||
|
if(json_string_set(value, "bar")) |
||||||
|
fail("json_string_set failed"); |
||||||
|
if(strcmp(json_string_value(value), "bar")) |
||||||
|
fail("invalid string value"); |
||||||
|
|
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
value = json_string(NULL); |
||||||
|
if(value) |
||||||
|
fail("json_string(NULL) failed"); |
||||||
|
|
||||||
|
/* invalid UTF-8 */ |
||||||
|
value = json_string("a\xefz"); |
||||||
|
if(value) |
||||||
|
fail("json_string(<invalid utf-8>) failed"); |
||||||
|
|
||||||
|
value = json_string_nocheck("foo"); |
||||||
|
if(!value) |
||||||
|
fail("json_string_nocheck failed"); |
||||||
|
if(strcmp(json_string_value(value), "foo")) |
||||||
|
fail("invalid string value"); |
||||||
|
|
||||||
|
if(json_string_set_nocheck(value, "bar")) |
||||||
|
fail("json_string_set_nocheck failed"); |
||||||
|
if(strcmp(json_string_value(value), "bar")) |
||||||
|
fail("invalid string value"); |
||||||
|
|
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* invalid UTF-8 */ |
||||||
|
value = json_string_nocheck("qu\xff"); |
||||||
|
if(!value) |
||||||
|
fail("json_string_nocheck failed"); |
||||||
|
if(strcmp(json_string_value(value), "qu\xff")) |
||||||
|
fail("invalid string value"); |
||||||
|
|
||||||
|
if(json_string_set_nocheck(value, "\xfd\xfe\xff")) |
||||||
|
fail("json_string_set_nocheck failed"); |
||||||
|
if(strcmp(json_string_value(value), "\xfd\xfe\xff")) |
||||||
|
fail("invalid string value"); |
||||||
|
|
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
|
||||||
|
value = json_integer(123); |
||||||
|
if(!value) |
||||||
|
fail("json_integer failed"); |
||||||
|
if(json_integer_value(value) != 123) |
||||||
|
fail("invalid integer value"); |
||||||
|
if(json_number_value(value) != 123.0) |
||||||
|
fail("invalid number value"); |
||||||
|
|
||||||
|
if(json_integer_set(value, 321)) |
||||||
|
fail("json_integer_set failed"); |
||||||
|
if(json_integer_value(value) != 321) |
||||||
|
fail("invalid integer value"); |
||||||
|
if(json_number_value(value) != 321.0) |
||||||
|
fail("invalid number value"); |
||||||
|
|
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
value = json_real(123.123); |
||||||
|
if(!value) |
||||||
|
fail("json_real failed"); |
||||||
|
if(json_real_value(value) != 123.123) |
||||||
|
fail("invalid integer value"); |
||||||
|
if(json_number_value(value) != 123.123) |
||||||
|
fail("invalid number value"); |
||||||
|
|
||||||
|
if(json_real_set(value, 321.321)) |
||||||
|
fail("json_real_set failed"); |
||||||
|
if(json_real_value(value) != 321.321) |
||||||
|
fail("invalid real value"); |
||||||
|
if(json_number_value(value) != 321.321) |
||||||
|
fail("invalid number value"); |
||||||
|
|
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
value = json_true(); |
||||||
|
if(!value) |
||||||
|
fail("json_true failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
value = json_false(); |
||||||
|
if(!value) |
||||||
|
fail("json_false failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
value = json_null(); |
||||||
|
if(!value) |
||||||
|
fail("json_null failed"); |
||||||
|
json_decref(value); |
||||||
|
|
||||||
|
/* Test reference counting on singletons (true, false, null) */ |
||||||
|
value = json_true(); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("refcounting true works incorrectly"); |
||||||
|
json_decref(value); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("refcounting true works incorrectly"); |
||||||
|
json_incref(value); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("refcounting true works incorrectly"); |
||||||
|
|
||||||
|
value = json_false(); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("refcounting false works incorrectly"); |
||||||
|
json_decref(value); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("refcounting false works incorrectly"); |
||||||
|
json_incref(value); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("refcounting false works incorrectly"); |
||||||
|
|
||||||
|
value = json_null(); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("refcounting null works incorrectly"); |
||||||
|
json_decref(value); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("refcounting null works incorrectly"); |
||||||
|
json_incref(value); |
||||||
|
if(value->refcount != (size_t)-1) |
||||||
|
fail("refcounting null works incorrectly"); |
||||||
|
} |
@ -0,0 +1,373 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* Copyright (c) 2010-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
#include <jansson.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static void run_tests() |
||||||
|
{ |
||||||
|
json_t *j, *j2; |
||||||
|
int i1, i2, i3; |
||||||
|
json_int_t I1; |
||||||
|
int rv; |
||||||
|
double f; |
||||||
|
char *s; |
||||||
|
|
||||||
|
json_error_t error; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple, valid json_pack cases |
||||||
|
*/ |
||||||
|
|
||||||
|
/* true */ |
||||||
|
rv = json_unpack(json_true(), "b", &i1); |
||||||
|
if(rv || !i1) |
||||||
|
fail("json_unpack boolean failed"); |
||||||
|
|
||||||
|
/* false */ |
||||||
|
rv = json_unpack(json_false(), "b", &i1); |
||||||
|
if(rv || i1) |
||||||
|
fail("json_unpack boolean failed"); |
||||||
|
|
||||||
|
/* null */ |
||||||
|
if(json_unpack(json_null(), "n")) |
||||||
|
fail("json_unpack null failed"); |
||||||
|
|
||||||
|
/* integer */ |
||||||
|
j = json_integer(42); |
||||||
|
rv = json_unpack(j, "i", &i1); |
||||||
|
if(rv || i1 != 42) |
||||||
|
fail("json_unpack integer failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* json_int_t */ |
||||||
|
j = json_integer(5555555); |
||||||
|
rv = json_unpack(j, "I", &I1); |
||||||
|
if(rv || I1 != 5555555) |
||||||
|
fail("json_unpack json_int_t failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* real */ |
||||||
|
j = json_real(1.7); |
||||||
|
rv = json_unpack(j, "f", &f); |
||||||
|
if(rv || f != 1.7) |
||||||
|
fail("json_unpack real failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* number */ |
||||||
|
j = json_integer(12345); |
||||||
|
rv = json_unpack(j, "F", &f); |
||||||
|
if(rv || f != 12345.0) |
||||||
|
fail("json_unpack (real or) integer failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
j = json_real(1.7); |
||||||
|
rv = json_unpack(j, "F", &f); |
||||||
|
if(rv || f != 1.7) |
||||||
|
fail("json_unpack real (or integer) failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* string */ |
||||||
|
j = json_string("foo"); |
||||||
|
rv = json_unpack(j, "s", &s); |
||||||
|
if(rv || strcmp(s, "foo")) |
||||||
|
fail("json_unpack string failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* empty object */ |
||||||
|
j = json_object(); |
||||||
|
if(json_unpack(j, "{}")) |
||||||
|
fail("json_unpack empty object failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* empty list */ |
||||||
|
j = json_array(); |
||||||
|
if(json_unpack(j, "[]")) |
||||||
|
fail("json_unpack empty list failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* non-incref'd object */ |
||||||
|
j = json_object(); |
||||||
|
rv = json_unpack(j, "o", &j2); |
||||||
|
if(rv || j2 != j || j->refcount != 1) |
||||||
|
fail("json_unpack object failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* incref'd object */ |
||||||
|
j = json_object(); |
||||||
|
rv = json_unpack(j, "O", &j2); |
||||||
|
if(rv || j2 != j || j->refcount != 2) |
||||||
|
fail("json_unpack object failed"); |
||||||
|
json_decref(j); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* simple object */ |
||||||
|
j = json_pack("{s:i}", "foo", 42); |
||||||
|
rv = json_unpack(j, "{s:i}", "foo", &i1); |
||||||
|
if(rv || i1 != 42) |
||||||
|
fail("json_unpack simple object failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* simple array */ |
||||||
|
j = json_pack("[iii]", 1, 2, 3); |
||||||
|
rv = json_unpack(j, "[i,i,i]", &i1, &i2, &i3); |
||||||
|
if(rv || i1 != 1 || i2 != 2 || i3 != 3) |
||||||
|
fail("json_unpack simple array failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* object with many items & strict checking */ |
||||||
|
j = json_pack("{s:i, s:i, s:i}", "a", 1, "b", 2, "c", 3); |
||||||
|
rv = json_unpack(j, "{s:i, s:i, s:i}", "a", &i1, "b", &i2, "c", &i3); |
||||||
|
if(rv || i1 != 1 || i2 != 2 || i3 != 3) |
||||||
|
fail("json_unpack object with many items failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Invalid cases |
||||||
|
*/ |
||||||
|
|
||||||
|
j = json_integer(42); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "z")) |
||||||
|
fail("json_unpack succeeded with invalid format character"); |
||||||
|
check_error("Unexpected format character 'z'", "<format>", 1, 1, 1); |
||||||
|
|
||||||
|
if(!json_unpack_ex(NULL, &error, 0, "[i]")) |
||||||
|
fail("json_unpack succeeded with NULL root"); |
||||||
|
check_error("NULL root value", "<root>", -1, -1, 0); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* mismatched open/close array/object */ |
||||||
|
j = json_pack("[]"); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "[}")) |
||||||
|
fail("json_unpack failed to catch mismatched ']'"); |
||||||
|
check_error("Unexpected format character '}'", "<format>", 1, 2, 2); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
j = json_pack("{}"); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "{]")) |
||||||
|
fail("json_unpack failed to catch mismatched '}'"); |
||||||
|
check_error("Expected format 's', got ']'", "<format>", 1, 2, 2); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* missing close array */ |
||||||
|
j = json_pack("[]"); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "[")) |
||||||
|
fail("json_unpack failed to catch missing ']'"); |
||||||
|
check_error("Unexpected end of format string", "<format>", 1, 2, 2); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* missing close object */ |
||||||
|
j = json_pack("{}"); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "{")) |
||||||
|
fail("json_unpack failed to catch missing '}'"); |
||||||
|
check_error("Unexpected end of format string", "<format>", 1, 2, 2); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* garbage after format string */ |
||||||
|
j = json_pack("[i]", 42); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "[i]a", &i1)) |
||||||
|
fail("json_unpack failed to catch garbage after format string"); |
||||||
|
check_error("Garbage after format string", "<format>", 1, 4, 4); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
j = json_integer(12345); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "ia", &i1)) |
||||||
|
fail("json_unpack failed to catch garbage after format string"); |
||||||
|
check_error("Garbage after format string", "<format>", 1, 2, 2); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* NULL format string */ |
||||||
|
j = json_pack("[]"); |
||||||
|
if(!json_unpack_ex(j, &error, 0, NULL)) |
||||||
|
fail("json_unpack failed to catch null format string"); |
||||||
|
check_error("NULL or empty format string", "<format>", -1, -1, 0); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* NULL string pointer */ |
||||||
|
j = json_string("foobie"); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "s", NULL)) |
||||||
|
fail("json_unpack failed to catch null string pointer"); |
||||||
|
check_error("NULL string argument", "<args>", 1, 1, 1); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* invalid types */ |
||||||
|
j = json_integer(42); |
||||||
|
j2 = json_string("foo"); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "s")) |
||||||
|
fail("json_unpack failed to catch invalid type"); |
||||||
|
check_error("Expected string, got integer", "<validation>", 1, 1, 1); |
||||||
|
|
||||||
|
if(!json_unpack_ex(j, &error, 0, "n")) |
||||||
|
fail("json_unpack failed to catch invalid type"); |
||||||
|
check_error("Expected null, got integer", "<validation>", 1, 1, 1); |
||||||
|
|
||||||
|
if(!json_unpack_ex(j, &error, 0, "b")) |
||||||
|
fail("json_unpack failed to catch invalid type"); |
||||||
|
check_error("Expected true or false, got integer", "<validation>", 1, 1, 1); |
||||||
|
|
||||||
|
if(!json_unpack_ex(j2, &error, 0, "i")) |
||||||
|
fail("json_unpack failed to catch invalid type"); |
||||||
|
check_error("Expected integer, got string", "<validation>", 1, 1, 1); |
||||||
|
|
||||||
|
if(!json_unpack_ex(j2, &error, 0, "I")) |
||||||
|
fail("json_unpack failed to catch invalid type"); |
||||||
|
check_error("Expected integer, got string", "<validation>", 1, 1, 1); |
||||||
|
|
||||||
|
if(!json_unpack_ex(j, &error, 0, "f")) |
||||||
|
fail("json_unpack failed to catch invalid type"); |
||||||
|
check_error("Expected real, got integer", "<validation>", 1, 1, 1); |
||||||
|
|
||||||
|
if(!json_unpack_ex(j2, &error, 0, "F")) |
||||||
|
fail("json_unpack failed to catch invalid type"); |
||||||
|
check_error("Expected real or integer, got string", "<validation>", 1, 1, 1); |
||||||
|
|
||||||
|
if(!json_unpack_ex(j, &error, 0, "[i]")) |
||||||
|
fail("json_unpack failed to catch invalid type"); |
||||||
|
check_error("Expected array, got integer", "<validation>", 1, 1, 1); |
||||||
|
|
||||||
|
if(!json_unpack_ex(j, &error, 0, "{si}", "foo")) |
||||||
|
fail("json_unpack failed to catch invalid type"); |
||||||
|
check_error("Expected object, got integer", "<validation>", 1, 1, 1); |
||||||
|
|
||||||
|
json_decref(j); |
||||||
|
json_decref(j2); |
||||||
|
|
||||||
|
/* Array index out of range */ |
||||||
|
j = json_pack("[i]", 1); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "[ii]", &i1, &i2)) |
||||||
|
fail("json_unpack failed to catch index out of array bounds"); |
||||||
|
check_error("Array index 1 out of range", "<validation>", 1, 3, 3); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* NULL object key */ |
||||||
|
j = json_pack("{si}", "foo", 42); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "{si}", NULL, &i1)) |
||||||
|
fail("json_unpack failed to catch null string pointer"); |
||||||
|
check_error("NULL object key", "<args>", 1, 2, 2); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* Object key not found */ |
||||||
|
j = json_pack("{si}", "foo", 42); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "{si}", "baz", &i1)) |
||||||
|
fail("json_unpack failed to catch null string pointer"); |
||||||
|
check_error("Object item not found: baz", "<validation>", 1, 3, 3); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Strict validation |
||||||
|
*/ |
||||||
|
|
||||||
|
j = json_pack("[iii]", 1, 2, 3); |
||||||
|
rv = json_unpack(j, "[iii!]", &i1, &i2, &i3); |
||||||
|
if(rv || i1 != 1 || i2 != 2 || i3 != 3) |
||||||
|
fail("json_unpack array with strict validation failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
j = json_pack("[iii]", 1, 2, 3); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "[ii!]", &i1, &i2)) |
||||||
|
fail("json_unpack array with strict validation failed"); |
||||||
|
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* Like above, but with JSON_STRICT instead of '!' format */ |
||||||
|
j = json_pack("[iii]", 1, 2, 3); |
||||||
|
if(!json_unpack_ex(j, &error, JSON_STRICT, "[ii]", &i1, &i2)) |
||||||
|
fail("json_unpack array with strict validation failed"); |
||||||
|
check_error("1 array item(s) left unpacked", "<validation>", 1, 4, 4); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42); |
||||||
|
rv = json_unpack(j, "{sssi!}", "foo", &s, "baz", &i1); |
||||||
|
if(rv || strcmp(s, "bar") != 0 || i1 != 42) |
||||||
|
fail("json_unpack object with strict validation failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* Unpack the same item twice */ |
||||||
|
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s)) |
||||||
|
fail("json_unpack object with strict validation failed"); |
||||||
|
check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
j = json_pack("[i,{s:i,s:n},[i,i]]", 1, "foo", 2, "bar", 3, 4); |
||||||
|
if(json_unpack_ex(j, NULL, JSON_STRICT | JSON_VALIDATE_ONLY, |
||||||
|
"[i{sisn}[ii]]", "foo", "bar")) |
||||||
|
fail("json_unpack complex value with strict validation failed"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* ! and * must be last */ |
||||||
|
j = json_pack("[ii]", 1, 2); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "[i!i]", &i1, &i2)) |
||||||
|
fail("json_unpack failed to catch ! in the middle of an array"); |
||||||
|
check_error("Expected ']' after '!', got 'i'", "<format>", 1, 4, 4); |
||||||
|
|
||||||
|
if(!json_unpack_ex(j, &error, 0, "[i*i]", &i1, &i2)) |
||||||
|
fail("json_unpack failed to catch * in the middle of an array"); |
||||||
|
check_error("Expected ']' after '*', got 'i'", "<format>", 1, 4, 4); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
j = json_pack("{sssi}", "foo", "bar", "baz", 42); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "{ss!si}", "foo", &s, "baz", &i1)) |
||||||
|
fail("json_unpack failed to catch ! in the middle of an object"); |
||||||
|
check_error("Expected '}' after '!', got 's'", "<format>", 1, 5, 5); |
||||||
|
|
||||||
|
if(!json_unpack_ex(j, &error, 0, "{ss*si}", "foo", &s, "baz", &i1)) |
||||||
|
fail("json_unpack failed to catch ! in the middle of an object"); |
||||||
|
check_error("Expected '}' after '*', got 's'", "<format>", 1, 5, 5); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* Error in nested object */ |
||||||
|
j = json_pack("{s{snsn}}", "foo", "bar", "baz"); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "{s{sn!}}", "foo", "bar")) |
||||||
|
fail("json_unpack nested object with strict validation failed"); |
||||||
|
check_error("1 object item(s) left unpacked", "<validation>", 1, 7, 7); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* Error in nested array */ |
||||||
|
j = json_pack("[[ii]]", 1, 2); |
||||||
|
if(!json_unpack_ex(j, &error, 0, "[[i!]]", &i1)) |
||||||
|
fail("json_unpack nested array with strict validation failed"); |
||||||
|
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
/* Optional values */ |
||||||
|
j = json_object(); |
||||||
|
i1 = 0; |
||||||
|
if(json_unpack(j, "{s?i}", "foo", &i1)) |
||||||
|
fail("json_unpack failed for optional key"); |
||||||
|
if(i1 != 0) |
||||||
|
fail("json_unpack unpacked an optional key"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
i1 = 0; |
||||||
|
j = json_pack("{si}", "foo", 42); |
||||||
|
if(json_unpack(j, "{s?i}", "foo", &i1)) |
||||||
|
fail("json_unpack failed for an optional value"); |
||||||
|
if(i1 != 42) |
||||||
|
fail("json_unpack failed to unpack an optional value"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
j = json_object(); |
||||||
|
i1 = i2 = i3 = 0; |
||||||
|
if(json_unpack(j, "{s?[ii]s?{s{si}}}", |
||||||
|
"foo", &i1, &i2, |
||||||
|
"bar", "baz", "quux", &i3)) |
||||||
|
fail("json_unpack failed for complex optional values"); |
||||||
|
if(i1 != 0 || i2 != 0 || i3 != 0) |
||||||
|
fail("json_unpack unexpectedly unpacked something"); |
||||||
|
json_decref(j); |
||||||
|
|
||||||
|
j = json_pack("{s{si}}", "foo", "bar", 42); |
||||||
|
if(json_unpack(j, "{s?{s?i}}", "foo", "bar", &i1)) |
||||||
|
fail("json_unpack failed for complex optional values"); |
||||||
|
if(i1 != 42) |
||||||
|
fail("json_unpack failed to unpack"); |
||||||
|
json_decref(j); |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||||
|
* |
||||||
|
* Jansson is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the MIT license. See LICENSE for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef UTIL_H |
||||||
|
#define UTIL_H |
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H |
||||||
|
#include <config.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#if HAVE_LOCALE_H |
||||||
|
#include <locale.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <jansson.h> |
||||||
|
|
||||||
|
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__) |
||||||
|
|
||||||
|
#define fail(msg) \ |
||||||
|
do { \ |
||||||
|
failhdr; \ |
||||||
|
fprintf(stderr, "%s\n", msg); \ |
||||||
|
exit(1); \ |
||||||
|
} while(0) |
||||||
|
|
||||||
|
/* Assumes json_error_t error */ |
||||||
|
#define check_error(text_, source_, line_, column_, position_) \ |
||||||
|
do { \ |
||||||
|
if(strcmp(error.text, text_) != 0) { \ |
||||||
|
failhdr; \ |
||||||
|
fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, text_); \ |
||||||
|
exit(1); \ |
||||||
|
} \ |
||||||
|
if(strcmp(error.source, source_) != 0) { \ |
||||||
|
failhdr; \ |
||||||
|
\ |
||||||
|
fprintf(stderr, "source: \"%s\" != \"%s\"\n", error.source, source_); \ |
||||||
|
exit(1); \ |
||||||
|
} \ |
||||||
|
if(error.line != line_) { \ |
||||||
|
failhdr; \ |
||||||
|
fprintf(stderr, "line: %d != %d\n", error.line, line_); \ |
||||||
|
exit(1); \ |
||||||
|
} \ |
||||||
|
if(error.column != column_) { \ |
||||||
|
failhdr; \ |
||||||
|
fprintf(stderr, "column: %d != %d\n", error.column, column_); \ |
||||||
|
exit(1); \ |
||||||
|
} \ |
||||||
|
if(error.position != position_) { \ |
||||||
|
failhdr; \ |
||||||
|
fprintf(stderr, "position: %d != %d\n", error.position, position_); \ |
||||||
|
exit(1); \ |
||||||
|
} \ |
||||||
|
} while(0) |
||||||
|
|
||||||
|
|
||||||
|
static void run_tests(); |
||||||
|
|
||||||
|
int main() { |
||||||
|
#ifdef HAVE_SETLOCALE |
||||||
|
setlocale(LC_ALL, ""); |
||||||
|
#endif |
||||||
|
run_tests(); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,2 @@ |
|||||||
|
1 2 2 |
||||||
|
unable to decode byte 0xed near '"' |
@ -0,0 +1,2 @@ |
|||||||
|
1 3 3 |
||||||
|
unable to decode byte 0xe5 near '"\' |
@ -0,0 +1 @@ |
|||||||
|
["\å"] |
@ -0,0 +1,2 @@ |
|||||||
|
1 1 1 |
||||||
|
unable to decode byte 0xe5 |
@ -0,0 +1 @@ |
|||||||
|
[å] |
@ -0,0 +1,2 @@ |
|||||||
|
1 4 4 |
||||||
|
unable to decode byte 0xe5 near '123' |
@ -0,0 +1 @@ |
|||||||
|
[123å] |
@ -0,0 +1,2 @@ |
|||||||
|
1 4 4 |
||||||
|
unable to decode byte 0xe5 near '"\u' |
@ -0,0 +1,2 @@ |
|||||||
|
1 4 4 |
||||||
|
unable to decode byte 0xe5 near '1e1' |
@ -0,0 +1 @@ |
|||||||
|
[1e1å] |
@ -0,0 +1,2 @@ |
|||||||
|
1 2 2 |
||||||
|
unable to decode byte 0xe5 near 'a' |
@ -0,0 +1 @@ |
|||||||
|
[a蘊 |
@ -0,0 +1,2 @@ |
|||||||
|
1 2 2 |
||||||
|
unable to decode byte 0xe5 near '0' |
@ -0,0 +1 @@ |
|||||||
|
[0å] |
@ -0,0 +1,2 @@ |
|||||||
|
1 3 3 |
||||||
|
unable to decode byte 0xe5 near '1e' |
@ -0,0 +1 @@ |
|||||||
|
[1e蘊 |
@ -0,0 +1,2 @@ |
|||||||
|
1 2 2 |
||||||
|
unable to decode byte 0xe5 near '"' |
@ -0,0 +1 @@ |
|||||||
|
["å <-- invalid UTF-8"] |
@ -0,0 +1,2 @@ |
|||||||
|
1 0 0 |
||||||
|
unable to decode byte 0xe5 |
@ -0,0 +1,2 @@ |
|||||||
|
1 2 2 |
||||||
|
unable to decode byte 0x81 near '"' |
@ -0,0 +1,2 @@ |
|||||||
|
1 2 2 |
||||||
|
unable to decode byte 0xf4 near '"' |
@ -0,0 +1,2 @@ |
|||||||
|
1 2 2 |
||||||
|
unable to decode byte 0xe0 near '"' |
@ -0,0 +1,2 @@ |
|||||||
|
1 2 2 |
||||||
|
unable to decode byte 0xf0 near '"' |
@ -0,0 +1,2 @@ |
|||||||
|
1 2 2 |
||||||
|
unable to decode byte 0xc1 near '"' |
@ -0,0 +1,2 @@ |
|||||||
|
1 2 2 |
||||||
|
unable to decode byte 0xfd near '"' |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue