]> granicus.if.org Git - graphviz/commitdiff
add a test case to keep .vcxproj and .vcxproj.filters sources in sync
authorMatthew Fernandez <matthew.fernandez@gmail.com>
Tue, 27 Apr 2021 02:38:01 +0000 (19:38 -0700)
committerMatthew Fernandez <matthew.fernandez@gmail.com>
Mon, 3 May 2021 14:08:34 +0000 (07:08 -0700)
Inspired by reviewing !1925, I realized there are few safeguards for those of us
editing the .vcxproj files while not on Windows. This commit introduces a basic
seatbelt to try to keep these files in sync. In future we could extend this to
check for file existence and other consistency.

rtest/gvtest.py
rtest/test_regression.py

index 4d3b631ea92137c1872e26cf3e1b465537670466..d7239da9ed9935948574c74b123105ce04a07074 100644 (file)
@@ -7,6 +7,9 @@ import subprocess
 import tempfile
 from typing import List, Optional, Tuple
 
+ROOT = Path(__file__).resolve().parent.parent
+"""absolute path to the root of the repository"""
+
 def compile_c(src: Path, cflags: List[str] = [], link: List[str] = [],
             dst: Optional[Path] = None) -> Path:
   """compile a C program"""
index 84d5b295a63b21d761549b25cd420c9dd53ff6dd..5d6a625d0300f7df94e3a19fe9a5c38188d811e5 100644 (file)
@@ -8,10 +8,12 @@ import stat
 import subprocess
 import sys
 import tempfile
+from typing import List, Optional, Tuple
+import xml.etree.ElementTree as ET
 import pytest
 
 sys.path.append(os.path.dirname(__file__))
-from gvtest import run_c #pylint: disable=C0413
+from gvtest import ROOT, run_c #pylint: disable=C0413
 
 # The terminology used in rtest.py is a little inconsistent. At the
 # end it reports the total number of tests, the number of "failures"
@@ -858,3 +860,65 @@ def test_xdot_json():
                    '{P : [0.000000,0.000000,0.000000,36.000000,54.000000,' \
                      '36.000000,54.000000,0.000000]}\n'                    \
                    ']\n'
+
+# find all .vcxproj files
+VCXPROJS: List[Path] = []
+for root, _, files in os.walk(ROOT):
+  for stem in files:
+    # skip files generated by MSBuild itself
+    if stem in ("VCTargetsPath.vcxproj", "CompilerIdC.vcxproj",
+                "CompilerIdCXX.vcxproj"):
+      continue
+    p = Path(root) / stem
+    if p.suffix != ".vcxproj":
+      continue
+    VCXPROJS.append(p)
+
+@pytest.mark.parametrize("vcxproj", VCXPROJS)
+def test_vcxproj_inclusive(vcxproj: Path):
+  """check .vcxproj files correspond to .vcxproj.filters files"""
+
+  def fix_sep(path: str) -> str:
+    """translate Windows path separators to ease running this on non-Windows"""
+    return path.replace("\\", os.sep)
+
+  # FIXME: files missing a filters file
+  FILTERS_WAIVERS = (Path("lib/version/version.vcxproj"),)
+
+  filters = Path(f"{str(vcxproj)}.filters")
+  if vcxproj.relative_to(ROOT) not in FILTERS_WAIVERS:
+    assert filters.exists(), \
+      f"no {str(filters)} corresponding to {str(vcxproj)}"
+
+  # namespace the MSBuild elements live in
+  ns = "http://schemas.microsoft.com/developer/msbuild/2003"
+
+  # parse XML out of the .vcxproj file
+  srcs1 = set()
+  tree = ET.parse(vcxproj)
+  root = tree.getroot()
+  for elem in root:
+    if elem.tag == f"{{{ns}}}ItemGroup":
+      for child in elem:
+        if child.tag in (f"{{{ns}}}ClInclude", f"{{{ns}}}ClCompile"):
+          filename = fix_sep(child.attrib["Include"])
+          assert filename not in srcs1, \
+            f"duplicate source {filename} in {str(vcxproj)}"
+          srcs1.add(filename)
+
+  # parse XML out of the .vcxproj.filters file
+  if filters.exists():
+    srcs2 = set()
+    tree = ET.parse(filters)
+    root = tree.getroot()
+    for elem in root:
+      if elem.tag == f"{{{ns}}}ItemGroup":
+        for child in elem:
+          if child.tag in (f"{{{ns}}}ClInclude", f"{{{ns}}}ClCompile"):
+            filename = fix_sep(child.attrib["Include"])
+            assert filename not in srcs2, \
+              f"duplicate source {filename} in {str(filters)}"
+            srcs2.add(filename)
+
+    assert srcs1 == srcs2, \
+      "mismatch between sources in {str(vcxproj)} and {str(filters)}"