+/* This is a very basic escape function that escapes < > and &
+ Ideally we'd use g_markup_escape_string but that function isn't
+ available in Mono's eglib. This was written without looking at the
+ source of that function in glib. */
+static char *
+escape_string_for_xml (const char *string)
+{
+ GString *string_builder = g_string_new (NULL);
+ const char *start, *p;
+
+ start = p = string;
+ while (*p) {
+ while (*p && *p != '&' && *p != '<' && *p != '>')
+ p++;
+
+ g_string_append_len (string_builder, start, p - start);
+
+ if (*p == '\0')
+ break;
+
+ switch (*p) {
+ case '<':
+ g_string_append (string_builder, "<");
+ break;
+
+ case '>':
+ g_string_append (string_builder, ">");
+ break;
+
+ case '&':
+ g_string_append (string_builder, "&");
+ break;
+
+ default:
+ break;
+ }
+
+ p++;
+ start = p;
+ }
+
+ return g_string_free (string_builder, FALSE);
+}
+
+static int
+sort_assemblies (gconstpointer a, gconstpointer b)
+{
+ CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
+ CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
+
+ if (assembly_a->name == NULL && assembly_b->name == NULL)
+ return 0;
+ else if (assembly_a->name == NULL)
+ return -1;
+ else if (assembly_b->name == NULL)
+ return 1;
+
+ return strcmp (assembly_a->name, assembly_b->name);
+}
+
+static void
+dump_coverage (void)
+{
+ if (!coverage_methods && !coverage_assemblies)
+ return;
+
+ gather_coverage_statements ();
+ fprintf (outfile, "\nCoverage Summary:\n");
+
+ if (coverage_outfile) {
+ fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
+ fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
+ }
+
+ g_ptr_array_sort (coverage_assemblies, sort_assemblies);
+
+ for (guint i = 0; i < coverage_assemblies->len; i++) {
+ CoverageAssembly *assembly = coverage_assemblies->pdata[i];
+ GPtrArray *classes;
+
+ if (assembly->number_of_methods != 0) {
+ int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
+ fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
+ } else
+ fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
+
+ if (coverage_outfile) {
+ char *escaped_name, *escaped_filename;
+ escaped_name = escape_string_for_xml (assembly->name);
+ escaped_filename = escape_string_for_xml (assembly->filename);
+
+ fprintf (coverage_outfile, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, assembly->guid, escaped_filename, assembly->number_of_methods, assembly->fully_covered, assembly->partially_covered);
+
+ g_free (escaped_name);
+ g_free (escaped_filename);
+ }
+
+ classes = g_hash_table_lookup (coverage_assembly_classes, assembly->name);
+ if (classes) {
+ for (guint j = 0; j < classes->len; j++) {
+ CoverageClass *klass = classes->pdata[j];
+
+ if (klass->number_of_methods > 0) {
+ int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
+ fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
+ } else
+ fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
+
+ if (coverage_outfile) {
+ char *escaped_name;
+ escaped_name = escape_string_for_xml (klass->class_name);
+
+ fprintf (coverage_outfile, "\t\t<class name=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, klass->number_of_methods, klass->fully_covered, klass->partially_covered);
+ g_free (escaped_name);
+ }
+ }
+ }
+ }
+
+ for (guint i = 0; i < coverage_methods->len; i++) {
+ CoverageMethod *method = coverage_methods->pdata[i];
+
+ if (coverage_outfile) {
+ char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
+
+ escaped_assembly = escape_string_for_xml (method->assembly_name);
+ escaped_class = escape_string_for_xml (method->class_name);
+ escaped_method = escape_string_for_xml (method->method_name);
+ escaped_sig = escape_string_for_xml (method->method_signature);
+ escaped_filename = escape_string_for_xml (method->filename);
+
+ fprintf (coverage_outfile, "\t<method assembly=\"%s\" class=\"%s\" name=\"%s (%s)\" filename=\"%s\" token=\"%d\">\n", escaped_assembly, escaped_class, escaped_method, escaped_sig, escaped_filename, method->token);
+
+ g_free (escaped_assembly);
+ g_free (escaped_class);
+ g_free (escaped_method);
+ g_free (escaped_sig);
+ g_free (escaped_filename);
+
+ for (guint j = 0; j < method->coverage->len; j++) {
+ CoverageCoverage *coverage = method->coverage->pdata[j];
+ fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
+ }
+ fprintf (coverage_outfile, "\t</method>\n");
+ }
+ }
+
+ if (coverage_outfile) {
+ fprintf (coverage_outfile, "</coverage>\n");
+ fclose (coverage_outfile);
+ coverage_outfile = NULL;
+ }
+}
+