makiri 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c178d50564ea8152e886817e6df19628c491eb7bb1213e06ab70f6ffe8665015
4
- data.tar.gz: 55a69b4d93c40b130581cb078ffee01dc6355cec4843fdc0f86269d7d22a4390
3
+ metadata.gz: 8998ec8f7ae9b6e9f31fb3b880f50a53602e2cdea51b42faa33613638e2ceedc
4
+ data.tar.gz: df9c18e5917eb79e2b0311a65c70e1b648e40c62fd1b14ecec1d64b4394f3930
5
5
  SHA512:
6
- metadata.gz: c87c6c98a58fe500d60f7775c088c655d3938e22cf614ed57b84d395d15229bd4dfbf5e61909c86804132d90dfdb31d62278e36ed7399c66102590c13867ca36
7
- data.tar.gz: 2da0687466e5c2faab9d0c95d8181e529acf68b30165c9357375edb10e5dff5cd45557e7186c133fdfaf40043f7d2c2f3ee1a49d945cacea4f46c1b56b195c2c
6
+ metadata.gz: 5bd1a20a81195609e360ffc71108cef27a62803a387526dfdb38cf7cbf0ae6bde69e48815717ba1d236300c2aedd376589b4799c610a883ebf088d8a37e0f3ce
7
+ data.tar.gz: 1104a702f5348a2e52705c81153af1baaad12bcc3a649e5e863d5e8a6c98da3566ede4efa8cb0f0b8d091b0ba3ee9abef0c67dcf83ddae33d48706999df9cd74
@@ -24,11 +24,16 @@ jobs:
24
24
  # (intra-arena overflows). Runs on Linux because Valgrind is x86_64/amd64
25
25
  # Linux only.
26
26
  valgrind-memcheck:
27
- name: Valgrind memcheck (Ruby ${{ matrix.ruby }})
27
+ name: Valgrind memcheck (Ruby ${{ matrix.ruby }}, shard ${{ matrix.shard }}/4)
28
28
  runs-on: ubuntu-latest
29
29
  timeout-minutes: 360
30
30
  env:
31
31
  BUNDLE_WITH: valgrind
32
+ # Shard the suite across the matrix below: 4 parallel jobs each memcheck a
33
+ # quarter of the spec files, so the whole suite finishes in ~1/4 the
34
+ # wall-clock with no loss of coverage (each shard is balanced by file size).
35
+ VALGRIND_SHARDS: "4"
36
+ VALGRIND_SHARD_INDEX: ${{ matrix.shard }}
32
37
  # These heavy jobs verify memory discipline (uninit values, intra-arena
33
38
  # overflows, use-after-move), not the property space - so the full
34
39
  # 300-iteration PBT sweep (already run by the normal CI matrix) is
@@ -37,10 +42,16 @@ jobs:
37
42
  # keeping the run tractable.
38
43
  PBT_COUNT: "15"
39
44
  CSS_PBT_COUNT: "15"
45
+ # --track-origins roughly doubles memcheck's runtime and only adds an origin
46
+ # backtrace to a finding (the finding itself is still reported without it).
47
+ # Drop it on the frequent post-merge push gate to halve its runtime; keep it
48
+ # for the thorough nightly / manual runs, where the backtrace aids triage.
49
+ VALGRIND_TRACK_ORIGINS: ${{ github.event_name == 'push' && 'no' || 'yes' }}
40
50
  strategy:
41
51
  fail-fast: false
42
52
  matrix:
43
53
  ruby: ["3.4"]
54
+ shard: [0, 1, 2, 3] # keep in sync with VALGRIND_SHARDS above
44
55
 
45
56
  steps:
46
57
  - name: Checkout (with vendored Lexbor submodule)
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.0] - 2026-07-12
4
+
5
+ ### Fixed
6
+
7
+ * `Document#import_node` (DOM `importNode` / `adoptNode`) now imports an HTML
8
+ element whose name is a valid DOM name but not a well-formed XML QName (e.g.
9
+ `":good:times:"`, `"x<"`, `"0:a"`) instead of raising. Such an element is not
10
+ XML-serializable (`#to_xml` raises). An element carrying a similarly lenient
11
+ attribute name is still rejected.
12
+
3
13
  ## [0.7.0] - 2026-07-11
4
14
 
5
15
  ### Added
data/Rakefile CHANGED
@@ -88,7 +88,12 @@ begin
88
88
  "--error-limit=no",
89
89
  "--trace-children=yes", # spec processes may fork
90
90
  "--undef-value-errors=yes", # the point of this job (ruby_memcheck defaults to =no)
91
- "--track-origins=yes", # report where an uninitialised value came from
91
+ # Origin tracking (where an uninitialised value came from) roughly DOUBLES
92
+ # memcheck's run time. It is a diagnostic aid, not a detection capability -
93
+ # memcheck still reports the uninitialised USE without it - so the frequent
94
+ # post-merge push gate turns it off (VALGRIND_TRACK_ORIGINS=no) to run in
95
+ # ~half the time, while the nightly / manual runs keep it on for the backtrace.
96
+ "--track-origins=#{ENV.fetch('VALGRIND_TRACK_ORIGINS', 'yes')}",
92
97
  "--leak-check=no", # leaks are `rake leaks`' job, not this one
93
98
  ],
94
99
  )
