git.fiddlerwoaroof.com
t/round-trip.lisp
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))))