Browse Source

Merge branch 'maint'

Nicolas Goaziou 8 years ago
parent
commit
2e32709843
2 changed files with 303 additions and 200 deletions
  1. 94 93
      lisp/org-clock.el
  2. 209 107
      testing/lisp/test-org-clock.el

+ 94 - 93
lisp/org-clock.el

@@ -2454,34 +2454,30 @@ from the dynamic block definition."
 	 (multifile (plist-get params :multifile))
 	 (multifile (plist-get params :multifile))
 	 (block (plist-get params :block))
 	 (block (plist-get params :block))
 	 (sort (plist-get params :sort))
 	 (sort (plist-get params :sort))
-	 (header (plist-get  params :header))
-	 (narrow (plist-get params :narrow))
+	 (header (plist-get params :header))
 	 (ws (or (plist-get params :wstart) 1))
 	 (ws (or (plist-get params :wstart) 1))
 	 (ms (or (plist-get params :mstart) 1))
 	 (ms (or (plist-get params :mstart) 1))
 	 (link (plist-get params :link))
 	 (link (plist-get params :link))
 	 (maxlevel (or (plist-get params :maxlevel) 3))
 	 (maxlevel (or (plist-get params :maxlevel) 3))
 	 (emph (plist-get params :emphasize))
 	 (emph (plist-get params :emphasize))
-	 (level-p (plist-get params :level))
+	 (compact? (plist-get params :compact))
+	 (narrow (or (plist-get params :narrow) (and compact? '40!)))
+	 (level? (and (not compact?) (plist-get params :level)))
 	 (timestamp (plist-get params :timestamp))
 	 (timestamp (plist-get params :timestamp))
 	 (properties (plist-get params :properties))
 	 (properties (plist-get params :properties))
-	 (ntcol (max 1 (or (plist-get params :tcolumns) 100)))
-	 (indent (plist-get params :indent))
+	 (ntcol (if compact? 1
+		  (max 1 (or (plist-get params :tcolumns) 100))))
+	 (indent (or compact? (plist-get params :indent)))
 	 (formula (plist-get params :formula))
 	 (formula (plist-get params :formula))
 	 (case-fold-search t)
 	 (case-fold-search t)
-	 range-text total-time tbl level hlc
-	 file-time entries entry headline
-	 recalc narrow-cut-p)
+	 range-text total-time recalc narrow-cut-p)
 
 
-    ;; Implement abbreviations
-    (when (plist-get params :compact)
-      (setq level nil indent t narrow (or narrow '40!) ntcol 1))
-
-    ;; Some consistency test for parameters
+    ;; Some consistency test for parameters.
     (unless (integerp ntcol)
     (unless (integerp ntcol)
       (setq params (plist-put params :tcolumns (setq ntcol 100))))
       (setq params (plist-put params :tcolumns (setq ntcol 100))))
 
 
     (when (and narrow (integerp narrow) link)
     (when (and narrow (integerp narrow) link)
-      ;; We cannot have both integer narrow and link
+      ;; We cannot have both integer narrow and link.
       (message
       (message
        "Using hard narrowing in clocktable to allow for links")
        "Using hard narrowing in clocktable to allow for links")
       (setq narrow (intern (format "%d!" narrow))))
       (setq narrow (intern (format "%d!" narrow))))
@@ -2499,19 +2495,19 @@ from the dynamic block definition."
 	       narrow))))
 	       narrow))))
 
 
     (when block
     (when block
-      ;; Get the range text for the header
+      ;; Get the range text for the header.
       (setq range-text (nth 2 (org-clock-special-range block nil t ws ms))))
       (setq range-text (nth 2 (org-clock-special-range block nil t ws ms))))
 
 