@@ -96,7 +101,34 @@ begin
96
101
  namespace :spec do
97
102
  desc "Run the spec suite under Valgrind memcheck (ruby_memcheck; needs the " \
98
103
  ":valgrind bundler group and the valgrind binary)"
99
- RubyMemcheck::RSpec::RakeTask.new(valgrind: :compile)
104
+ RubyMemcheck::RSpec::RakeTask.new(valgrind: :compile) do |t|
105
+ # Let spec_helper skip :slow examples (fail-closed limit tests whose sheer
106
+ # volume dominates memcheck without adding memory-safety coverage).
107
+ ENV["VALGRIND"] = "1"
108
+
109
+ # Optional sharding for CI wall-clock: with VALGRIND_SHARDS=N (>1) the spec
110
+ # files are partitioned into N groups and this run does only group
111
+ # VALGRIND_SHARD_INDEX, so N parallel matrix jobs cover the whole suite in
112
+ # ~1/N the time WITHOUT dropping any coverage. Files are packed
113
+ # largest-first onto the currently-lightest shard (greedy LPT by file size,
114
+ # a proxy for runtime), so the slowest shard - which sets wall-clock - is
115
+ # balanced rather than left to name order. Deterministic, so every shard
116
+ # computes the same partition and takes its own index. Default N=1 = the
117
+ # whole suite (local `rake spec:valgrind`).
118
+ shards = Integer(ENV.fetch("VALGRIND_SHARDS", "1"))
119
+ if shards > 1
120
+ index = Integer(ENV.fetch("VALGRIND_SHARD_INDEX", "0"))
121
+ buckets = Array.new(shards) { { load: 0, files: [] } }
122
+ # sort by (size desc, name) so the packing is fully deterministic across
123
+ # shards regardless of Array#sort stability.
124
+ Dir["spec/**/*_spec.rb"].sort_by { |f| [-File.size(f), f] }.each do |f|
125
+ b = buckets.min_by { |x| x[:load] }
126
+ b[:files] << f
127
+ b[:load] += File.size(f)
128
+ end
129
+ t.pattern = buckets.fetch(index)[:files]
130
+ end
131
+ end
100
132
  end
101
133
  rescue LoadError
102
134
  # ruby_memcheck not installed (optional :valgrind group absent) - skip the task.
@@ -185,15 +185,37 @@ h2x_make(mkr_xml_doc_t *xdoc, lxb_dom_node_t *s, const char *pdef, uint32_t pdef
185
185
  size_t nl;
186
186
  const lxb_char_t *nm = lxb_dom_element_qualified_name(lxb_dom_interface_element(s), &nl);
187
187
  if (!MKR_FITS_U32(nl)) return MKR_XML_MUT_OOM;
188
+
189
+ /* The element's namespace URI (borrowed from the source doc's ns table). */
190
+ uint32_t eul;
191
+ const char *euri = mkr_html_ns_uri(s, &eul);
192
+
193
+ /* Strict first, so a valid QName stays XML-serializable. importNode/adoptNode
194
+ * never re-validate an existing node's name (DOM requires the source already
195
+ * be well-formed for its representation), so an HTML name that is a valid DOM
196
+ * element name but NOT a well-formed XML QName (":good:times:", "x<", "0:a")
197
+ * is taken VERBATIM as an unprefixed DOM-loose name (local == whole) rather
198
+ * than rejected - the same non-serializable escape hatch as
199
+ * create_loose_dom_element. HTML elements are always unprefixed. The namespace
200
+ * is passed DIRECTLY because ns resolution at link time skips loose names
201
+ * (resolve_node_ns), so a synthesized xmlns declaration would not reach it. */
188
202
  mkr_xml_node_t *el = NULL;
189
203
  mkr_xml_mut_status_t st = mkr_xml_new_element(xdoc, (const char *)nm, (uint32_t)nl, &el);
204
+ if (st == MKR_XML_MUT_BAD_NAME && nl > 0) {
205
+ mkr_xml_qname_t qn = {
206
+ (const char *)nm, (uint32_t)nl, /* qname */
207
+ (const char *)nm, 0, /* prefix (none) */
208
+ (const char *)nm, (uint32_t)nl, /* local == qname */
209
+ };
210
+ st = mkr_xml_new_loose_dom_element(xdoc, &qn, euri, eul, &el);
211
+ }
190
212
  if (st != MKR_XML_MUT_OK) return st;
191
213
 
192
- /* Declare the element's default namespace iff it differs from the inherited
193
- * one, so this (unprefixed, like all HTML elements) element resolves to it.
194
- * An element with no namespace under an inherited default undeclares (xmlns=""). */
195
- uint32_t eul;
196
- const char *euri = mkr_html_ns_uri(s, &eul);
214
+ /* Declare the default namespace iff it differs from the inherited one, so
215
+ * this (unprefixed, like all HTML elements) element and its children resolve
216
+ * to it. An element with no namespace under an inherited default undeclares
217
+ * (xmlns=""). For a loose element this only feeds child inheritance - its own
218
+ * ns_uri was already set directly above. */
197
219
  if (!mkr_uri_eq(euri, eul, pdef, pdef_len)) {
198
220
  st = h2x_declare_ns(xdoc, el, NULL, 0, euri, eul);
199
221
  if (st != MKR_XML_MUT_OK) return st;
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Makiri
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: makiri
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - takahashim