From 4c8e430e92ab290aca6f2147785dcadb26e7c809 Mon Sep 17 00:00:00 2001
From: Michael Lee <michael.lee.0x2a@gmail.com>
Date: Mon, 29 Jul 2019 09:31:16 -0700
Subject: [PATCH] Fix XML attribute order in tests

Our tests recently started breaking -- it turns out this was because
LXML 4.4.0 was released this Saturday, and LXML apparently respects now
respects the insertion order of dicts/keyword arguments when setting
XML attributes.

See the changelog for more details: https://github.com/lxml/lxml/blob/master/CHANGES.txt

I didn't feel like tweaking a bunch of tests, so decided to fix this
in a relatively lazy way: I just went through every place we instantiated
an "Element" or "SubElement" object in report.py and manually sorted the
attributes.

The only exception was the precision info in stats.py -- I discovered to
my mild surprise that the order of those stats apparently has semantic
meaning (the higher the number, the less "precise" that line is?), so
decided it might be more useful to preserve that ordering.

And for good measure, I pinned the LXML test requirement to 4.4.0 so
we won't need to remember to keep attributes sorted or whatever when
adding new tests in the future.
---
 mypy/report.py                    | 22 +++++++++++-----------
 test-data/unit/check-reports.test |  2 +-
 test-requirements.txt             |  2 +-
 3 files changed, 13 insertions(+), 13 deletions(-)

--- mypy.orig/mypy/report.py
+++ mypy/mypy/report.py
@@ -445,10 +445,11 @@
                 status = visitor.line_map.get(lineno, stats.TYPE_EMPTY)
                 file_info.counts[status] += 1
                 etree.SubElement(root, 'line',
-                                 number=str(lineno),
-                                 precision=stats.precision_names[status],
+                                 any_info=self._get_any_info_for_line(visitor, lineno),
                                  content=line_text.rstrip('\n').translate(self.control_fixer),
-                                 any_info=self._get_any_info_for_line(visitor, lineno))
+                                 number=str(lineno),
+                                 precision=stats.precision_names[status]
+                                 )
         # Assumes a layout similar to what XmlReporter uses.
         xslt_path = os.path.relpath('mypy-html.xslt', path)
         transform_pi = etree.ProcessingInstruction('xml-stylesheet',
@@ -483,9 +484,9 @@
         for file_info in output_files:
             etree.SubElement(root, 'file',
                              file_info.attrib(),
-                             total=str(file_info.total()),
+                             module=file_info.module,
                              name=file_info.name,
-                             module=file_info.module)
+                             total=str(file_info.total()))
         xslt_path = os.path.relpath('mypy-html.xslt', '.')
         transform_pi = etree.ProcessingInstruction('xml-stylesheet',
                 'type="text/xsl" href="%s"' % pathname2url(xslt_path))
@@ -517,8 +518,8 @@
 
     def as_xml(self) -> Any:
         package_element = etree.Element('package',
-                                        name=self.name,
-                                        complexity='1.0')
+                                        complexity='1.0',
+                                        name=self.name)
         package_element.attrib['branch-rate'] = '0'
         package_element.attrib['line-rate'] = get_line_rate(self.covered_lines, self.total_lines)
         classes_element = etree.SubElement(package_element, 'classes')
@@ -559,8 +560,8 @@
         class_name = os.path.basename(path)
         file_info = FileInfo(path, tree._fullname)
         class_element = etree.Element('class',
-                                      filename=path,
                                       complexity='1.0',
+                                      filename=path,
                                       name=class_name)
         etree.SubElement(class_element, 'methods')
         lines_element = etree.SubElement(class_element, 'lines')
@@ -582,10 +583,10 @@
                     branch = True
                 file_info.counts[status] += 1
                 line_element = etree.SubElement(lines_element, 'line',
-                                                number=str(lineno),
-                                                precision=stats.precision_names[status],
+                                                branch=str(branch).lower(),
                                                 hits=str(hits),
-                                                branch=str(branch).lower())
+                                                number=str(lineno),
+                                                precision=stats.precision_names[status])
                 if branch:
                     line_element.attrib['condition-coverage'] = '50% (1/2)'
             class_element.attrib['branch-rate'] = '0'