-    ;; Compute the total time
-    (setq total-time (apply '+ (mapcar 'cadr tables)))
+    ;; Compute the total time.
+    (setq total-time (apply #'+ (mapcar #'cadr tables)))
 
 
-    ;; Now we need to output this tsuff
+    ;; Now we need to output this tsuff.
     (goto-char ipos)
     (goto-char ipos)
 
 
-    ;; Insert the text *before* the actual table
+    ;; Insert the text *before* the actual table.
     (insert-before-markers
     (insert-before-markers
      (or header
      (or header
-	 ;; Format the standard header
+	 ;; Format the standard header.
 	 (concat
 	 (concat
 	  "#+CAPTION: "
 	  "#+CAPTION: "
 	  (nth 9 lwords) " ["
 	  (nth 9 lwords) " ["
@@ -2525,104 +2521,109 @@ from the dynamic block definition."
     ;; Insert the narrowing line
     ;; Insert the narrowing line
     (when (and narrow (integerp narrow) (not narrow-cut-p))
     (when (and narrow (integerp narrow) (not narrow-cut-p))
       (insert-before-markers
       (insert-before-markers
-       "|"                            ; table line starter
-       (if multifile "|" "")          ; file column, maybe
-       (if level-p   "|" "")          ; level column, maybe
-       (if timestamp "|" "")          ; timestamp column, maybe
-       (if properties (make-string (length properties) ?|) "")  ;properties columns, maybe
-       (format "<%d>| |\n" narrow)))  ; headline and time columns
+       "|"				;table line starter
+       (if multifile "|" "")		;file column, maybe
+       (if level? "|" "")		;level column, maybe
+       (if timestamp "|" "")		;timestamp column, maybe
+       (if properties (make-string (length properties) ?|) "") ;properties columns, maybe
+       (format "<%d>| |\n" narrow)))	; headline and time columns
 
 
     ;; Insert the table header line
     ;; Insert the table header line
     (insert-before-markers
     (insert-before-markers
-     "|"                              ; table line starter
-     (if multifile (concat (nth 1 lwords) "|") "")  ; file column, maybe
-     (if level-p   (concat (nth 2 lwords) "|") "")  ; level column, maybe
-     (if timestamp (concat (nth 3 lwords) "|") "")  ; timestamp column, maybe
-     (if properties (concat (mapconcat 'identity properties "|") "|") "") ;properties columns, maybe
+     "|"					   ;table line starter
+     (if multifile (concat (nth 1 lwords) "|") "") ;file column, maybe
+     (if level? (concat (nth 2 lwords) "|") "") ;level column, maybe
+     (if timestamp (concat (nth 3 lwords) "|") "") ;timestamp column, maybe
+     (if properties			;properties columns, maybe
+	 (concat (mapconcat #'identity properties "|") "|")
+       "")
      (nth 4 lwords) "|"			;headline
      (nth 4 lwords) "|"			;headline
      (nth 5 lwords) "|"			;time column
      (nth 5 lwords) "|"			;time column
-     (make-string (1- (min maxlevel (or ntcol 100))) ?|)
+     (make-string (max 0 (1- (min maxlevel (or ntcol 100))))
+		  ?|)			;other time columns
      (if (eq formula '%) "%|\n" "\n"))
      (if (eq formula '%) "%|\n" "\n"))
 
 
     ;; Insert the total time in the table
     ;; Insert the total time in the table
     (insert-before-markers
     (insert-before-markers
-     "|-\n"                            ; a hline
-     "|"                               ; table line starter
+     "|-\n"				;a hline
+     "|"				;table line starter
      (if multifile (concat "| " (nth 6 lwords) " ") "")
      (if multifile (concat "| " (nth 6 lwords) " ") "")
-					; file column, maybe
-     (if level-p   "|"      "")        ; level column, maybe
-     (if timestamp "|"      "")        ; timestamp column, maybe
-     (make-string (length properties) ?|)  ; properties columns, maybe
-     (concat (format org-clock-total-time-cell-format (nth 7 lwords))  "| ") ; instead of a headline
+					;file column, maybe
+     (if level?   "|"      "")		;level column, maybe
+     (if timestamp "|"      "")		;timestamp column, maybe
+     (make-string (length properties) ?|) ;properties columns, maybe
+     (concat (format org-clock-total-time-cell-format (nth 7 lwords))
+	     "| ")
      (format org-clock-total-time-cell-format
      (format org-clock-total-time-cell-format
 	     (org-duration-from-minutes (or total-time 0))) ;time
 	     (org-duration-from-minutes (or total-time 0))) ;time
      "|"
      "|"
-     (make-string (1- (min maxlevel (or ntcol 100))) ?|)
+     (make-string (max 0 (1- (min maxlevel (or ntcol 100)))) ?|)
      (cond ((not (eq formula '%)) "")
      (cond ((not (eq formula '%)) "")
 	   ((or (not total-time) (= total-time 0)) "0.0|")
 	   ((or (not total-time) (= total-time 0)) "0.0|")
 	   (t  "100.0|"))
 	   (t  "100.0|"))
      "\n")
      "\n")
 
 
-    ;; Now iterate over the tables and insert the data
-    ;; but only if any time has been collected
+    ;; Now iterate over the tables and insert the data but only if any
+    ;; time has been collected.
     (when (and total-time (> total-time 0))
     (when (and total-time (> total-time 0))
-
-      (while (setq tbl (pop tables))
-	;; now tbl is the table resulting from one file.
-	(setq file-time (nth 1 tbl))
+      (pcase-dolist (`(,file-name ,file-time ,entries) tables)
 	(when (or (and file-time (> file-time 0))
 	(when (or (and file-time (> file-time 0))
 		  (not (plist-get params :fileskip0)))
 		  (not (plist-get params :fileskip0)))
-	  (insert-before-markers "|-\n")  ; a hline because a new file starts
-	  ;; First the file time, if we have multiple files
+	  (insert-before-markers "|-\n") ;hline at new file
+	  ;; First the file time, if we have multiple files.
 	  (when multifile
 	  (when multifile
-	    ;; Summarize the time collected from this file
+	    ;; Summarize the time collected from this file.
 	    (insert-before-markers
 	    (insert-before-markers
 	     (format (concat "| %s %s | %s%s"
 	     (format (concat "| %s %s | %s%s"
-			     (format org-clock-file-time-cell-format (nth 8 lwords))
+			     (format org-clock-file-time-cell-format
+				     (nth 8 lwords))
 			     " | *%s*|\n")
 			     " | *%s*|\n")
-		     (file-name-nondirectory (car tbl))
-		     (if level-p   "| " "") ; level column, maybe
-		     (if timestamp "| " "") ; timestamp column, maybe
-		     (if properties (make-string (length properties) ?|) "")  ;properties columns, maybe
-		     (org-duration-from-minutes (nth 1 tbl))))) ; the time
+		     (file-name-nondirectory file-name)
+		     (if level?   "| " "")  ;level column, maybe
+		     (if timestamp "| " "") ;timestamp column, maybe
+		     (if properties	    ;properties columns, maybe
+			 (make-string (length properties) ?|)
+		       "")
+		     (org-duration-from-minutes file-time)))) ;time
 
 
 	  ;; Get the list of node entries and iterate over it
 	  ;; Get the list of node entries and iterate over it
-	  (setq entries (nth 2 tbl))
-	  (while (setq entry (pop entries))
-	    (setq level (car entry)
-		  headline (nth 1 entry)
-		  hlc (if emph (or (cdr (assoc level hlchars)) "") ""))
-	    (when narrow-cut-p
-	      (if (and (string-match (concat "\\`" org-bracket-link-regexp
-					     "\\'")
-				     headline)
-		       (match-end 3))
-		  (setq headline
-			(format "[[%s][%s]]"
-				(match-string 1 headline)
-				(org-shorten-string (match-string 3 headline)
-						    narrow)))
-		(setq headline (org-shorten-string headline narrow))))
-	    (insert-before-markers
-	     "|"                      ; start the table line
-	     (if multifile "|" "")    ; free space for file name column?
-	     (if level-p (format "%d|" (car entry)) "")   ; level, maybe
-	     (if timestamp (concat (nth 2 entry) "|") "") ; timestamp, maybe
-	     (if properties
-		 (concat
-		  (mapconcat
-		   (lambda (p) (or (cdr (assoc p (nth 4 entry))) ""))
-		   properties "|") "|") "")  ;properties columns, maybe
-	     (if indent (org-clocktable-indent-string level) "") ; indentation
-	     hlc headline hlc "|"                                ; headline
-	     (make-string (1- (min ntcol level)) ?|) ; empty fields for higher levels
-	     hlc (org-duration-from-minutes (nth 3 entry)) hlc ; time
-	     (make-string (1+ (- maxlevel level)) ?|)
-	     (if (eq formula '%)
-		 (format "%.1f |" (* 100 (/ (nth 3 entry) (float total-time))))
-	       "")
-	     "\n"			; close line
-	     )))))
+	  (when (> maxlevel 0)
+	    (pcase-dolist (`(,level ,headline ,ts ,time . ,props) entries)
+	      (when narrow-cut-p
+		(setq headline
+		      (if (and (string-match
+				(format "\\`%s\\'" org-bracket-link-regexp)
+				headline)
+			       (match-end 3))
+			  (format "[[%s][%s]]"
+				  (match-string 1 headline)
+				  (org-shorten-string (match-string 3 headline)
+						      narrow))
+			(org-shorten-string headline narrow))))
+	      (let ((hlc (if emph (or (cdr (assoc level hlchars)) "") "")))
+		(insert-before-markers
+		 "|"		       ;start the table line
+		 (if multifile "|" "") ;free space for file name column?
+		 (if level? (format "%d|" level) "") ;level, maybe
+		 (if timestamp (concat ts "|") "")   ;timestamp, maybe
+		 (if properties		;properties columns, maybe
+		     (concat (mapconcat (lambda (p)
+					  (or (cdr (assoc p props)) ""))
+					properties
+					"|")
+			     "|")
+		   "")
+		 (if indent		;indentation
+		     (org-clocktable-indent-string level)
+		   "")
+		 hlc headline hlc "|"			 ;headline
+		 (make-string (1- (min ntcol level)) ?|) ;empty fields for higher levels
+		 hlc (org-duration-from-minutes time) hlc ; time
+		 (make-string (1+ (- maxlevel level)) ?|)
+		 (if (eq formula '%)
+		     (format "%.1f |" (* 100 (/ time (float total-time))))
+		   "")
+		 "\n")))))))
     (delete-char -1)
     (delete-char -1)
     (cond
     (cond
      ;; Possibly rescue old formula?
      ;; Possibly rescue old formula?
@@ -2638,12 +2639,12 @@ from the dynamic block definition."
       (setq recalc t))
       (setq recalc t))
      (t
      (t
       (user-error "Invalid :formula parameter in clocktable")))
       (user-error "Invalid :formula parameter in clocktable")))
-    ;; Back to beginning, align the table, recalculate if necessary
+    ;; Back to beginning, align the table, recalculate if necessary.
     (goto-char ipos)
     (goto-char ipos)
     (skip-chars-forward "^|")
     (skip-chars-forward "^|")
     (org-table-align)
     (org-table-align)
     (when org-hide-emphasis-markers
     (when org-hide-emphasis-markers
-      ;; we need to align a second time
+      ;; We need to align a second time.
       (org-table-align))
       (org-table-align))
     (when sort
     (when sort
       (save-excursion
       (save-excursion

+ 209 - 107
testing/lisp/test-org-clock.el

@@ -47,16 +47,16 @@ range.  INPUT2 can be omitted if clock hasn't finished yet.
 
 
 Return the clock line as a string."
 Return the clock line as a string."
   (let* ((beg (org-test-clock-create-timestamp input1 t t))
   (let* ((beg (org-test-clock-create-timestamp input1 t t))
-	 (end (and input2 (org-test-clock-create-timestamp input2 t t)))
-	 (sec-diff (and input2 (floor (- (org-time-string-to-seconds end)
-					 (org-time-string-to-seconds beg))))))
+         (end (and input2 (org-test-clock-create-timestamp input2 t t)))
+         (sec-diff (and input2 (floor (- (org-time-string-to-seconds end)
+                                         (org-time-string-to-seconds beg))))))
     (concat org-clock-string " " beg
     (concat org-clock-string " " beg
-	    (when end
-	      (concat "--" end " => "
-		      (format "%2d:%02d"
-			      (/ sec-diff 3600)
-			      (/ (mod sec-diff 3600) 60))))
-	    "\n")))
+            (when end
+              (concat "--" end " => "
+                      (format "%2d:%02d"
+                              (/ sec-diff 3600)
+                              (/ (mod sec-diff 3600) 60))))
+            "\n")))
 
 
 (defun test-org-clock-clocktable-contents-at-point (options)
 (defun test-org-clock-clocktable-contents-at-point (options)
   "Return contents of a clocktable at point.
   "Return contents of a clocktable at point.
@@ -86,119 +86,119 @@ contents.  The clocktable doesn't appear in the buffer."
   (should-not
   (should-not
    (org-test-with-temp-text "* H"
    (org-test-with-temp-text "* H"
      (let ((org-clock-into-drawer nil)
      (let ((org-clock-into-drawer nil)
-	   (org-log-into-drawer nil))
+           (org-log-into-drawer nil))
        (org-clock-into-drawer))))
        (org-clock-into-drawer))))
   (should-not
   (should-not
    (org-test-with-temp-text "* H"
    (org-test-with-temp-text "* H"
      (let ((org-clock-into-drawer nil)
      (let ((org-clock-into-drawer nil)
-	   (org-log-into-drawer t))
+           (org-log-into-drawer t))
        (org-clock-into-drawer))))
        (org-clock-into-drawer))))
   (should-not
   (should-not
    (org-test-with-temp-text "* H"
    (org-test-with-temp-text "* H"
      (let ((org-clock-into-drawer nil)
      (let ((org-clock-into-drawer nil)
-	   (org-log-into-drawer "BAR"))
+           (org-log-into-drawer "BAR"))
        (org-clock-into-drawer))))
        (org-clock-into-drawer))))
   ;; When `org-clock-into-drawer' is a string, use it
   ;; When `org-clock-into-drawer' is a string, use it
   ;; unconditionally.
   ;; unconditionally.
   (should
   (should
    (equal "FOO"
    (equal "FOO"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer "FOO")
-		  (org-log-into-drawer nil))
-	      (org-clock-into-drawer)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer "FOO")
+                  (org-log-into-drawer nil))
+              (org-clock-into-drawer)))))
   (should
   (should
    (equal "FOO"
    (equal "FOO"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer "FOO")
-		  (org-log-into-drawer t))
-	      (org-clock-into-drawer)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer "FOO")
+                  (org-log-into-drawer t))
+              (org-clock-into-drawer)))))
   (should
   (should
    (equal "FOO"
    (equal "FOO"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer "FOO")
-		  (org-log-into-drawer "BAR"))
-	      (org-clock-into-drawer)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer "FOO")
+                  (org-log-into-drawer "BAR"))
+              (org-clock-into-drawer)))))
   ;; When `org-clock-into-drawer' is an integer, return it.
   ;; When `org-clock-into-drawer' is an integer, return it.
   (should
   (should
    (= 1
    (= 1
       (org-test-with-temp-text "* H"
       (org-test-with-temp-text "* H"
-	(let ((org-clock-into-drawer 1)
-	      (org-log-into-drawer nil))
-	  (org-clock-into-drawer)))))
+        (let ((org-clock-into-drawer 1)
+              (org-log-into-drawer nil))
+          (org-clock-into-drawer)))))
   (should
   (should
    (= 1
    (= 1
       (org-test-with-temp-text "* H"
       (org-test-with-temp-text "* H"
-	(let ((org-clock-into-drawer 1)
-	      (org-log-into-drawer t))
-	  (org-clock-into-drawer)))))
+        (let ((org-clock-into-drawer 1)
+              (org-log-into-drawer t))
+          (org-clock-into-drawer)))))
   (should
   (should
    (= 1
    (= 1
       (org-test-with-temp-text "* H"
       (org-test-with-temp-text "* H"
-	(let ((org-clock-into-drawer 1)
-	      (org-log-into-drawer "BAR"))
-	  (org-clock-into-drawer)))))
+        (let ((org-clock-into-drawer 1)
+              (org-log-into-drawer "BAR"))
+          (org-clock-into-drawer)))))
   ;; Otherwise, any non-nil value defaults to `org-log-into-drawer' or
   ;; Otherwise, any non-nil value defaults to `org-log-into-drawer' or
   ;; "LOGBOOK" if it is nil.
   ;; "LOGBOOK" if it is nil.
   (should
   (should
    (equal "LOGBOOK"
    (equal "LOGBOOK"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer t)
-		  (org-log-into-drawer nil))
-	      (org-clock-into-drawer)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer t)
+                  (org-log-into-drawer nil))
+              (org-clock-into-drawer)))))
   (should
   (should
    (equal "LOGBOOK"
    (equal "LOGBOOK"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer t)
-		  (org-log-into-drawer t))
-	      (org-clock-into-drawer)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer t)
+                  (org-log-into-drawer t))
+              (org-clock-into-drawer)))))
   (should
   (should
    (equal "FOO"
    (equal "FOO"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer t)
-		  (org-log-into-drawer "FOO"))
-	      (org-clock-into-drawer)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer t)
+                  (org-log-into-drawer "FOO"))
+              (org-clock-into-drawer)))))
   ;; A non-nil "CLOCK_INTO_DRAWER" property overrides
   ;; A non-nil "CLOCK_INTO_DRAWER" property overrides
   ;; `org-clock-into-drawer' value.
   ;; `org-clock-into-drawer' value.
   (should
   (should
    (equal "LOGBOOK"
    (equal "LOGBOOK"
-	  (org-test-with-temp-text
-	      "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: t\n:END:"
-	    (let ((org-clock-into-drawer nil)
-		  (org-log-into-drawer nil))
-	      (org-clock-into-drawer)))))
+          (org-test-with-temp-text
+              "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: t\n:END:"
+            (let ((org-clock-into-drawer nil)
+                  (org-log-into-drawer nil))
+              (org-clock-into-drawer)))))
   (should
   (should
    (equal "FOO"
    (equal "FOO"
-	  (org-test-with-temp-text
-	      "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: FOO\n:END:"
-	    (let ((org-clock-into-drawer nil)
-		  (org-log-into-drawer nil))
-	      (org-clock-into-drawer)))))
+          (org-test-with-temp-text
+              "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: FOO\n:END:"
+            (let ((org-clock-into-drawer nil)
+                  (org-log-into-drawer nil))
+              (org-clock-into-drawer)))))
   (should-not
   (should-not
    (org-test-with-temp-text
    (org-test-with-temp-text
        "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: nil\n:END:"
        "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: nil\n:END:"
      (let ((org-clock-into-drawer t)
      (let ((org-clock-into-drawer t)
-	   (org-log-into-drawer nil))
+           (org-log-into-drawer nil))
        (org-clock-into-drawer))))
        (org-clock-into-drawer))))
   ;; "CLOCK_INTO_DRAWER" can be inherited.
   ;; "CLOCK_INTO_DRAWER" can be inherited.
   (should
   (should
    (equal "LOGBOOK"
    (equal "LOGBOOK"
-	  (org-test-with-temp-text
-	      "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: t\n:END:\n** H2<point>"
-	    (let ((org-clock-into-drawer nil)
-		  (org-log-into-drawer nil))
-	      (org-clock-into-drawer)))))
+          (org-test-with-temp-text
+              "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: t\n:END:\n** H2<point>"
+            (let ((org-clock-into-drawer nil)
+                  (org-log-into-drawer nil))
+              (org-clock-into-drawer)))))
   (should
   (should
    (equal "FOO"
    (equal "FOO"
-	  (org-test-with-temp-text
-	      "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: FOO\n:END:\n** H2<point>"
-	    (let ((org-clock-into-drawer nil)
-		  (org-log-into-drawer nil))
-	      (org-clock-into-drawer)))))
+          (org-test-with-temp-text
+              "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: FOO\n:END:\n** H2<point>"
+            (let ((org-clock-into-drawer nil)
+                  (org-log-into-drawer nil))
+              (org-clock-into-drawer)))))
   (should-not
   (should-not
    (org-test-with-temp-text
    (org-test-with-temp-text
        "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: nil\n:END:\n** H2<point>"
        "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: nil\n:END:\n** H2<point>"
      (let ((org-clock-into-drawer t)
      (let ((org-clock-into-drawer t)
-	   (org-log-into-drawer nil))
+           (org-log-into-drawer nil))
        (org-clock-into-drawer)))))
        (org-clock-into-drawer)))))
 
 
 (ert-deftest test-org-clock/drawer-name ()
 (ert-deftest test-org-clock/drawer-name ()
@@ -208,64 +208,64 @@ contents.  The clocktable doesn't appear in the buffer."
   (should-not
   (should-not
    (org-test-with-temp-text "* H"
    (org-test-with-temp-text "* H"
      (let ((org-clock-into-drawer nil)
      (let ((org-clock-into-drawer nil)
-	   (org-log-into-drawer nil))
+           (org-log-into-drawer nil))
        (org-clock-drawer-name))))
        (org-clock-drawer-name))))
   (should-not
   (should-not
    (org-test-with-temp-text "* H"
    (org-test-with-temp-text "* H"
      (let ((org-clock-into-drawer nil)
      (let ((org-clock-into-drawer nil)
-	   (org-log-into-drawer t))
+           (org-log-into-drawer t))
        (org-clock-drawer-name))))
        (org-clock-drawer-name))))
   (should-not
   (should-not
    (org-test-with-temp-text "* H"
    (org-test-with-temp-text "* H"
      (let ((org-clock-into-drawer nil)
      (let ((org-clock-into-drawer nil)
-	   (org-log-into-drawer "FOO"))
+           (org-log-into-drawer "FOO"))
        (org-clock-drawer-name))))
        (org-clock-drawer-name))))
   ;; A string value for `org-clock-into-drawer' means to use it
   ;; A string value for `org-clock-into-drawer' means to use it
   ;; unconditionally.
   ;; unconditionally.
   (should
   (should
    (equal "FOO"
    (equal "FOO"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer "FOO")
-		  (org-log-into-drawer nil))
-	      (org-clock-drawer-name)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer "FOO")
+                  (org-log-into-drawer nil))
+              (org-clock-drawer-name)))))
   (should
   (should
    (equal "FOO"
    (equal "FOO"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer "FOO")
-		  (org-log-into-drawer t))
-	      (org-clock-drawer-name)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer "FOO")
+                  (org-log-into-drawer t))
+              (org-clock-drawer-name)))))
   (should
   (should
    (equal "FOO"
    (equal "FOO"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer "FOO")
-		  (org-log-into-drawer "BAR"))
-	      (org-clock-drawer-name)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer "FOO")
+                  (org-log-into-drawer "BAR"))
+              (org-clock-drawer-name)))))
   ;; When the value in `org-clock-into-drawer' is a number, re-use
   ;; When the value in `org-clock-into-drawer' is a number, re-use
   ;; `org-log-into-drawer' or use default "LOGBOOK" value.
   ;; `org-log-into-drawer' or use default "LOGBOOK" value.
   (should
   (should
    (equal "FOO"
    (equal "FOO"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer 1)
-		  (org-log-into-drawer "FOO"))
-	      (org-clock-drawer-name)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer 1)
+                  (org-log-into-drawer "FOO"))
+              (org-clock-drawer-name)))))
   (should
   (should
    (equal "LOGBOOK"
    (equal "LOGBOOK"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer 1)
-		  (org-log-into-drawer t))
-	      (org-clock-drawer-name)))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer 1)
+                  (org-log-into-drawer t))
+              (org-clock-drawer-name)))))
   (should
   (should
    (equal "LOGBOOK"
    (equal "LOGBOOK"
-	  (org-test-with-temp-text "* H"
-	    (let ((org-clock-into-drawer 1)
-		  (org-log-into-drawer nil))
-	      (org-clock-drawer-name))))))
+          (org-test-with-temp-text "* H"
+            (let ((org-clock-into-drawer 1)
+                  (org-log-into-drawer nil))
+              (org-clock-drawer-name))))))
 
 
 
 
 ;;; Clocktable
 ;;; Clocktable
 
 
