9641dc0a |
(in-package :cl-user)
(defpackage cl-yaml-test.round-trip
(:use :cl :fiveam)
(:export :round-trip)
(:documentation "Round-trip Emitter/Parser tests."))
(in-package :cl-yaml-test.round-trip)
(def-suite round-trip
:description "YAML emitter/parser tests.")
(in-suite round-trip)
(defclass invoice ()
((number :initarg :number :accessor invoice-number)
(date :initarg :date :accessor invoice-date)
(bill-to :initarg :bill-to :accessor invoice-bill-to)
;; skip ship-to until aliases and anchors are more developed)
(product :initarg :product :accessor invoice-product)
(tax :initarg :tax :accessor invoice-tax)
(total :initarg :total :accessor invoice-total)
(comments :initarg :comments :accessor invoice-comments)))
(defclass address ()
((lines :initarg :lines :accessor address-lines)
(city :initarg :city :accessor address-city)
(state :initarg :state :accessor address-state)
(postal :initarg :postal :accessor address-postal)))
(defclass product-order ()
((sku :initarg :sku :accessor product-order-sku)
(quantity :initarg :quantity :accessor product-order-quantity)
(description :initarg :description :accessor product-order-description)
(price :initarg :price :accessor product-order-price)))
(defun make-test-invoice ()
(make-instance 'invoice
:number 34843
:date "2001-01-23"
:bill-to (list "Chris" "Dumars"
(make-instance 'address
:lines "458 Walkman Dr.
Suite #292"
:city "Royal Oak"
:state "MI"
:postal "48046"))
:product (list (make-instance 'product-order
:sku "BL394D"
:quantity 4
:description "Basketball"
:price 450.00)
(make-instance 'product-order
:sku "BL4438H"
:quantity 1
:description "Super Hoop"
:price 2392.00))
:tax 251.42
:total 4443.52
:comments "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338."))
;;; Methods to emit CLOS objects
(defmethod yaml.emitter:emit-object (emitter (obj address))
(yaml.emitter:emit-mapping (emitter :style :block-mapping-style)
(yaml.emitter:emit-scalar emitter "lines")
(yaml.emitter:emit-scalar emitter (address-lines obj)
:style :literal-scalar-style)
(yaml.emitter:emit-scalar emitter "city")
(yaml.emitter:emit-scalar emitter (address-city obj))
(yaml.emitter:emit-scalar emitter "state")
(yaml.emitter:emit-scalar emitter (address-state obj))
(yaml.emitter:emit-scalar emitter "postal")
(yaml.emitter:emit-scalar emitter (address-postal obj))))
(defmethod yaml.emitter:emit-object (emitter (obj product-order))
(yaml.emitter:emit-mapping (emitter :style :block-mapping-style)
(yaml.emitter:emit-scalar emitter "sku")
(yaml.emitter:emit-scalar emitter (product-order-sku obj))
(yaml.emitter:emit-scalar emitter "quantity")
(yaml.emitter:emit-scalar emitter (product-order-quantity obj))
(yaml.emitter:emit-scalar emitter "description")
(yaml.emitter:emit-scalar emitter (product-order-description obj))
(yaml.emitter:emit-scalar emitter "price")
(yaml.emitter:emit-scalar emitter (product-order-price obj))))
(defmethod yaml.emitter:emit-object (emitter (obj invoice))
(yaml.emitter:emit-mapping (emitter :style :block-mapping-style
:tag "clarkevans.com,2002:invoice")
(yaml.emitter:emit-scalar emitter "invoice")
(yaml.emitter:emit-scalar emitter (invoice-number obj))
(yaml.emitter:emit-scalar emitter "date")
(yaml.emitter:emit-scalar emitter (invoice-date obj))
(yaml.emitter:emit-scalar emitter "bill-to")
(yaml.emitter:emit-mapping (emitter :style :block-mapping-style)
(yaml.emitter:emit-scalar emitter "given")
(yaml.emitter:emit-scalar emitter (first (invoice-bill-to obj)))
(yaml.emitter:emit-scalar emitter "family")
(yaml.emitter:emit-scalar emitter (second (invoice-bill-to obj)))
(yaml.emitter:emit-scalar emitter "address")
(yaml.emitter:emit-object emitter (third (invoice-bill-to obj))))
(yaml.emitter:emit-scalar emitter "product")
(yaml.emitter:emit-sequence (emitter :style :block-sequence-style)
(dolist (product (invoice-product obj))
(yaml.emitter:emit-object emitter product)))
(yaml.emitter:emit-scalar emitter "tax")
(yaml.emitter:emit-scalar emitter (invoice-tax obj))
(yaml.emitter:emit-scalar emitter "total")
(yaml.emitter:emit-scalar emitter (invoice-total obj))
(yaml.emitter:emit-scalar emitter "comments")
(yaml.emitter:emit-scalar emitter (invoice-comments obj)
:plain-implicit t
:quoted-implicit t
:style :plain-scalar-style)))
;;; Functions to construct objects from YAML
(defun construct-address (mapping)
(make-instance 'address
:lines (gethash "lines" mapping)
:city (gethash "city" mapping)
:state (gethash "state" mapping)
:postal (gethash "postal" mapping)))
(defun construct-product-order (mapping)
(make-instance 'product-order
:sku (gethash "sku" mapping)
:quantity (gethash "quantity" mapping)
:description (gethash "description" mapping)
:price (gethash "price" mapping)))
(defun construct-invoice (mapping)
(let ((bill-to-mapping (gethash "bill-to" mapping)))
(make-instance 'invoice
:number (gethash "invoice" mapping)
:date (gethash "date" mapping)
:bill-to (list (gethash "given" bill-to-mapping)
(gethash "family" bill-to-mapping)
(construct-address (gethash "address" bill-to-mapping)))
:product (mapcar #'construct-product-order
(gethash "product" mapping))
:tax (gethash "tax" mapping)
:total (gethash "total" mapping)
:comments (gethash "comments" mapping))))
;;; Register a construction function with a YAML tag
(eval-when (:load-toplevel)
(yaml.parser:register-mapping-converter "clarkevans.com,2002:invoice"
#'construct-invoice))
;;; Helper
(defun test-emit (obj)
(yaml.emitter:with-emitter-to-string (emitter)
(yaml.emitter:emit-stream (emitter)
(yaml.emitter:emit-document (emitter :implicit t)
(yaml.emitter:emit-object emitter obj)))))
;;; Round-trip test
(test invoice
(let* ((invoice-a (make-test-invoice))
(yaml-a (test-emit invoice-a))
(invoice-b (yaml:parse yaml-a))
(yaml-b (test-emit invoice-b)))
(is (string= yaml-a yaml-b))))
|