| {% if table_entry_type == "Component" %} | |
| <p> | |
|  Filter: <input type="text" id="filter" size="30" /> (e.g. "crypto,VR") | |
| </p> | |
| {% endif %} | |
| <table> | |
| <thead> | |
| <tr> | |
| <th class="column-entry-bold">{{ table_entry_type }}</th> | |
| <th class="column-entry-bold" onclick="sortTable(SORT_COLUMN.LINE)" title= | |
| "Line coverage is the percentage of code lines which have been executed at least once. Only executable lines within function bodies are considered to be code lines."> | |
| Line Coverage | |
| </th> | |
| <th class="column-entry-bold" onclick="sortTable(SORT_COLUMN.FUNCTION)" title= | |
| "Function coverage is the percentage of functions which have been executed at least once. A function is considered to be executed if any of its instantiations are executed."> | |
| Function Coverage | |
| </th> | |
| <th class="column-entry-bold" onclick="sortTable(SORT_COLUMN.REGION)" title= | |
| "Region coverage is the percentage of code regions which have been executed at least once. A code region may span multiple lines (e.g in a large function body with no control flow). However, it's also possible for a single line to contain multiple code regions (e.g in 'return x || y && z')."> | |
| Region Coverage | |
| </th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for entry in entries %} | |
| <tr class="light-row"> | |
| <td> | |
| {% if entry["is_dir"] == True %} | |
| <pre><a href='{{ entry["href"] }}'>{{ entry["name"] }}/</a></pre> | |
| {% else %} | |
| <pre><a href='{{ entry["href"] }}'>{{ entry["name"] }}</a></pre> | |
| {% endif %} | |
| </td> | |
| {% for feature in ("lines", "functions", "regions") %} | |
| <td class='column-entry-{{ entry[feature]["color_class"] }}'> | |
| <pre>{{ entry[feature]["percentage"] }}% ({{ entry[feature]["covered"] }}/{{ entry[feature]["total"] }})</pre> | |
| </td> | |
| {% endfor %} | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| {% if total_entry %} | |
| <tfoot> | |
| <tr class="light-row-bold"> | |
| <td> | |
| <pre>Totals</pre> | |
| </td> | |
| {% for feature in ("lines", "functions", "regions") %} | |
| <td class='column-entry-{{ total_entry[feature]["color_class"] }}'> | |
| <pre>{{ total_entry[feature]["percentage"] }}% ({{ total_entry[feature]["covered"] }}/{{ total_entry[feature]["total"] }})</pre> | |
| </td> | |
| {% endfor %} | |
| </tr> | |
| </tfoot> | |
| {% endif %} | |
| </table> | |
| <script> | |
| const SORT_COLUMN = { | |
| LINE: 2, | |
| FUNCTION: 3, | |
| REGION: 4, | |
| } | |
| const SORT_TYPES = { | |
| UNSET: -1, | |
| PERCENT: 0, | |
| AGGREGATE: 1, | |
| } | |
| var SORT_ORDER = { | |
| [SORT_COLUMN.LINE]: [SORT_TYPES.UNSET], | |
| [SORT_COLUMN.FUNCTION]: [SORT_TYPES.UNSET], | |
| [SORT_COLUMN.REGION]: [SORT_TYPES.UNSET], | |
| } | |
| function sortTable(columnNumber) { | |
| SORT_ORDER[columnNumber] = ++SORT_ORDER[columnNumber] % 2; | |
| let columnSortOrder = SORT_ORDER[columnNumber]; | |
| let tbody = document.getElementsByTagName("tbody")[0]; | |
| [].slice.call(tbody.rows).sort(function(a, b) { | |
| let aColumn = a.cells[columnNumber-1].textContent; | |
| let bColumn = b.cells[columnNumber-1].textContent; | |
| let aColumnCompare, bColumnCompare; | |
| if (columnSortOrder == SORT_TYPES.PERCENT) { | |
| aColumnCompare = parseFloat(/([0-9.]+)%/.exec(aColumn)[1]); | |
| bColumnCompare = parseFloat(/([0-9.]+)%/.exec(bColumn)[1]); | |
| } else { | |
| aColumnCompare = parseInt(/\/(\d+)/.exec(aColumn)[1]); | |
| bColumnCompare = parseInt(/\/(\d+)/.exec(bColumn)[1]); | |
| } | |
| return ( | |
| aColumnCompare < bColumnCompare ? -1: | |
| aColumnCompare > bColumnCompare ? 1 : 0); | |
| }).forEach(function(value, index) { | |
| tbody.appendChild(value); | |
| }); | |
| } | |
| {% if table_entry_type == "Component" %} | |
| function filterInputChanged() { | |
| let filter = document.getElementById("filter"); | |
| let filterString = filter.value; | |
| // Update query paramater in URL. | |
| let queryParams = new URLSearchParams(window.location.search); | |
| queryParams.set('filter', filterString); | |
| let newPath = window.location.pathname; | |
| if (filterString) | |
| newPath += '?' + queryParams.toString(); | |
| history.pushState(null, '', newPath); | |
| filterRowsIfNeeded(); | |
| } | |
| function filterRowsIfNeeded() { | |
| let queryParams = new URLSearchParams(window.location.search); | |
| let filterString = queryParams.get('filter') || ""; | |
| let filterValuesLower = filterString.toLowerCase().split(','); | |
| let tbody = document.getElementsByTagName("tbody")[0]; | |
| let rows = tbody.getElementsByTagName("tr"); | |
| for (let row of rows) { | |
| let td = row.getElementsByTagName("td"); | |
| let tdContent = row.innerText.toLowerCase(); | |
| // |match| should be true on empty search (show everything). | |
| let match = !filterValuesLower; | |
| for (let filterValueLower of filterValuesLower) { | |
| if (tdContent.includes(filterValueLower)) { | |
| match = true; | |
| break | |
| } | |
| } | |
| if (match && row.style.display == 'none') | |
| row.style.display = 'table-row'; | |
| else if (!match) | |
| row.style.display = 'none'; | |
| } | |
| // Update filter value in input box (for cases when filter is provided | |
| // as part of page load URL). | |
| let filter = document.getElementById("filter"); | |
| if (filter.value != filterString) | |
| filter.value = filterString; | |
| } | |
| window.onload = filterRowsIfNeeded; | |
| var filter = document.getElementById("filter"); | |
| filter.onchange = filter.onkeyup = filterInputChanged; | |
| {% endif %} | |
| </script> |