From 425d76468564b6b4d2593c8775f5ce6d763f052d Mon Sep 17 00:00:00 2001
From: Zack Galbreath <zack.galbreath@kitware.com>
Date: Wed, 30 Mar 2016 10:31:25 -0400
Subject: [PATCH] CTestCoverageCollectGCOV: Honor CTEST_EXTRA_COVERAGE_GLOB

Teach CTestCoverageCollectGCOV to honor the
CTEST_EXTRA_COVERAGE_GLOB variable.  When this variable is set,
this module will glob for matching source files that were not
covered and include them in the resulting tar file.
---
 Modules/CTestCoverageCollectGCOV.cmake        | 57 +++++++++++++++++++
 .../TestProject/extra/uncovered1.cpp          |  0
 .../TestProject/uncovered2.cpp                |  0
 Tests/CTestCoverageCollectGCOV/test.cmake.in  |  4 ++
 4 files changed, 61 insertions(+)
 create mode 100644 Tests/CTestCoverageCollectGCOV/TestProject/extra/uncovered1.cpp
 create mode 100644 Tests/CTestCoverageCollectGCOV/TestProject/uncovered2.cpp

diff --git a/Modules/CTestCoverageCollectGCOV.cmake b/Modules/CTestCoverageCollectGCOV.cmake
index f31e432b18..3b74631baf 100644
--- a/Modules/CTestCoverageCollectGCOV.cmake
+++ b/Modules/CTestCoverageCollectGCOV.cmake
@@ -172,6 +172,21 @@ function(ctest_coverage_collect_gcov)
   set(unfiltered_gcov_files)
   file(GLOB_RECURSE unfiltered_gcov_files RELATIVE ${binary_dir} "${coverage_dir}/*.gcov")
 
+  # if CTEST_EXTRA_COVERAGE_GLOB was specified we search for files
+  # that might be uncovered
+  if (DEFINED CTEST_EXTRA_COVERAGE_GLOB)
+    set(uncovered_files)
+    foreach(search_entry IN LISTS CTEST_EXTRA_COVERAGE_GLOB)
+      if(NOT GCOV_QUIET)
+        message("Add coverage glob: ${search_entry}")
+      endif()
+      file(GLOB_RECURSE matching_files "${source_dir}/${search_entry}")
+      if (matching_files)
+        list(APPEND uncovered_files "${matching_files}")
+      endif()
+    endforeach()
+  endif()
+
   set(gcov_files)
   foreach(gcov_file ${unfiltered_gcov_files})
     file(STRINGS ${binary_dir}/${gcov_file} first_line LIMIT_COUNT 1 ENCODING UTF-8)
@@ -195,20 +210,62 @@ function(ctest_coverage_collect_gcov)
       endif()
     endforeach()
 
+    get_filename_component(resolved_source_file "${source_file}" ABSOLUTE)
+    foreach(uncovered_file IN LISTS uncovered_files)
+      get_filename_component(resolved_uncovered_file "${uncovered_file}" ABSOLUTE)
+      if (resolved_uncovered_file STREQUAL resolved_source_file)
+        list(REMOVE_ITEM uncovered_files "${uncovered_file}")
+      endif()
+    endforeach()
+
     if(NOT is_excluded)
       list(APPEND gcov_files ${gcov_file})
     endif()
   endforeach()
 
+  foreach (uncovered_file ${uncovered_files})
+    # Check if this uncovered file should be excluded.
+    set(is_excluded false)
+    foreach(exclude_entry IN LISTS CTEST_CUSTOM_COVERAGE_EXCLUDE)
+      if(uncovered_file MATCHES "${exclude_entry}")
+        set(is_excluded true)
+        if(NOT GCOV_QUIET)
+          message("Excluding coverage for: ${uncovered_file} which matches ${exclude_entry}")
+        endif()
+        break()
+      endif()
+    endforeach()
+    if(is_excluded)
+      continue()
+    endif()
+
+    # Copy from source to binary dir, preserving any intermediate subdirectories.
+    get_filename_component(filename "${uncovered_file}" NAME)
+    get_filename_component(relative_path "${uncovered_file}" DIRECTORY)
+    string(REPLACE "${source_dir}" "" relative_path "${relative_path}")
+    if (relative_path)
+      # Strip leading slash.
+      string(SUBSTRING "${relative_path}" 1 -1 relative_path)
+    endif()
+    file(COPY ${uncovered_file} DESTINATION ${binary_dir}/uncovered/${relative_path})
+    if(relative_path)
+      list(APPEND uncovered_files_for_tar uncovered/${relative_path}/${filename})
+    else()
+      list(APPEND uncovered_files_for_tar uncovered/${filename})
+    endif()
+  endforeach()
+
   # tar up the coverage info with the same date so that the md5
   # sum will be the same for the tar file independent of file time
   # stamps
   string(REPLACE ";" "\n" gcov_files "${gcov_files}")
   string(REPLACE ";" "\n" label_files "${label_files}")
+  string(REPLACE ";" "\n" uncovered_files_for_tar "${uncovered_files_for_tar}")
   file(WRITE "${coverage_dir}/coverage_file_list.txt"
     "${gcov_files}
 ${coverage_dir}/data.json
 ${label_files}
+${uncovered_files_for_tar}
 ")
 
   if (GCOV_QUIET)
diff --git a/Tests/CTestCoverageCollectGCOV/TestProject/extra/uncovered1.cpp b/Tests/CTestCoverageCollectGCOV/TestProject/extra/uncovered1.cpp
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Tests/CTestCoverageCollectGCOV/TestProject/uncovered2.cpp b/Tests/CTestCoverageCollectGCOV/TestProject/uncovered2.cpp
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Tests/CTestCoverageCollectGCOV/test.cmake.in b/Tests/CTestCoverageCollectGCOV/test.cmake.in
index b2e6d6dfec..d48ef61307 100644
--- a/Tests/CTestCoverageCollectGCOV/test.cmake.in
+++ b/Tests/CTestCoverageCollectGCOV/test.cmake.in
@@ -15,7 +15,9 @@ list(APPEND CTEST_CUSTOM_COVERAGE_EXCLUDE
   "/foo/something"
   "/3rdparty/"
   "/bar/somethingelse"
+  "/CMakeFiles/"
 )
+list(APPEND CTEST_EXTRA_COVERAGE_GLOB "*.cpp")
 
 include(CTestCoverageCollectGCOV)
 set(tar_file ${CTEST_BINARY_DIRECTORY}/gcov.tar)
@@ -41,6 +43,8 @@ set(expected_out
   Testing/CoverageInfo/data.json
   Testing/CoverageInfo/extra.cpp.gcov
   Testing/CoverageInfo/main.cpp.gcov
+  uncovered/extra/uncovered1.cpp
+  uncovered/uncovered2.cpp
 )
 
 if("${out}" STREQUAL "${expected_out}")
-- 
GitLab