Class: Trainer::XCResult::Helper

Inherits:
Object
  • Object
show all
Defined in:
trainer/lib/trainer/xcresult/helper.rb

Overview

Helper class for XML and node operations

Class Method Summary collapse

Class Method Details

.create_xml_element(name, **attributes) ⇒ REXML::Element

Creates an XML element with the given name and attributes

Parameters:

  • name (String)

    The name of the XML element

  • attributes (Hash)

    A hash of attributes to add to the element

Returns:

  • (REXML::Element)

    The created XML element



13
14
15
16
17
18
19
20
21
22
# File 'trainer/lib/trainer/xcresult/helper.rb', line 13

def self.create_xml_element(name, **attributes)
  # Sanitize invalid XML characters (control chars except tab/CR/LF) to avoid errors when generating XML
  sanitizer = proc { |text| text.to_s.gsub(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/) { |c| format("\\u%04x", c.ord) } }
  element = REXML::Element.new(sanitizer.call(name))
  attributes.compact.each do |key, value|
    safe_value = sanitizer.call(value.to_s)
    element.attributes[key.to_s] = safe_value
  end
  element
end

.find_json_children(node, *node_types) ⇒ Array<Hash>

Find children of a node by specified node types

Parameters:

  • node (Hash, nil)

    The JSON node to search within

  • node_types (Array<String>)

    The node types to filter by

Returns:

  • (Array<Hash>)

    Array of child nodes matching the specified types



29
30
31
32
33
# File 'trainer/lib/trainer/xcresult/helper.rb', line 29

def self.find_json_children(node, *node_types)
  return [] if node.nil? || node['children'].nil?

  node['children'].select { |child| node_types.include?(child['nodeType']) }
end

.supports_xcresulttool_version_23?Boolean

Check if the current xcresulttool supports new commands introduced in Xcode 16+

Since Xcode 16b3, xcresulttool has marked get <object> --format json as deprecated/legacy, and replaced it with xcrun xcresulttool get test-results tests instead.

Returns:

  • (Boolean)

    Whether the xcresulttool supports Xcode 16+ commands



64
65
66
67
68
# File 'trainer/lib/trainer/xcresult/helper.rb', line 64

def self.supports_xcresulttool_version_23?
  version = xcresulttool_version

  !version.nil? && version >= Gem::Version.new(23_021)
end

.supports_xcresulttool_version_24?Boolean

Returns:



70
71
72
73
74
# File 'trainer/lib/trainer/xcresult/helper.rb', line 70

def self.supports_xcresulttool_version_24?
  version = xcresulttool_version

  !version.nil? && version >= Gem::Version.new(24_408)
end

.xcresulttool_versionGem::Version?

The version of the xcresulttool on the current PATH

Not memoized: xcrun resolves through DEVELOPER_DIR, which the xcode_select, xcversion, xcodes and xcode_install actions change during a run, so the answer can differ between two calls in the same process.

Returns:

  • (Gem::Version, nil)

    The version, or nil when there is no xcrun to ask



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'trainer/lib/trainer/xcresult/helper.rb', line 42

def self.xcresulttool_version
  # e.g. DEVELOPER_DIR=/Applications/Xcode_16_beta_3.app
  # xcresulttool version 23021, format version 3.53 (current)
  # xcresulttool version 24408, schema version: 0.1.0 (legacy commands format version: 3.56)
  output = `xcrun xcresulttool version`
  match = output.match(/xcresulttool version (?<version>[\d.]+)/)
  raise "Could not read the xcresulttool version from #{output.strip.inspect}" if match.nil?

  Gem::Version.new(match[:version])
rescue Errno::ENOENT
  # No xcrun at all: not macOS, or no developer tools installed. Every other
  # failure is left to surface, so a change in what xcresulttool reports is not
  # silently read as "this version is not supported".
  nil
end