From 2911373b89c54c4e3b8c27938a81f7860fa1799e Mon Sep 17 00:00:00 2001 From: Kevin Rosenberg Date: Sun, 20 Apr 2003 05:15:10 +0000 Subject: [PATCH] 0.pre8.79 - inspector bug fixes, refactoring, more tests --- contrib/sb-aclrepl/aclrepl-tests.lisp | 123 +++++++++-------- contrib/sb-aclrepl/inspect.lisp | 245 +++++++++++++++++---------------- version.lisp-expr | 2 +- 3 files changed, 191 insertions(+), 179 deletions(-) diff --git a/contrib/sb-aclrepl/aclrepl-tests.lisp b/contrib/sb-aclrepl/aclrepl-tests.lisp index 7e08ceb..154936a 100644 --- a/contrib/sb-aclrepl/aclrepl-tests.lisp +++ b/contrib/sb-aclrepl/aclrepl-tests.lisp @@ -5,10 +5,10 @@ (import '(sb-aclrepl::inspected-parts sb-aclrepl::inspected-description sb-aclrepl::inspected-elements sb-aclrepl::parts-count - sb-aclrepl::parts-seq-type sb-aclrepl::find-object-part-with-id + sb-aclrepl::parts-seq-type sb-aclrepl::find-part-id sb-aclrepl::element-at sb-aclrepl::label-at sb-aclrepl::display-inspected-parts - sb-aclrepl::display-labelled-element + sb-aclrepl::display-labeled-element sb-aclrepl::*inspect-unbound-object-marker*)) (eval-when (:compile-toplevel :load-toplevel :execute) @@ -41,6 +41,9 @@ (defstruct empty-struct ) +(defstruct tiny-struct + (first 10)) + (defstruct simple-struct (first) (slot-2 'a-value) @@ -49,35 +52,38 @@ (defparameter *empty-class* (make-instance 'empty-class)) (defparameter *simple-class* (make-instance 'simple-class)) (defparameter *empty-struct* (make-empty-struct)) +(defparameter *tiny-struct* (make-tiny-struct)) (defparameter *simple-struct* (make-simple-struct)) (defparameter *normal-list* '(a b 3)) (defparameter *dotted-list* '(a b . 3)) (defparameter *cons-pair* '(#c(1 2) . a-symbol)) (defparameter *complex* #c(1 2)) (defparameter *ratio* 22/7) +(defparameter *double* 5.5d0) (defparameter *array* (make-array '(3 3 2) :initial-element nil)) (defparameter *vector* (make-array '(20):initial-contents '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19))) (defun find-position (object id) - (nth-value 0 (find-object-part-with-id object id))) + (nth-value 0 (find-part-id object id))) (defun parts (object) (inspected-parts object)) (defun description (object) (inspected-description object)) -(defun elements (object &optional print skip) - (nth-value 0 (inspected-elements object print skip))) -(defun elements-labels (object &optional print skip) +(defun elements (object &optional print (skip 0)) + (nth-value 0 (inspected-elements object print skip ))) +(defun elements-labels (object &optional print (skip 0)) (nth-value 1 (inspected-elements object print skip))) -(defun elements-count (object &optional print skip) +(defun elements-count (object &optional print (skip 0)) (nth-value 2 (inspected-elements object print skip))) -(defun labelled-element (object pos &optional print skip) +(defun labeled-element (object pos &optional print (skip 0)) (with-output-to-string (strm) - (display-labelled-element (aref (elements object print skip) pos) - (aref (elements-labels object print skip) pos) - strm))) + (display-labeled-element + (aref (the simple-vector (elements object print skip)) pos) + (aref (the simple-vector (elements-labels object print skip)) pos) + strm))) (deftest find.list.0 (find-position *normal-list* 0) 0) (deftest find.list.1 (find-position *normal-list* 0) 0) @@ -163,7 +169,6 @@ (14 . "[2,1,0]") (15 . "[2,1,1]") (16 . "[2,2,0]") (17 . "[2,2,1]"))) - (deftest empty.class.0 (elements-count *empty-class*) 0) (deftest empty.class.1 (elements *empty-class*) nil) (deftest empty.class.2 (elements-labels *empty-class*) nil) @@ -172,7 +177,7 @@ (deftest simple.class.1 (elements *simple-class*) #(#.*inspect-unbound-object-marker* 0 "abc")) (deftest simple.class.2 (elements-labels *simple-class*) - #((0 . A) (1 . SECOND) (2 . REALLY-LONG-SLOT-NAME))) + #((0 . "A") (1 . "SECOND") (2 . "REALLY-LONG-SLOT-NAME"))) (deftest empty.struct.0 (elements-count *empty-struct*) 0) (deftest empty.struct.1 (elements *empty-struct*) nil) @@ -185,82 +190,88 @@ #((0 . "FIRST") (1 . "SLOT-2") (2 . "REALLY-LONG-STRUCT-SLOT-NAME"))) -(deftest display.simple-struct.0 - (labelled-element *simple-struct* 0) +(deftest display.simple-struct.0 (labeled-element *simple-struct* 0) " 0 FIRST ----------> the symbol NIL") -(deftest display.simple-struct.1 - (labelled-element *simple-struct* 1) +(deftest display.simple-struct.1 (labeled-element *simple-struct* 1) " 1 SLOT-2 ---------> the symbol A-VALUE") -(deftest display.simple-struct.2 - (labelled-element *simple-struct* 2) +(deftest display.simple-struct.2 (labeled-element *simple-struct* 2) " 2 REALLY-LONG-STRUCT-SLOT-NAME -> a simple-string (4) \"defg\"") -(deftest display.simple-class.0 - (labelled-element *simple-class* 0) +(deftest display.simple-class.0 (labeled-element *simple-class* 0) " 0 A --------------> ..unbound..") -(deftest display.simple-class.1 - (labelled-element *simple-class* 1) +(deftest display.simple-class.1 (labeled-element *simple-class* 1) " 1 SECOND ---------> fixnum 0") -(deftest display.simple-class.2 - (labelled-element *simple-class* 2) +(deftest display.simple-class.2 (labeled-element *simple-class* 2) " 2 REALLY-LONG-SLOT-NAME -> a simple-string (3) \"abc\"") -(deftest display.complex.0 - (labelled-element *complex* 0) +(deftest display.complex.0 (labeled-element *complex* 0) " 0 real -----------> fixnum 1") -(deftest display.complex.1 - (labelled-element *complex* 1) +(deftest display.complex.1 (labeled-element *complex* 1) " 1 imag -----------> fixnum 2") -(deftest display.ratio.0 - (labelled-element *ratio* 0) +(deftest display.ratio.0 (labeled-element *ratio* 0) " 0 numerator ------> fixnum 22") -(deftest display.ratio.1 - (labelled-element *ratio* 1) +(deftest display.ratio.1 (labeled-element *ratio* 1) " 1 denominator ----> fixnum 7") -(deftest display.dotted-list.0 - (labelled-element *dotted-list* 0) +(deftest display.dotted-list.0 (labeled-element *dotted-list* 0) " 0-> the symbol A") -(deftest display.dotted-list.1 - (labelled-element *dotted-list* 1) +(deftest display.dotted-list.1 (labeled-element *dotted-list* 1) " 1-> the symbol B") -(deftest display.dotted-list.2 - (labelled-element *dotted-list* 2) +(deftest display.dotted-list.2 (labeled-element *dotted-list* 2) "tail-> fixnum 3") (deftest display.normal-list.0 - (labelled-element *normal-list* 0) + (labeled-element *normal-list* 0) " 0-> the symbol A") -(deftest display.normal-list.1 - (labelled-element *normal-list* 1) +(deftest display.normal-list.1 (labeled-element *normal-list* 1) " 1-> the symbol B") -(deftest display.normal-list.2 - (labelled-element *normal-list* 2) +(deftest display.normal-list.2 (labeled-element *normal-list* 2) " 2-> fixnum 3") -(deftest display.vector.0 - (labelled-element *vector* 0) +(deftest display.vector.0 (labeled-element *vector* 0) " 0-> fixnum 0") -(deftest display.vector.1 - (labelled-element *vector* 1) +(deftest display.vector.1 (labeled-element *vector* 1) " 1-> fixnum 1") -(deftest display.vector.skip1.0 - (labelled-element *vector* 0 nil 2) +(deftest display.vector.skip1.0 (labeled-element *vector* 0 nil 2) " ...") -(deftest display.vector.skip1.1 - (labelled-element *vector* 1 nil 2) +(deftest display.vector.skip1.1 (labeled-element *vector* 1 nil 2) " 2-> fixnum 2") -(deftest display.consp.0 - (labelled-element *cons-pair* 0) +(deftest display.consp.0 (labeled-element *cons-pair* 0) " 0 car ------------> complex number #C(1 2)") -(deftest display.consp.1 - (labelled-element *cons-pair* 1) +(deftest display.consp.1 (labeled-element *cons-pair* 1) " 1 cdr ------------> the symbol A-SYMBOL") +(deftest nil.parts.0 (elements-count nil) 5) + +(deftest tiny.struct.0 (elements-count *tiny-struct*) 1) +(deftest tiny.struct.1 (elements *tiny-struct*) #(10)) +(deftest tiny.struct.1 (elements-labels *tiny-struct*) #((0 . "FIRST"))) + +(deftest tiny.struct.skip1.0 (elements-count *tiny-struct* nil 1) 1) +(deftest tiny.struct.skip1.1 (elements *tiny-struct* nil 1) + #(nil)) +(deftest tiny.struct.skip1.2 (elements-labels *tiny-struct* nil 1) + #(:ellipses)) + +(deftest tiny.double.0 (elements-count *double*) 0) + +(deftest tiny.double.skip1.0 (elements-count *double* nil 1) 1) +(deftest tiny.double.skip1.1 (elements *double* nil 1) + #(nil)) +(deftest tiny.doubel.skip1.2 (elements-labels *double* nil 1) + #(:ellipses)) + +(deftest tiny.double.skip2.0 (elements-count *double* nil 2) 1) +(deftest tiny.double.skip2.1 (elements *double* nil 2) + #(nil)) +(deftest tiny.double.skip2.2 (elements-labels *double* nil 2) + #(:ellipses)) + + (do-tests) (when (pending-tests) diff --git a/contrib/sb-aclrepl/inspect.lisp b/contrib/sb-aclrepl/inspect.lisp index dcec042..f127cad 100644 --- a/contrib/sb-aclrepl/inspect.lisp +++ b/contrib/sb-aclrepl/inspect.lisp @@ -72,7 +72,7 @@ i set
set named component to evalated form (setf (inspect-object-stack *current-inspect*) (list object)) (setf (inspect-select-stack *current-inspect*) (list (format nil "(inspect ~S)" object))) - (%inspect output-stream)) + (redisplay output-stream)) (setq sb-impl::*inspect-fun* #'inspector) @@ -132,7 +132,7 @@ i set set named component to evalated form (inspect-object-stack *current-inspect*)) (defun redisplay (stream) - (%inspect stream)) + (display-current stream)) ;;; ;;; istep command processing @@ -152,7 +152,7 @@ i set set named component to evalated form ((stack) (output-inspect-note stream "Object has no parent")) (t - (redisplay stream)))) + (no-object-msg stream)))) (defun istep-cmd-inspect-* (stream) (reset-stack) @@ -171,7 +171,7 @@ i set set named component to evalated form (let ((parent (second (stack))) (id (car (inspect-select-stack *current-inspect*)))) (multiple-value-bind (position parts) - (find-object-part-with-id parent id) + (find-part-id parent id) (let ((new-position (if (string= ">" option) (1+ position) (1- position)))) @@ -180,17 +180,12 @@ i set set named component to evalated form (setf (car (inspect-object-stack *current-inspect*)) value) (setf (car (inspect-select-stack *current-inspect*)) - (if (integerp id) - new-position - (let ((label (label-at parts new-position))) - (if (stringp label) - (read-from-string label) - label)))) + (id-at parts new-position)) (redisplay stream)) (output-inspect-note stream "Parent has no selectable component indexed by ~d" new-position)))))) - (redisplay stream))) + (no-object-msg stream))) (defun istep-cmd-set-raw (option-string stream) (when (inspect-object-stack *current-inspect*) @@ -234,7 +229,7 @@ i set set named component to evalated form (symbol (format nil "which is the ~a component of" select)) (string - (format nil "which was selected by ~S" select)) + (format nil "which was selected by ~A" select)) (t (write-to-string select)))) @@ -245,17 +240,17 @@ i set set named component to evalated form (output-inspect-note stream "The current object is:") (dotimes (i (length stack)) (output-inspect-note - stream "~A, ~A~%" + stream "~A, ~A" (inspected-description (nth i stack)) (select-description (nth i (inspect-select-stack *current-inspect*)))))) - (%inspect stream)))) + (no-object-msg stream)))) (defun istep-cmd-set (id-string value-string stream) (if (stack) (let ((id (when id-string (read-from-string id-string)))) (multiple-value-bind (position parts) - (find-object-part-with-id (car (stack)) id) + (find-part-id (car (stack)) id) (if parts (if position (when value-string @@ -269,18 +264,18 @@ i set set named component to evalated form (string (output-inspect-note stream result)) (t - (%inspect stream)))))) + (redisplay stream)))))) (output-inspect-note stream "Object has no selectable component named by ~A" id)) (output-inspect-note stream "Object has no selectable components")))) - (%inspect stream))) + (no-object-msg stream))) (defun istep-cmd-select-component (id stream) (if (stack) (multiple-value-bind (position parts) - (find-object-part-with-id (car (stack)) id) + (find-part-id (car (stack)) id) (cond ((integerp position) (let* ((value (element-at parts position))) @@ -306,7 +301,7 @@ i set set named component to evalated form stream "Enter a valid index (~:[0-~W~;0~])" (= (parts-count parts) 1) (1- (parts-count parts)))))))) - (%inspect stream))) + (no-object-msg stream))) (defun istep-cmd-set-stack (form stream) (reset-stack) @@ -320,17 +315,21 @@ i set set named component to evalated form ;;; ;;; aclrepl-specific inspection display ;;; + + (defun no-object-msg (s) + (output-inspect-note s "No object is being inspected")) + + (defun display-current (s) + (if (stack) + (let ((inspected (car (stack)))) + (setq cl:* inspected) + (display-inspect inspected s *inspect-length* *inspect-skip*)) + (no-object-msg))) - (defun %inspect (s) - (if (inspect-object-stack *current-inspect*) - (let ((inspected)) - (setq cl:* (car (inspect-object-stack *current-inspect*))) - (display-inspected-parts inspected s *inspect-length* *inspect-skip*)) - (output-inspect-note s "No object is being inspected"))) ) ;; end binding for multithreading -(defun display-inspected-parts (object stream &optional length skip) +(defun display-inspect (object stream &optional length skip) (multiple-value-bind (elements labels count) (inspected-elements object length skip) (format stream "~&~A" (inspected-description object)) @@ -339,7 +338,7 @@ i set set named component to evalated form (princ #\newline stream) (dotimes (i count) (fresh-line stream) - (display-labelled-element (elt elements i) (elt labels i) stream)))) + (display-labeled-element (elt elements i) (elt labels i) stream)))) (defun array-label-p (label) (and (stringp (cdr label)) (char= (char (cdr label) 0) #\[))) @@ -347,7 +346,7 @@ i set set named component to evalated form (defun named-or-array-label-p (label) (consp label)) -(defun display-labelled-element (element label stream) +(defun display-labeled-element (element label stream) (cond ((eq label :ellipses) (format stream " ...")) @@ -369,9 +368,9 @@ i set set named component to evalated form ;;; process print length and skip selectors ;;; ;;; FUNCTIONS TO CONSIDER FOR EXPORT -;;; FIND-OBJECT-PART-WITH-ID +;;; FIND-PART-ID ;;; ELEMENT-AT -;;; LABEL-AT +;;; ID-AT ;;; INSPECTED-ELEMENTS ;;; INSPECTED-DESCRIPTION ;;; @@ -388,32 +387,23 @@ i set set named component to evalated form ;;; *inspect-read-cmd* will return an impl-dependent cmd that sbcl will ;;; send to the contributed inspector for processing and display. -(defun find-object-part-with-id (object id) +(defun find-part-id (object id) "COMPONENT-ID can be an integer or a name of a id. Returns (VALUES POSITION PARTS). POSITION is NIL if the id is invalid or not found." - (if object - (let* ((parts (inspected-parts object)) - (seq-type (parts-seq-type parts)) - (count (parts-count parts)) - (components (parts-components parts))) - (when (symbolp id) - (setq id (symbol-name id))) - (let ((position - (cond ((and (eq seq-type :named) - (stringp id)) - (position id (the list components) :key #'car - :test #'string-equal)) - ((and (eq seq-type :improper-list) - (stringp id) - (string-equal id "tail")) - (1- count)) - ((numberp id) - (when (< -1 id count) - id))))) - (values position parts))) - (values nil nil))) - + (let* ((parts (inspected-parts object)) + (name (when (symbolp id) (symbol-name id) id))) + (values + (if (numberp id) + (when (< -1 id (parts-count parts)) id) + (case (parts-seq-type parts) + (:named + (position name (the list (parts-components parts)) + :key #'car :test #'string-equal)) + (:improper-list + (when (string-equal name "tail") + (1- (parts-count parts)))))) + parts))) (defun element-at (parts position) (let ((count (parts-count parts)) @@ -431,7 +421,7 @@ POSITION is NIL if the id is invalid or not found." (t (elt components position)))))) -(defun label-at (parts position) +(defun id-at (parts position) (let ((count (parts-count parts))) (when (< -1 position count) (case (parts-seq-type parts) @@ -446,14 +436,76 @@ POSITION is NIL if the id is invalid or not found." (t position))))) -(defun label-at-maybe-with-index (parts position) +(defun inspected-elements (object &optional length (skip 0)) + "Returns elements of an object that have been trimmed and labeled based on +length and skip. Returns (VALUES ELEMENTS LABELS ELEMENT-COUNT) +where ELEMENTS and LABELS are vectors containing ELEMENT-COUNT items. +LABELS may be a string, number, cons pair, :tail, or :ellipses. +This function may return an ELEMENT-COUNT of up to (+ 3 length) which would +include an :ellipses at the beginning, :ellipses at the end, +and the last element." + (let* ((parts (inspected-parts object)) + (print-length (if length length (parts-count parts))) + (last-part (last-part parts)) + (last-requested (last-requested parts print-length skip)) + (element-count (compute-elements-count parts print-length skip)) + (first-to (if (first-element-ellipses-p parts skip) 1 0)) + (elements (when (plusp element-count) (make-array element-count))) + (labels (when (plusp element-count) (make-array element-count)))) + ;; possible first ellipses + (when (first-element-ellipses-p parts skip) + (set-element-values elements labels 0 nil :ellipses)) + ;; main elements + (do ((i 0 (1+ i))) + ((> i (- last-requested skip))) + (set-element elements labels parts (+ i first-to) (+ i skip))) + ;; last parts value if needed + (when (< last-requested last-part) + (set-element elements labels parts (- element-count 1) last-part)) + ;; ending ellipses or next to last parts value if needed + (when (< last-requested (1- last-part)) + (if (= last-requested (- last-part 2)) + (set-element elements labels parts (- element-count 2) (1- last-part)) + (set-element-values elements labels (- element-count 2) nil :ellipses))) + (values elements labels element-count))) + +(defun last-requested (parts print skip) + (min (1- (parts-count parts)) (+ skip print -1))) + +(defun last-part (parts) + (1- (parts-count parts))) + +(defun compute-elements-count (parts length skip) + "Compute the number of elements in parts given the print length and skip." + (let ((element-count (min length (max 0 (- (parts-count parts) skip))))) + (when (plusp skip) ; starting ellipses + (incf element-count)) + (when (< (last-requested parts length skip) + (last-part parts)) ; last value + (incf element-count) + (when (< (last-requested parts length skip) + (1- (last-part parts))) ; ending ellipses + (incf element-count))) + element-count)) + +(defun set-element (elements labels parts to-index from-index) + (set-element-values elements labels to-index (element-at parts from-index) + (label-at parts from-index))) + +(defun set-element-values (elements labels index element label) + (setf (aref elements index) element) + (setf (aref labels index) label)) + +(defun first-element-ellipses-p (parts skip) + (and (parts-count parts) (plusp skip))) + +(defun label-at (parts position) "Helper function for inspected-elements. Conses the -position with the label is the label is a string." - (let ((label (label-at parts position))) - (if (or (stringp label) - (and (symbolp label) (not (eq label :tail)))) - (cons position label) - label))) +position with the label if the label is a string." + (let ((id (id-at parts position))) + (if (stringp id) + (cons position id) + id))) (defun array-index-string (index parts) "Formats an array index in row major format." @@ -467,57 +519,6 @@ position with the label is the label is a string." (push r list))) (format nil "[~W~{,~W~}]" (car list) (cdr list)))))) -(defun inspected-elements (object &optional length skip) - "Returns elements of an object that have been trimmed and labeled based on -length and skip. Returns (VALUES ELEMENTS LABELS ELEMENT-COUNT) -where ELEMENTS and LABELS are vectors containing ELEMENT-COUNT items. -LABELS may be a string, number, cons pair, :tail, or :ellipses. -This function may return an ELEMENT-COUNT of up to (+ 3 length) which would -include an :ellipses at the beginning, :ellipses at the end, -and the last element." - (let* ((parts (inspected-parts object)) - (count (parts-count parts))) - (unless skip (setq skip 0)) - (unless length (setq length count)) - (let* ((last (1- count)) - (last-req (min last (+ skip length -1))) ;; last requested element - (total (min (- count skip) length))) - (when (and (plusp total) (plusp skip)) ; starting ellipses - (incf total)) - (when (< last-req last) ; last value - (incf total) - (when (< last-req (1- last)) ; ending ellipses - (incf total))) - (let ((index 0) - (elements nil) - (labels nil)) - (declare (type (or simple-vector null) elements labels)) - (when (plusp total) - (setq elements (make-array total :adjustable nil :fill-pointer nil :initial-element nil)) - (setq labels (make-array total :adjustable nil :fill-pointer nil)) - (when (plusp skip) - (setf (aref labels 0) :ellipses) - (incf index)) - (do ((i 0 (1+ i))) - ((> i (- last-req skip))) - (setf (aref elements (+ i index)) (element-at parts (+ i skip))) - (setf (aref labels (+ i index)) (label-at-maybe-with-index parts - (+ i skip)))) - - (when (< last-req last) ; last value - (setf (aref elements (- total 1)) (element-at parts last)) - (setf (aref labels (- total 1)) (label-at-maybe-with-index parts - last)) - (when (< last-req (1- last)) ; ending ellipses or 2nd to last value - (if (= last-req (- last 2)) - (progn - (setf (aref elements (- total 2)) (element-at parts (1- last))) - (setf (aref labels (- total 2)) (label-at-maybe-with-index - parts (1- last)))) - (setf (aref labels (- total 2)) :ellipses))))) - (values elements labels total))))) - - ;;; INSPECTED-DESCRIPTION ;;; @@ -669,15 +670,15 @@ and the last element." (defmethod inspected-parts ((object symbol)) (let ((components - (list (cons "name" (symbol-name object)) - (cons "package" (symbol-package object)) - (cons "value" (if (boundp object) + (list (cons "NAME" (symbol-name object)) + (cons "PACKAGE" (symbol-package object)) + (cons "VALUE" (if (boundp object) (symbol-value object) *inspect-unbound-object-marker*)) - (cons "function" (if (fboundp object) + (cons "FUNCTION" (if (fboundp object) (symbol-function object) *inspect-unbound-object-marker*)) - (cons "plist" (symbol-plist object))))) + (cons "PLIST" (symbol-plist object))))) (list components (length components) :named nil))) (defun inspected-structure-parts (object) @@ -694,14 +695,14 @@ and the last element." (list components (length components) :named nil))) (defun inspected-standard-object-parts (object) - (let ((reversed-components nil) + (let ((components nil) (class-slots (sb-pcl::class-slots (class-of object)))) - (dolist (class-slot class-slots reversed-components) + (dolist (class-slot class-slots components) (let* ((slot-name (slot-value class-slot 'sb-pcl::name)) (slot-value (if (slot-boundp object slot-name) - (slot-value object slot-name) - *inspect-unbound-object-marker*))) - (push (cons slot-name slot-value) reversed-components))))) + (slot-value object slot-name) + *inspect-unbound-object-marker*))) + (push (cons (symbol-name slot-name) slot-value) components))))) (defmethod inspected-parts ((object standard-object)) diff --git a/version.lisp-expr b/version.lisp-expr index 5df4dcd..9ecf95b 100644 --- a/version.lisp-expr +++ b/version.lisp-expr @@ -17,4 +17,4 @@ ;;; checkins which aren't released. (And occasionally for internal ;;; versions, especially for internal versions off the main CVS ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".) -"0.pre8.78" +"0.pre8.79" -- 1.7.10.4