-(ert-deftest test-org-clock/clocktable ()
-  "Test clocktable specifications."
+(ert-deftest test-org-clock/clocktable/ranges ()
+  "Test ranges in Clock table."
   ;; Relative time: Previous two days.
   ;; Relative time: Previous two days.
   (should
   (should
    (equal
    (equal
@@ -277,7 +277,7 @@ contents.  The clocktable doesn't appear in the buffer."
 | Foo                          |        | 8:00 |
 | Foo                          |        | 8:00 |
 "
 "
     (org-test-with-temp-text
     (org-test-with-temp-text
-	"* Relative times in clocktable\n** Foo\n<point>"
+        "* Relative times in clocktable\n** Foo\n<point>"
       (insert (org-test-clock-create-clock "-3d 8:00" "-3d 12:00"))
       (insert (org-test-clock-create-clock "-3d 8:00" "-3d 12:00"))
       (insert (org-test-clock-create-clock "-2d 15:00" "-2d 18:00"))
       (insert (org-test-clock-create-clock "-2d 15:00" "-2d 18:00"))
       (insert (org-test-clock-create-clock "-1d 8:00" "-1d 13:00"))
       (insert (org-test-clock-create-clock "-1d 8:00" "-1d 13:00"))
@@ -294,7 +294,7 @@ contents.  The clocktable doesn't appear in the buffer."
 | Foo                          |        | 6:00 |
 | Foo                          |        | 6:00 |
 "
 "
     (org-test-with-temp-text
     (org-test-with-temp-text
-	"* Relative times in clocktable\n** Foo\n<point>"
+        "* Relative times in clocktable\n** Foo\n<point>"
       (insert (org-test-clock-create-clock "-2d 15:00" "-2d 18:00"))
       (insert (org-test-clock-create-clock "-2d 15:00" "-2d 18:00"))
       (insert (org-test-clock-create-clock "-1d 8:00" "-1d 13:00"))
       (insert (org-test-clock-create-clock "-1d 8:00" "-1d 13:00"))
       (insert (org-test-clock-create-clock ". 1:00" ". 2:00"))
       (insert (org-test-clock-create-clock ". 1:00" ". 2:00"))
@@ -311,11 +311,14 @@ contents.  The clocktable doesn't appear in the buffer."
 | Foo                          |        | 6:00 |
 | Foo                          |        | 6:00 |
 "
 "
     (org-test-with-temp-text
     (org-test-with-temp-text
-	"* Relative times in clocktable\n** Foo\n<point>"
+        "* Relative times in clocktable\n** Foo\n<point>"
       (insert (org-test-clock-create-clock "-10y 15:00" "-10y 18:00"))
       (insert (org-test-clock-create-clock "-10y 15:00" "-10y 18:00"))
       (insert (org-test-clock-create-clock "-2d 15:00" "-2d 18:00"))
       (insert (org-test-clock-create-clock "-2d 15:00" "-2d 18:00"))
       (test-org-clock-clocktable-contents-at-point
       (test-org-clock-clocktable-contents-at-point
-       ":block untilnow :indent nil"))))
+       ":block untilnow :indent nil")))))
+
+(ert-deftest test-org-clock/clocktable/tags ()
+  "Test \":tags\" parameter in Clock table."
   ;; Test tag filtering.
   ;; Test tag filtering.
   (should
   (should
    (equal
    (equal
@@ -331,7 +334,10 @@ contents.  The clocktable doesn't appear in the buffer."
       (insert (org-test-clock-create-clock ". 2:00" ". 4:00"))
       (insert (org-test-clock-create-clock ". 2:00" ". 4:00"))
       (goto-line 2)
       (goto-line 2)
       (test-org-clock-clocktable-contents-at-point
       (test-org-clock-clocktable-contents-at-point
-       ":tags \"tag\" :indent nil"))))
+       ":tags \"tag\" :indent nil")))))
+
+(ert-deftest test-org-clock/clocktable/scope ()
+  "Test \":scope\" parameter in Clock table."
   ;; Test `file-with-archives' scope.  In particular, preserve "TBLFM"
   ;; Test `file-with-archives' scope.  In particular, preserve "TBLFM"
   ;; line, and ignore "file" column.
   ;; line, and ignore "file" column.
   (should
   (should
@@ -343,7 +349,7 @@ contents.  The clocktable doesn't appear in the buffer."
 | Test         | 704d 9:01   | foo |
 | Test         | 704d 9:01   | foo |
 "
 "
     (org-test-with-temp-text-in-file
     (org-test-with-temp-text-in-file
-	"* Test
+        "* Test
 CLOCK: [2012-03-29 Thu 16:40]--[2014-03-04 Thu 00:41] => 16905:01
 CLOCK: [2012-03-29 Thu 16:40]--[2014-03-04 Thu 00:41] => 16905:01
 
 
 #+BEGIN: clocktable :scope file-with-archives
 #+BEGIN: clocktable :scope file-with-archives
@@ -356,7 +362,103 @@ CLOCK: [2012-03-29 Thu 16:40]--[2014-03-04 Thu 00:41] => 16905:01
       (forward-line 2)
       (forward-line 2)
       (buffer-substring-no-properties
       (buffer-substring-no-properties
        (point) (progn (goto-char (point-max))
        (point) (progn (goto-char (point-max))
-		      (line-beginning-position -1))))))
+                      (line-beginning-position -1)))))))
+
+(ert-deftest test-org-clock/clocktable/maxlevel ()
+  "Test \":maxlevel\" parameter in Clock table."
+  (should
+   (equal "| Headline     | Time   |      |   |
+|--------------+--------+------+---|
+| *Total time* | *6:00* |      |   |
+|--------------+--------+------+---|
+| Foo          | 6:00   |      |   |
+| \\_  Bar      |        | 2:00 |   |
+"
+          (org-test-with-temp-text
+              "
+* Foo
+CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] =>  4:00
+** Bar
+CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] =>  2:00
+
+* Report
+<point>#+BEGIN: clocktable :maxlevel 3
+#+END:"
+            (org-update-dblock)
+            (buffer-substring-no-properties
+	     (line-beginning-position 3)
+	     (progn (goto-char (point-max))
+		    (line-beginning-position))))))
+  (should
+   (equal "| Headline     | Time   |      |
+|--------------+--------+------|
+| *Total time* | *6:00* |      |
+|--------------+--------+------|
+| Foo          | 6:00   |      |
+| \\_  Bar      |        | 2:00 |
+"
+          (org-test-with-temp-text
+              "
+* Foo
+CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] =>  4:00
+** Bar
+CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] =>  2:00
+
+* Report
+<point>#+BEGIN: clocktable :maxlevel 2
+#+END:"
+            (org-update-dblock)
+            (buffer-substring-no-properties
+	     (line-beginning-position 3)
+	     (progn (goto-char (point-max))
+		    (line-beginning-position))))))
+  (should
+   (equal "| Headline     | Time   |
+|--------------+--------|
+| *Total time* | *6:00* |
+|--------------+--------|
+| Foo          | 6:00   |
+"
+          (org-test-with-temp-text
+              "
+* Foo
+CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] =>  4:00
+** Bar
+CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] =>  2:00
+
+* Report
+<point>#+BEGIN: clocktable :maxlevel 1
+#+END:"
+            (org-update-dblock)
+	    (buffer-substring-no-properties
+	     (line-beginning-position 3)
+	     (progn (goto-char (point-max))
+		    (line-beginning-position))))))
+  ;; Special ":maxlevel 0" case: only report total file time.
+  (should
+   (equal "| Headline     | Time   |
+|--------------+--------|
+| *Total time* | *6:00* |
+|--------------+--------|
+"
+          (org-test-with-temp-text
+              "
+* Foo
+CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] =>  4:00
+** Bar
+CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] =>  2:00
+
+* Report
+<point>#+BEGIN: clocktable :maxlevel 0
+#+END:"
+            (org-update-dblock)
+            (buffer-substring-no-properties
+	     (line-beginning-position 3)
+	     (progn (goto-char (point-max))
+		    (line-beginning-position)))))))
+
+(ert-deftest test-org-clock/clocktable/formula ()
+  "Test \":formula\" parameter in Clock table."
   ;; Test ":formula %".  Handle various duration formats.
   ;; Test ":formula %".  Handle various duration formats.
   (should
   (should
    (equal
    (equal
@@ -368,7 +470,7 @@ CLOCK: [2012-03-29 Thu 16:40]--[2014-03-04 Thu 00:41] => 16905:01
 | Bar          |   2:00 |  33.3 |
 | Bar          |   2:00 |  33.3 |
 "
 "
     (org-test-with-temp-text
     (org-test-with-temp-text
-	"* Foo
+        "* Foo
   CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] =>  4:00
   CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] =>  4:00
 * Bar
 * Bar
   CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] =>  2:00
   CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] =>  2:00
@@ -379,7 +481,7 @@ CLOCK: [2012-03-29 Thu 16:40]--[2014-03-04 Thu 00:41] => 16905:01
 "
 "
       (org-update-dblock)
       (org-update-dblock)
       (buffer-substring-no-properties (line-beginning-position 3)
       (buffer-substring-no-properties (line-beginning-position 3)
-				      (line-beginning-position 9)))))
+                                      (line-beginning-position 9)))))
   (should
   (should
    (equal
    (equal
     "| Headline     | Time      |     % |
     "| Headline     | Time      |     % |
@@ -390,7 +492,7 @@ CLOCK: [2012-03-29 Thu 16:40]--[2014-03-04 Thu 00:41] => 16905:01
 | Bar          | 2:00      |   7.1 |
 | Bar          | 2:00      |   7.1 |
 "
 "
     (org-test-with-temp-text
     (org-test-with-temp-text
-	"
+        "
 * Foo
 * Foo
   CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00
   CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00
 * Bar
 * Bar
@@ -400,7 +502,7 @@ CLOCK: [2012-03-29 Thu 16:40]--[2014-03-04 Thu 00:41] => 16905:01
 #+END:"
 #+END:"
       (org-update-dblock)
       (org-update-dblock)
       (buffer-substring-no-properties (line-beginning-position 3)
       (buffer-substring-no-properties (line-beginning-position 3)
-				      (line-beginning-position 9))))))
+                                      (line-beginning-position 9))))))
 
 
 (provide 'test-org-clock)
 (provide 'test-org-clock)
 ;;; test-org-clock.el end here
 ;;; test-org-clock.el end here