moxml 0.5.175 → 0.5.177

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: 14463613d8081db0efee985379ddcae7740f8044d9f0f7a8192f5f1828337a27
4
- data.tar.gz: 7fcb65b5d65fedc5d9766d31f409441eafba5f78b8212478a854bcd17ffdf167
3
+ metadata.gz: 021a0d5d25cfe9e12a0a29968649a0801d0379e30b4f6aefe349d86a8959149b
4
+ data.tar.gz: 197a8e2c303f7fa1e29e7f25b8d64502d6c3d3179ca342af4b02dda168e7fa89
5
5
  SHA512:
6
- metadata.gz: b7c402c813be0883260893a9e47290653e61104cf870702d7d46b155299d87752f8ec5662455ebb0b75c4b776d8962502d7ed223a3b5fca0764fb2600ae67488
7
- data.tar.gz: cc0a50d550e12d1496c0f31a623c9e1c3a7569fe1d65b7796b97a1a30d63d994812d02c28cd6cfcfd22352a11d0fc9ed25d4a2488bda768ac0898342dad7837a
6
+ metadata.gz: cddcf3bb2b304e5d78779cba12b3f57514b783336642a8e40ca7d8894ba47485ef6ffff114b9f8a1430e6b9669067c86f4de2bbd53aac6d18ed8ffef52828d5f
7
+ data.tar.gz: 6d88eb872c4a876d9f387af3e006eb8247935ff58f70c6d3249d6b3afba7809e3485c117fbeaa3dc61103ab9858997b98c230efc870e0dac0a914971bb20e49e
data/Rakefile CHANGED
@@ -355,13 +355,44 @@ namespace :conformance do
355
355
  task :export do
356
356
  require "json"
357
357
  require_relative "lib/moxml"
358
- dataset = Moxml::XSD::BuiltinLexical::CONFORMANCE_CASES
358
+ lexical = Moxml::XSD::BuiltinLexical::CONFORMANCE_CASES
359
+ regex = Moxml::XSD::SchemaRules::XsdRegex::CONFORMANCE_CASES
360
+ wildcards = Moxml::XSD::SchemaRules::Wildcard::WILDCARD_CASES
361
+ entities = Moxml::EntityRegistry::ENTITY_CONFORMANCE_CASES
359
362
  payload = {
360
- schema: 1,
363
+ schema: 3,
361
364
  generator: "moxml #{Moxml::VERSION}",
362
365
  cases: {
363
- "builtin_lexical" => dataset.map do |type, lexical, expected|
364
- { "type" => type, "lexical" => lexical, "valid" => expected }
366
+ "builtin_lexical" => lexical.map do |type, lexical_value, expected|
367
+ { "type" => type, "lexical" => lexical_value, "valid" => expected }
368
+ end,
369
+ "xsd_regex_translation" => regex.map do |pattern, value, matches, translatable|
370
+ {
371
+ "pattern" => pattern, "value" => value,
372
+ "matches" => matches, "translatable" => translatable
373
+ }
374
+ end,
375
+ "schema_wildcards" => wildcards.map do |row|
376
+ constraint, process, not_namespace, not_qname, item_ns, item_qname,
377
+ defined, sibling_names, covers, exclusion = row
378
+ {
379
+ "constraint" => constraint, "process" => process,
380
+ "not_namespace" => not_namespace, "not_qname" => not_qname,
381
+ "item_namespace" => item_ns, "item_qname" => item_qname,
382
+ "defined" => defined, "sibling_names" => sibling_names,
383
+ "covers" => covers, "exclusion" => exclusion
384
+ }
385
+ end,
386
+ "entity_registry" => entities.map do |row|
387
+ seed, qname, qcp, restore_entities, mode, declared, codepoint,
388
+ names, primary, standard, restore = row
389
+ {
390
+ "seed" => seed, "qname" => qname, "qcp" => qcp,
391
+ "restore_entities" => restore_entities, "mode" => mode,
392
+ "declared" => declared, "codepoint" => codepoint,
393
+ "names" => names, "primary" => primary,
394
+ "standard" => standard, "restore" => restore
395
+ }
365
396
  end,
366
397
  },
367
398
  }
@@ -369,7 +400,8 @@ namespace :conformance do
369
400
  FileUtils.mkdir_p(dir)
370
401
  target = File.join(dir, "moxml-conformance.json")
371
402
  File.write(target, "#{JSON.pretty_generate(payload)}\n")
372
- puts "conformance: exported #{dataset.size} cases to #{target}"
403
+ total = lexical.size + regex.size + wildcards.size + entities.size
404
+ puts "conformance: exported #{total} cases to #{target}"
373
405
  end
374
406
  end
375
407
 
@@ -1,6 +1,6 @@
1
1
  {
2
- "schema": 1,
3
- "generator": "moxml 0.5.171",
2
+ "schema": 3,
3
+ "generator": "moxml 0.5.175",
4
4
  "cases": {
5
5
  "builtin_lexical": [
6
6
  {
@@ -408,6 +408,631 @@
408
408
  "lexical": "a:b",
409
409
  "valid": false
410
410
  }
411
+ ],
412
+ "xsd_regex_translation": [
413
+ {
414
+ "pattern": "\\p{IsBasicLatin}+",
415
+ "value": "abc",
416
+ "matches": true,
417
+ "translatable": true
418
+ },
419
+ {
420
+ "pattern": "\\p{IsBasicLatin}+",
421
+ "value": "€",
422
+ "matches": false,
423
+ "translatable": true
424
+ },
425
+ {
426
+ "pattern": "\\P{IsLatin-1Supplement}",
427
+ "value": "a",
428
+ "matches": true,
429
+ "translatable": true
430
+ },
431
+ {
432
+ "pattern": "\\P{IsLatin-1Supplement}",
433
+ "value": "é",
434
+ "matches": false,
435
+ "translatable": true
436
+ },
437
+ {
438
+ "pattern": "\\p{IsGreek}",
439
+ "value": "α",
440
+ "matches": true,
441
+ "translatable": true
442
+ },
443
+ {
444
+ "pattern": "\\p{IsGreek}",
445
+ "value": "a",
446
+ "matches": false,
447
+ "translatable": true
448
+ },
449
+ {
450
+ "pattern": "\\p{L}",
451
+ "value": "a",
452
+ "matches": true,
453
+ "translatable": true
454
+ },
455
+ {
456
+ "pattern": "\\p{L}",
457
+ "value": "1",
458
+ "matches": false,
459
+ "translatable": true
460
+ },
461
+ {
462
+ "pattern": "\\p{Nd}{1,3}",
463
+ "value": "5",
464
+ "matches": true,
465
+ "translatable": true
466
+ },
467
+ {
468
+ "pattern": "\\p{Nd}{1,3}",
469
+ "value": "abc",
470
+ "matches": false,
471
+ "translatable": true
472
+ },
473
+ {
474
+ "pattern": "\\d{1,3}",
475
+ "value": "5",
476
+ "matches": false,
477
+ "translatable": false
478
+ },
479
+ {
480
+ "pattern": "\\d{1,3}",
481
+ "value": "abc",
482
+ "matches": false,
483
+ "translatable": false
484
+ },
485
+ {
486
+ "pattern": "[0-9]{1,3}",
487
+ "value": "42",
488
+ "matches": true,
489
+ "translatable": true
490
+ },
491
+ {
492
+ "pattern": "[a-z]*",
493
+ "value": "",
494
+ "matches": true,
495
+ "translatable": true
496
+ },
497
+ {
498
+ "pattern": "a|b",
499
+ "value": "b",
500
+ "matches": true,
501
+ "translatable": true
502
+ },
503
+ {
504
+ "pattern": "a|b",
505
+ "value": "c",
506
+ "matches": false,
507
+ "translatable": true
508
+ },
509
+ {
510
+ "pattern": "a.c",
511
+ "value": "abc",
512
+ "matches": true,
513
+ "translatable": true
514
+ },
515
+ {
516
+ "pattern": "a\\.c",
517
+ "value": "a.c",
518
+ "matches": true,
519
+ "translatable": true
520
+ },
521
+ {
522
+ "pattern": "\\i\\c*",
523
+ "value": "x",
524
+ "matches": false,
525
+ "translatable": false
526
+ },
527
+ {
528
+ "pattern": "[\\p{IsBasicLatin}-[abc]]+",
529
+ "value": "a",
530
+ "matches": false,
531
+ "translatable": false
532
+ },
533
+ {
534
+ "pattern": "\\p{IsNoSuchBlock}",
535
+ "value": "a",
536
+ "matches": false,
537
+ "translatable": false
538
+ }
539
+ ],
540
+ "schema_wildcards": [
541
+ {
542
+ "constraint": "##any",
543
+ "process": "strict",
544
+ "not_namespace": null,
545
+ "not_qname": [],
546
+ "item_namespace": "local",
547
+ "item_qname": "{}x",
548
+ "defined": false,
549
+ "sibling_names": [],
550
+ "covers": true,
551
+ "exclusion": null
552
+ },
553
+ {
554
+ "constraint": "##any",
555
+ "process": "strict",
556
+ "not_namespace": null,
557
+ "not_qname": [],
558
+ "item_namespace": "tns",
559
+ "item_qname": "{}x",
560
+ "defined": false,
561
+ "sibling_names": [],
562
+ "covers": true,
563
+ "exclusion": null
564
+ },
565
+ {
566
+ "constraint": "##other",
567
+ "process": "strict",
568
+ "not_namespace": null,
569
+ "not_qname": [],
570
+ "item_namespace": "local",
571
+ "item_qname": "{}x",
572
+ "defined": false,
573
+ "sibling_names": [],
574
+ "covers": false,
575
+ "exclusion": null
576
+ },
577
+ {
578
+ "constraint": "##other",
579
+ "process": "strict",
580
+ "not_namespace": null,
581
+ "not_qname": [],
582
+ "item_namespace": "tns",
583
+ "item_qname": "{}x",
584
+ "defined": false,
585
+ "sibling_names": [],
586
+ "covers": false,
587
+ "exclusion": null
588
+ },
589
+ {
590
+ "constraint": "##other",
591
+ "process": "strict",
592
+ "not_namespace": null,
593
+ "not_qname": [],
594
+ "item_namespace": "urn:other",
595
+ "item_qname": "{}x",
596
+ "defined": false,
597
+ "sibling_names": [],
598
+ "covers": true,
599
+ "exclusion": null
600
+ },
601
+ {
602
+ "constraint": "##local",
603
+ "process": "strict",
604
+ "not_namespace": null,
605
+ "not_qname": [],
606
+ "item_namespace": "local",
607
+ "item_qname": "{}x",
608
+ "defined": false,
609
+ "sibling_names": [],
610
+ "covers": true,
611
+ "exclusion": null
612
+ },
613
+ {
614
+ "constraint": "##local",
615
+ "process": "strict",
616
+ "not_namespace": null,
617
+ "not_qname": [],
618
+ "item_namespace": "urn:b",
619
+ "item_qname": "{}x",
620
+ "defined": false,
621
+ "sibling_names": [],
622
+ "covers": false,
623
+ "exclusion": null
624
+ },
625
+ {
626
+ "constraint": "##targetNamespace urn:b",
627
+ "process": "strict",
628
+ "not_namespace": null,
629
+ "not_qname": [],
630
+ "item_namespace": "tns",
631
+ "item_qname": "{}x",
632
+ "defined": false,
633
+ "sibling_names": [],
634
+ "covers": true,
635
+ "exclusion": null
636
+ },
637
+ {
638
+ "constraint": "##targetNamespace urn:b",
639
+ "process": "strict",
640
+ "not_namespace": null,
641
+ "not_qname": [],
642
+ "item_namespace": "local",
643
+ "item_qname": "{}x",
644
+ "defined": false,
645
+ "sibling_names": [],
646
+ "covers": false,
647
+ "exclusion": null
648
+ },
649
+ {
650
+ "constraint": "urn:a urn:b",
651
+ "process": "strict",
652
+ "not_namespace": null,
653
+ "not_qname": [],
654
+ "item_namespace": "urn:a",
655
+ "item_qname": "{}x",
656
+ "defined": false,
657
+ "sibling_names": [],
658
+ "covers": true,
659
+ "exclusion": null
660
+ },
661
+ {
662
+ "constraint": "urn:a urn:b",
663
+ "process": "strict",
664
+ "not_namespace": null,
665
+ "not_qname": [],
666
+ "item_namespace": "urn:c",
667
+ "item_qname": "{}x",
668
+ "defined": false,
669
+ "sibling_names": [],
670
+ "covers": false,
671
+ "exclusion": null
672
+ },
673
+ {
674
+ "constraint": "urn:a",
675
+ "process": "lax",
676
+ "not_namespace": null,
677
+ "not_qname": [],
678
+ "item_namespace": "urn:a",
679
+ "item_qname": "{}x",
680
+ "defined": false,
681
+ "sibling_names": [],
682
+ "covers": true,
683
+ "exclusion": null
684
+ },
685
+ {
686
+ "constraint": "urn:a",
687
+ "process": "skip",
688
+ "not_namespace": null,
689
+ "not_qname": [],
690
+ "item_namespace": "urn:a",
691
+ "item_qname": "{}x",
692
+ "defined": false,
693
+ "sibling_names": [],
694
+ "covers": true,
695
+ "exclusion": null
696
+ },
697
+ {
698
+ "constraint": "##any",
699
+ "process": "strict",
700
+ "not_namespace": null,
701
+ "not_qname": [
702
+ "x"
703
+ ],
704
+ "item_namespace": "local",
705
+ "item_qname": "{}x",
706
+ "defined": false,
707
+ "sibling_names": [],
708
+ "covers": true,
709
+ "exclusion": "not_qname"
710
+ },
711
+ {
712
+ "constraint": "##any",
713
+ "process": "strict",
714
+ "not_namespace": null,
715
+ "not_qname": [
716
+ "x"
717
+ ],
718
+ "item_namespace": "local",
719
+ "item_qname": "{}y",
720
+ "defined": false,
721
+ "sibling_names": [],
722
+ "covers": true,
723
+ "exclusion": null
724
+ },
725
+ {
726
+ "constraint": "##any",
727
+ "process": "lax",
728
+ "not_namespace": null,
729
+ "not_qname": [
730
+ "x"
731
+ ],
732
+ "item_namespace": "local",
733
+ "item_qname": "{}x",
734
+ "defined": true,
735
+ "sibling_names": [],
736
+ "covers": true,
737
+ "exclusion": "not_qname"
738
+ },
739
+ {
740
+ "constraint": "##any",
741
+ "process": "skip",
742
+ "not_namespace": null,
743
+ "not_qname": [
744
+ "x"
745
+ ],
746
+ "item_namespace": "local",
747
+ "item_qname": "{}x",
748
+ "defined": true,
749
+ "sibling_names": [],
750
+ "covers": true,
751
+ "exclusion": "not_qname"
752
+ },
753
+ {
754
+ "constraint": "##any",
755
+ "process": "strict",
756
+ "not_namespace": null,
757
+ "not_qname": [
758
+ "##defined"
759
+ ],
760
+ "item_namespace": "local",
761
+ "item_qname": "{}x",
762
+ "defined": false,
763
+ "sibling_names": [],
764
+ "covers": true,
765
+ "exclusion": null
766
+ },
767
+ {
768
+ "constraint": "##any",
769
+ "process": "strict",
770
+ "not_namespace": null,
771
+ "not_qname": [
772
+ "##defined"
773
+ ],
774
+ "item_namespace": "local",
775
+ "item_qname": "{}x",
776
+ "defined": true,
777
+ "sibling_names": [],
778
+ "covers": true,
779
+ "exclusion": "not_qname"
780
+ },
781
+ {
782
+ "constraint": "##any",
783
+ "process": "strict",
784
+ "not_namespace": null,
785
+ "not_qname": [
786
+ "##definedSibling"
787
+ ],
788
+ "item_namespace": "local",
789
+ "item_qname": "{}x",
790
+ "defined": false,
791
+ "sibling_names": [
792
+ "{}y"
793
+ ],
794
+ "covers": true,
795
+ "exclusion": null
796
+ },
797
+ {
798
+ "constraint": "##any",
799
+ "process": "strict",
800
+ "not_namespace": null,
801
+ "not_qname": [
802
+ "##definedSibling"
803
+ ],
804
+ "item_namespace": "local",
805
+ "item_qname": "{}x",
806
+ "defined": false,
807
+ "sibling_names": [
808
+ "{}x"
809
+ ],
810
+ "covers": true,
811
+ "exclusion": "not_qname"
812
+ },
813
+ {
814
+ "constraint": "urn:a",
815
+ "process": "strict",
816
+ "not_namespace": "urn:z",
817
+ "not_qname": [],
818
+ "item_namespace": "urn:a",
819
+ "item_qname": "{}x",
820
+ "defined": false,
821
+ "sibling_names": [],
822
+ "covers": true,
823
+ "exclusion": null
824
+ },
825
+ {
826
+ "constraint": "urn:a",
827
+ "process": "strict",
828
+ "not_namespace": "urn:z",
829
+ "not_qname": [],
830
+ "item_namespace": "urn:z",
831
+ "item_qname": "{}x",
832
+ "defined": false,
833
+ "sibling_names": [],
834
+ "covers": false,
835
+ "exclusion": "not_namespace"
836
+ },
837
+ {
838
+ "constraint": "urn:a",
839
+ "process": "lax",
840
+ "not_namespace": "urn:z",
841
+ "not_qname": [],
842
+ "item_namespace": "urn:z",
843
+ "item_qname": "{}x",
844
+ "defined": false,
845
+ "sibling_names": [],
846
+ "covers": false,
847
+ "exclusion": "not_namespace"
848
+ }
849
+ ],
850
+ "entity_registry": [
851
+ {
852
+ "seed": {
853
+ "amp": "&"
854
+ },
855
+ "qname": "amp",
856
+ "qcp": 38,
857
+ "restore_entities": false,
858
+ "mode": "lenient",
859
+ "declared": true,
860
+ "codepoint": 38,
861
+ "names": [
862
+ "amp"
863
+ ],
864
+ "primary": "amp",
865
+ "standard": true,
866
+ "restore": true
867
+ },
868
+ {
869
+ "seed": {
870
+ "NonBreakingSpace": " ",
871
+ "nbsp": " "
872
+ },
873
+ "qname": "nbsp",
874
+ "qcp": 160,
875
+ "restore_entities": true,
876
+ "mode": "lenient",
877
+ "declared": true,
878
+ "codepoint": 160,
879
+ "names": [
880
+ "NonBreakingSpace",
881
+ "nbsp"
882
+ ],
883
+ "primary": "nbsp",
884
+ "standard": false,
885
+ "restore": true
886
+ },
887
+ {
888
+ "seed": {
889
+ "nbsp": " "
890
+ },
891
+ "qname": "nbsp",
892
+ "qcp": 160,
893
+ "restore_entities": false,
894
+ "mode": "lenient",
895
+ "declared": true,
896
+ "codepoint": 160,
897
+ "names": [
898
+ "nbsp"
899
+ ],
900
+ "primary": "nbsp",
901
+ "standard": false,
902
+ "restore": false
903
+ },
904
+ {
905
+ "seed": {
906
+ "nbsp": " "
907
+ },
908
+ "qname": "nbsp",
909
+ "qcp": 160,
910
+ "restore_entities": true,
911
+ "mode": "strict",
912
+ "declared": true,
913
+ "codepoint": 160,
914
+ "names": [
915
+ "nbsp"
916
+ ],
917
+ "primary": "nbsp",
918
+ "standard": false,
919
+ "restore": true
920
+ },
921
+ {
922
+ "seed": {
923
+ "AElig": "Æ"
924
+ },
925
+ "qname": "AElig",
926
+ "qcp": 198,
927
+ "restore_entities": true,
928
+ "mode": "lenient",
929
+ "declared": true,
930
+ "codepoint": 198,
931
+ "names": [
932
+ "AElig"
933
+ ],
934
+ "primary": "AElig",
935
+ "standard": false,
936
+ "restore": true
937
+ },
938
+ {
939
+ "seed": {
940
+ "fjlig": "fj"
941
+ },
942
+ "qname": "fjlig",
943
+ "qcp": 102,
944
+ "restore_entities": true,
945
+ "mode": "lenient",
946
+ "declared": true,
947
+ "codepoint": 102,
948
+ "names": [
949
+ "fjlig"
950
+ ],
951
+ "primary": "fjlig",
952
+ "standard": false,
953
+ "restore": true
954
+ },
955
+ {
956
+ "seed": {
957
+ "DotDot": " ⃜"
958
+ },
959
+ "qname": "DotDot",
960
+ "qcp": 32,
961
+ "restore_entities": true,
962
+ "mode": "lenient",
963
+ "declared": true,
964
+ "codepoint": 32,
965
+ "names": [
966
+ "DotDot"
967
+ ],
968
+ "primary": "DotDot",
969
+ "standard": false,
970
+ "restore": true
971
+ },
972
+ {
973
+ "seed": {
974
+ "NotEqualTilde": "≂̸"
975
+ },
976
+ "qname": "NotEqualTilde",
977
+ "qcp": 8770,
978
+ "restore_entities": true,
979
+ "mode": "lenient",
980
+ "declared": true,
981
+ "codepoint": 8770,
982
+ "names": [
983
+ "NotEqualTilde"
984
+ ],
985
+ "primary": "NotEqualTilde",
986
+ "standard": false,
987
+ "restore": true
988
+ },
989
+ {
990
+ "seed": {
991
+ "esc": "\\u00A0"
992
+ },
993
+ "qname": "esc",
994
+ "qcp": 160,
995
+ "restore_entities": true,
996
+ "mode": "lenient",
997
+ "declared": true,
998
+ "codepoint": 160,
999
+ "names": [
1000
+ "esc"
1001
+ ],
1002
+ "primary": "esc",
1003
+ "standard": false,
1004
+ "restore": true
1005
+ },
1006
+ {
1007
+ "seed": {
1008
+ "bad": ""
1009
+ },
1010
+ "qname": "bad",
1011
+ "qcp": 0,
1012
+ "restore_entities": true,
1013
+ "mode": "lenient",
1014
+ "declared": false,
1015
+ "codepoint": null,
1016
+ "names": [],
1017
+ "primary": null,
1018
+ "standard": false,
1019
+ "restore": false
1020
+ },
1021
+ {
1022
+ "seed": {
1023
+ "nbsp": " "
1024
+ },
1025
+ "qname": "nope",
1026
+ "qcp": 9999,
1027
+ "restore_entities": true,
1028
+ "mode": "lenient",
1029
+ "declared": false,
1030
+ "codepoint": null,
1031
+ "names": [],
1032
+ "primary": null,
1033
+ "standard": false,
1034
+ "restore": false
1035
+ }
411
1036
  ]
412
1037
  }
413
1038
  }
data/lib/moxml/context.rb CHANGED
@@ -214,9 +214,10 @@ module Moxml
214
214
  # Compile an XSD schema from a file path — <xs:include href>
215
215
  # resolves relative to the file's directory (the engine
216
216
  # resolves it; reading the file into #xsd would lose that).
217
- # The #452 rule index reads this document only; included
218
- # schema parts stay engine-validated (the index can only
219
- # under-report).
217
+ # The #452 rule index reads this document only; a document
218
+ # that composes others (include/import/redefine/override)
219
+ # turns the strict re-checks off (#510) — included parts stay
220
+ # engine-validated and the index can only under-report.
220
221
  def xsd_file(path)
221
222
  handle = config.adapter.xsd_compile_file(path.to_s)
222
223
  source = File.read(path.to_s)
@@ -229,7 +230,10 @@ module Moxml
229
230
  # types) are visible to the head's content models — the
230
231
  # single-document faces cannot see them (#483 item 4). The
231
232
  # Schema's #version / SchemaRules index read the HEAD document
232
- # (the first path); engine-set semantics cover the rest.
233
+ # (the first path); engine-set semantics cover the rest. A set
234
+ # is composition-shaped by contract, so the strict re-checks
235
+ # stay off (#510): the index under-reports instead of
236
+ # false-reporting components that live in a member.
233
237
  # Adapters without a set-capable engine raise
234
238
  # Moxml::NotImplementedError.
235
239
  def xsd_set(*paths)
@@ -238,7 +242,7 @@ module Moxml
238
242
 
239
243
  handle = config.adapter.xsd_compile_set(paths.map(&:to_s))
240
244
  source = File.read(paths.first.to_s)
241
- Moxml::XSD::Schema.new(handle, self, source)
245
+ Moxml::XSD::Schema.new(handle, self, source, self_contained: false)
242
246
  end
243
247
 
244
248
  # Compile a RELAX NG schema from source or from a file path (the
@@ -30,6 +30,58 @@ module Moxml
30
30
  # Standard XML predefined entities (XML spec §4.6)
31
31
  STANDARD_CODEPOINTS = Set[0x26, 0x3C, 0x3E, 0x22, 0x27].freeze
32
32
 
33
+ # The entity-registry conformance dataset (#496): lookup and
34
+ # restoration answers as data, pinned BY the Ruby suite
35
+ # (spec/moxml/entity_registry_conformance_spec.rb) and exported
36
+ # for the Rust/TS runners. Row shape: [seed, qname, qcp,
37
+ # restore_entities, restoration_mode, declared, codepoint, names,
38
+ # primary, standard, restore]. Each row seeds a fresh :custom
39
+ # registry through the public provider path — so the seed values
40
+ # are LEXICAL forms (raw characters, multi-character sequences,
41
+ # "\\uXXXX" escapes), exercising parse_codepoint exactly as a
42
+ # custom provider does — then queries one name and one codepoint
43
+ # and pins every face answer:
44
+ #
45
+ # * declared?/codepoint_for_name on qname
46
+ # * names_for_codepoint/primary_name_for_codepoint on qcp
47
+ # * standard_entity?/should_restore? on qcp under the row's config
48
+ #
49
+ # The committed semantics the rows pin: a multi-character lexical
50
+ # (an entity naming a sequence, e.g. "fjlig" -> "fj") maps to its
51
+ # FIRST character's codepoint; an empty lexical is dropped; the
52
+ # reverse map keeps insertion order with the lowercase name
53
+ # preferred as primary (falling back to the first name); the five
54
+ # predefined entities restore regardless of config; every other
55
+ # known codepoint restores only with restore_entities on (the
56
+ # strict mode answers as lenient until DTD parsing exists); an
57
+ # unknown codepoint never restores.
58
+ ENTITY_CONFORMANCE_CASES = [
59
+ [{ "amp" => "&" }, "amp", 38, false, "lenient",
60
+ true, 38, ["amp"], "amp", true, true],
61
+ [{ "NonBreakingSpace" => "\u00A0", "nbsp" => "\u00A0" },
62
+ "nbsp", 160, true, "lenient",
63
+ true, 160, ["NonBreakingSpace", "nbsp"], "nbsp", false, true],
64
+ [{ "nbsp" => "\u00A0" }, "nbsp", 160, false, "lenient",
65
+ true, 160, ["nbsp"], "nbsp", false, false],
66
+ [{ "nbsp" => "\u00A0" }, "nbsp", 160, true, "strict",
67
+ true, 160, ["nbsp"], "nbsp", false, true],
68
+ [{ "AElig" => "Æ" }, "AElig", 198, true, "lenient",
69
+ true, 198, ["AElig"], "AElig", false, true],
70
+ [{ "fjlig" => "fj" }, "fjlig", 102, true, "lenient",
71
+ true, 102, ["fjlig"], "fjlig", false, true],
72
+ [{ "DotDot" => "\u0020\u20DC" }, "DotDot", 32, true, "lenient",
73
+ true, 32, ["DotDot"], "DotDot", false, true],
74
+ [{ "NotEqualTilde" => "≂̸" }, "NotEqualTilde", 8770, true,
75
+ "lenient",
76
+ true, 8770, ["NotEqualTilde"], "NotEqualTilde", false, true],
77
+ [{ "esc" => "\\u00A0" }, "esc", 160, true, "lenient",
78
+ true, 160, ["esc"], "esc", false, true],
79
+ [{ "bad" => "" }, "bad", 0, true, "lenient",
80
+ false, nil, [], nil, false, false],
81
+ [{ "nbsp" => "\u00A0" }, "nope", 9999, true, "lenient",
82
+ false, nil, [], nil, false, false],
83
+ ].map(&:freeze).freeze
84
+
33
85
  class << self
34
86
  # Get the raw entity data from the bundled JSON source
35
87
  # @return [Hash{String => String}] entity name to character mapping
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.175"
4
+ VERSION = "0.5.177"
5
5
  end
@@ -78,17 +78,83 @@ module Moxml
78
78
  nil
79
79
  end
80
80
 
81
+ # The wildcard-face conformance dataset (#496): admission and
82
+ # negative-exclusion answers as data, pinned BY the Ruby suite
83
+ # (spec/moxml/wildcard_conformance_spec.rb) and exported for
84
+ # the Rust/TS runners. Row shape: [constraint, process,
85
+ # not_namespace, not_qname tokens, item_namespace, item_qname,
86
+ # defined, sibling_names, covers, exclusion]. Disallowed
87
+ # entries keep the pre-resolution token forms the constructor
88
+ # receives; item qnames are in the Clark form the face
89
+ # receives; item namespaces use the face's spellings
90
+ # ("local", "tns", or the URI).
91
+ WILDCARD_CASES = [
92
+ ["##any", "strict", nil, [], "local", "{}x", false, [], true, nil],
93
+ ["##any", "strict", nil, [], "tns", "{}x", false, [], true, nil],
94
+ ["##other", "strict", nil, [], "local", "{}x", false, [], false, nil],
95
+ ["##other", "strict", nil, [], "tns", "{}x", false, [], false, nil],
96
+ ["##other", "strict", nil, [], "urn:other", "{}x", false, [], true, nil],
97
+ ["##local", "strict", nil, [], "local", "{}x", false, [], true, nil],
98
+ ["##local", "strict", nil, [], "urn:b", "{}x", false, [], false, nil],
99
+ ["##targetNamespace urn:b", "strict", nil, [], "tns", "{}x", false, [],
100
+ true, nil],
101
+ ["##targetNamespace urn:b", "strict", nil, [], "local", "{}x", false,
102
+ [], false, nil],
103
+ ["urn:a urn:b", "strict", nil, [], "urn:a", "{}x", false, [], true,
104
+ nil],
105
+ ["urn:a urn:b", "strict", nil, [], "urn:c", "{}x", false, [], false,
106
+ nil],
107
+ ["urn:a", "lax", nil, [], "urn:a", "{}x", false, [], true, nil],
108
+ ["urn:a", "skip", nil, [], "urn:a", "{}x", false, [], true, nil],
109
+ ["##any", "strict", nil, ["x"], "local", "{}x", false, [], true,
110
+ "not_qname"],
111
+ ["##any", "strict", nil, ["x"], "local", "{}y", false, [], true, nil],
112
+ ["##any", "lax", nil, ["x"], "local", "{}x", true, [], true,
113
+ "not_qname"],
114
+ ["##any", "skip", nil, ["x"], "local", "{}x", true, [], true,
115
+ "not_qname"],
116
+ ["##any", "strict", nil, ["##defined"], "local", "{}x", false, [],
117
+ true, nil],
118
+ ["##any", "strict", nil, ["##defined"], "local", "{}x", true, [],
119
+ true, "not_qname"],
120
+ ["##any", "strict", nil, ["##definedSibling"], "local", "{}x", false,
121
+ ["{}y"], true, nil],
122
+ ["##any", "strict", nil, ["##definedSibling"], "local", "{}x", false,
123
+ ["{}x"], true, "not_qname"],
124
+ ["urn:a", "strict", "urn:z", [], "urn:a", "{}x", false, [], true,
125
+ nil],
126
+ ["urn:a", "strict", "urn:z", [], "urn:z", "{}x", false, [], false,
127
+ "not_namespace"],
128
+ ["urn:a", "lax", "urn:z", [], "urn:z", "{}x", false, [], false,
129
+ "not_namespace"],
130
+ ].map(&:freeze).freeze
131
+
81
132
  private
82
133
 
83
134
  def namespace_constraint(value, not_namespace)
84
135
  if not_namespace
85
- [:not, not_namespace.split(/\s+/).reject(&:empty?)]
136
+ [:not, namespace_entries(not_namespace)]
86
137
  elsif value.nil? || value == "##any" || value == ""
87
138
  [:any, []]
88
139
  elsif value == "##other"
89
140
  [:other, []]
90
141
  else
91
- [:list, value.split(/\s+/).reject(&:empty?)]
142
+ [:list, namespace_entries(value)]
143
+ end
144
+ end
145
+
146
+ # Namespace entries carry one internal convention: the
147
+ # special tokens keep their string spellings (entry_covers?
148
+ # maps them to :tns/:local); real URIs become Symbols, the
149
+ # same shape covers? receives. A String entry would never
150
+ # equal a Symbol namespace — the comparison would silently
151
+ # under-report on every :list and :not wildcard.
152
+ def namespace_entries(value)
153
+ value.to_s.split(/\s+/).reject(&:empty?).map do |entry|
154
+ case entry
155
+ when "##targetNamespace", "##local" then entry
156
+ else entry.to_sym
157
+ end
92
158
  end
93
159
  end
94
160
 
@@ -131,7 +197,7 @@ module Moxml
131
197
  :attribute_default, :elements,
132
198
  :attributes, :contents, :version,
133
199
  :element_patterns, :attribute_patterns,
134
- :substitution_members)
200
+ :substitution_members, :self_contained)
135
201
 
136
202
  # Resolves a QName token against an element's in-scope
137
203
  # bindings. node.namespaces is adapter-divergent (some
@@ -189,6 +255,35 @@ module Moxml
189
255
  nil
190
256
  end
191
257
 
258
+ # The cross-language conformance dataset (#496): the
259
+ # translator's answers as data, pinned BY the Ruby suite
260
+ # (spec/moxml/xsd_regex_conformance_spec.rb) and exported to
261
+ # conformance/moxml-conformance.json for the Rust/TS runners.
262
+ # Each row: [pattern, value, matches, translatable].
263
+ CONFORMANCE_CASES = [
264
+ ["\\p{IsBasicLatin}+", "abc", true, true],
265
+ ["\\p{IsBasicLatin}+", "€", false, true],
266
+ ["\\P{IsLatin-1Supplement}", "a", true, true],
267
+ ["\\P{IsLatin-1Supplement}", "é", false, true],
268
+ ["\\p{IsGreek}", "α", true, true],
269
+ ["\\p{IsGreek}", "a", false, true],
270
+ ["\\p{L}", "a", true, true],
271
+ ["\\p{L}", "1", false, true],
272
+ ["\\p{Nd}{1,3}", "5", true, true],
273
+ ["\\p{Nd}{1,3}", "abc", false, true],
274
+ ["\\d{1,3}", "5", false, false],
275
+ ["\\d{1,3}", "abc", false, false],
276
+ ["[0-9]{1,3}", "42", true, true],
277
+ ["[a-z]*", "", true, true],
278
+ ["a|b", "b", true, true],
279
+ ["a|b", "c", false, true],
280
+ ["a.c", "abc", true, true],
281
+ ["a\\.c", "a.c", true, true],
282
+ ["\\i\\c*", "x", false, false],
283
+ ["[\\p{IsBasicLatin}-[abc]]+", "a", false, false],
284
+ ["\\p{IsNoSuchBlock}", "a", false, false],
285
+ ].map(&:freeze).freeze
286
+
192
287
  def scan(pattern)
193
288
  out = +""
194
289
  index = 0
@@ -243,7 +338,7 @@ module Moxml
243
338
  # Index one schema document. Returns nil when the source
244
339
  # cannot be indexed — the engine's answer then stands alone
245
340
  # (today's behavior).
246
- def index(schema_source, context)
341
+ def index(schema_source, context, self_contained: nil)
247
342
  root = context.parse(schema_source).root
248
343
  return nil unless root&.name == "schema"
249
344
 
@@ -251,7 +346,8 @@ module Moxml
251
346
  root["targetNamespace"].to_s,
252
347
  root["elementFormDefault"] == "qualified",
253
348
  root["attributeFormDefault"] == "qualified",
254
- {}, {}, {}, schema_version(root), {}, {}, {}
349
+ {}, {}, {}, schema_version(root), {}, {}, {},
350
+ self_contained.nil? ? composition_free?(root) : self_contained
255
351
  )
256
352
  child_elements(root).each do |decl|
257
353
  case decl.name
@@ -265,6 +361,24 @@ module Moxml
265
361
  nil
266
362
  end
267
363
 
364
+ COMPOSITION_ELEMENTS = %w[include import redefine override].freeze
365
+
366
+ # A schema document that composes other documents
367
+ # (include/import/redefine/override) declares only part of
368
+ # the compiled declaration surface in its own source — the
369
+ # engine resolves the members, and a strict re-check run
370
+ # against this index alone would answer "not declared" for
371
+ # components that live in a member (#510): a false report,
372
+ # which the one-directional contract forbids. Composition
373
+ # therefore turns the strict re-checks off — the index
374
+ # under-reports instead. Schema sets (xsd_set) are always
375
+ # composition-shaped: any member may carry declarations.
376
+ def composition_free?(root)
377
+ child_elements(root).none? do |decl|
378
+ COMPOSITION_ELEMENTS.include?(decl.name)
379
+ end
380
+ end
381
+
268
382
  # The effective schema version declared at the root
269
383
  # (#460 item 2): vc:minVersion="1.1" on <xs:schema> scopes
270
384
  # the document to 1.1 semantics; anything else reports
@@ -511,12 +625,18 @@ module Moxml
511
625
  model = index.contents[root_qname]
512
626
  findings = []
513
627
  findings << nil_finding(root, declaration) if declaration
514
- findings.concat(attribute_findings(root, model, index))
515
- if model
628
+ # The strict wildcard/attribute re-checks answer "not
629
+ # declared" against this index's declaration sets — they
630
+ # vouch only when the index saw the whole declaration
631
+ # surface (no composition, #510).
632
+ if model && index.self_contained
516
633
  child_elements(root).each do |child|
517
634
  findings.concat(child_findings(child, model, index))
518
635
  end
519
636
  end
637
+ if index.self_contained
638
+ findings.concat(attribute_findings(root, model, index))
639
+ end
520
640
  findings.compact
521
641
  end
522
642
 
data/lib/moxml/xsd.rb CHANGED
@@ -96,11 +96,15 @@ module Moxml
96
96
  "schema released (deterministic drop); compile a new one"
97
97
  end
98
98
 
99
- def initialize(handle, context, schema_source = nil)
99
+ def initialize(handle, context, schema_source = nil,
100
+ self_contained: nil)
100
101
  @handle = handle
101
102
  @context = context
102
103
  @rules_index =
103
- schema_source && SchemaRules.index(schema_source, context)
104
+ if schema_source
105
+ SchemaRules.index(schema_source, context,
106
+ self_contained: self_contained)
107
+ end
104
108
  assert_adapter_matches_handle!
105
109
  end
106
110
 
data/lib/moxml.rb CHANGED
@@ -101,7 +101,7 @@ module Moxml
101
101
  autoload :Adapter, "moxml/adapter"
102
102
  autoload :XPath, "moxml/xpath"
103
103
  autoload :SAX, "moxml/sax"
104
- autoload :Signature, "moxml/signature"
104
+ autoload :Signature, "moxml/signature" unless RUBY_ENGINE == "opal"
105
105
  autoload :C14n, "moxml/c14n"
106
106
  autoload :Reference, "moxml/reference"
107
107
  autoload :XSLT, "moxml/xslt"
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Processing model §Entities (normative): pinned here.
4
+
5
+ require "spec_helper"
6
+
7
+ # The entity-registry conformance dataset (#496): lookup and
8
+ # restoration answers AS DATA, pinned by the Ruby suite and exported
9
+ # for the Rust/TS runners. A row failing here breaks the
10
+ # cross-language contract. Rows seed a fresh :custom registry through
11
+ # the public provider path — seed values are lexical forms — then pin
12
+ # every face answer for one name and one codepoint query.
13
+ RSpec.describe "entity-registry conformance dataset" do
14
+ it "carries the dataset" do
15
+ expect(Moxml::EntityRegistry::ENTITY_CONFORMANCE_CASES.size).to be >= 10
16
+ end
17
+
18
+ it "answers every row exactly as the dataset expects" do
19
+ Moxml::EntityRegistry::ENTITY_CONFORMANCE_CASES.each do |row|
20
+ seed, qname, qcp, restore_entities, restoration_mode, declared,
21
+ codepoint, names, primary, standard, restore = row
22
+ registry = Moxml::EntityRegistry.new(
23
+ mode: :custom,
24
+ entity_provider: -> { seed },
25
+ )
26
+
27
+ label = "seed #{seed.inspect}, qname #{qname.inspect}, qcp #{qcp}"
28
+
29
+ expect(registry.declared?(qname)).to eql(declared),
30
+ "declared?(#{qname.inspect}) [#{label}]"
31
+ expect(registry.codepoint_for_name(qname)).to eql(codepoint),
32
+ "codepoint_for_name(" \
33
+ "#{qname.inspect}) " \
34
+ "[#{label}]"
35
+ expect(registry.names_for_codepoint(qcp)).to eql(names),
36
+ "names_for_codepoint(" \
37
+ "#{qcp}) [#{label}]"
38
+ expect(registry.primary_name_for_codepoint(qcp)).to eql(primary),
39
+ "primary_name_" \
40
+ "for_codepoint(" \
41
+ "#{qcp}) [#{label}]"
42
+ expect(registry.standard_entity?(qcp)).to eql(standard),
43
+ "standard_entity?(#{qcp}) " \
44
+ "[#{label}]"
45
+ config = Moxml::Config.new
46
+ config.restore_entities = restore_entities
47
+ config.entity_restoration_mode = restoration_mode.to_sym
48
+ expect(registry.should_restore?(qcp, config: config)).to eql(restore),
49
+ "should_restore?(" \
50
+ "#{qcp}) " \
51
+ "[#{label}]"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "open3"
5
+ require "rbconfig"
6
+ require "tempfile"
7
+ require "opal"
8
+
9
+ RSpec.describe "Signature autoload boundary", :native_fs do
10
+ let(:lib) { File.expand_path("../../lib", __dir__) }
11
+
12
+ it "keeps the native signature module lazy and usable on Ruby" do
13
+ source = <<~RUBY_CODE
14
+ require "moxml"
15
+ abort "signature was loaded eagerly" if $LOADED_FEATURES.any? { |path| path.end_with?("moxml/signature.rb") }
16
+ abort "signature autoload missing" unless Moxml.autoload?(:Signature) == "moxml/signature"
17
+ abort "signature module unavailable" unless Moxml::Signature::DSIG_NS == "http://www.w3.org/2000/09/xmldsig#"
18
+ abort "OpenSSL unavailable" unless defined?(OpenSSL::PKey::RSA)
19
+ puts "native signature available"
20
+ RUBY_CODE
21
+ output, error, status = Open3.capture3(RbConfig.ruby, "-I", lib, "-e", source)
22
+ expect(status.success?).to be(true), error
23
+ expect(output).to include("native signature available")
24
+ end
25
+
26
+ it "does not compile the native OpenSSL dependency into Opal" do
27
+ # Compile the actual entrypoint declaration. This isolates the optional
28
+ # signature boundary from the other modules loaded by the Opal boot file.
29
+ declaration = File.readlines(File.join(lib, "moxml.rb"))
30
+ .find { |line| line.include?('autoload :Signature, "moxml/signature"') }
31
+ expect(declaration).not_to be_nil
32
+
33
+ builder = Opal::Builder.new
34
+ builder.append_paths(lib)
35
+ builder.build("opal")
36
+ builder.build_str(<<~RUBY_CODE, "signature-autoload-boundary")
37
+ module Moxml
38
+ #{declaration}
39
+ end
40
+ raise "native Signature loaded under Opal" if Moxml.const_defined?(:Signature, false)
41
+ puts "Opal signature boundary passed"
42
+ RUBY_CODE
43
+
44
+ Tempfile.create(["moxml-signature-boundary", ".js"]) do |file|
45
+ file.write(builder.to_s)
46
+ file.flush
47
+ output, error, status = Open3.capture3("node", file.path)
48
+ expect(status.success?).to be(true), error
49
+ expect(output).to include("Opal signature boundary passed")
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Processing model §Validation (normative): pinned here.
4
+
5
+ require "spec_helper"
6
+
7
+ # The wildcard-face conformance dataset (#496): admission (covers?)
8
+ # and negative-exclusion answers AS DATA, pinned by the Ruby suite
9
+ # and exported for the Rust/TS runners. A row failing here breaks the
10
+ # cross-language contract. Rows drive the real Wildcard through its
11
+ # public constructor — disallowed entries in their pre-resolution
12
+ # token form, item qnames in the Clark form the face receives.
13
+ RSpec.describe "wildcard-face conformance dataset" do
14
+ it "carries the dataset" do
15
+ expect(Moxml::XSD::SchemaRules::Wildcard::WILDCARD_CASES.size).to be >= 20
16
+ end
17
+
18
+ it "answers every row exactly as the dataset expects" do
19
+ Moxml::XSD::SchemaRules::Wildcard::WILDCARD_CASES.each do |row|
20
+ constraint, process, not_namespace, not_qname, item_ns, item_qname,
21
+ defined, sibling_names, covers, exclusion = row
22
+ wildcard = Moxml::XSD::SchemaRules::Wildcard.new(
23
+ constraint, process,
24
+ not_namespace: not_namespace, not_qname: not_qname.join(" "),
25
+ element: nil
26
+ )
27
+
28
+ covers_answer = wildcard.covers?(item_ns.to_sym)
29
+ expect(covers_answer).to eql(covers),
30
+ "covers?(#{item_ns.inspect}) under " \
31
+ "#{constraint.inspect}: got " \
32
+ "#{covers_answer.inspect}, dataset expects " \
33
+ "#{covers.inspect}"
34
+
35
+ exclusion_answer = wildcard.negative_exclusion(
36
+ item_qname, item_ns.to_sym,
37
+ defined: defined, sibling_names: sibling_names
38
+ )
39
+ expected_exclusion = exclusion&.to_sym
40
+ expect(exclusion_answer).to eql(expected_exclusion),
41
+ "negative_exclusion(#{item_qname.inspect}, " \
42
+ "#{item_ns.inspect}) under " \
43
+ "#{constraint.inspect}: got " \
44
+ "#{exclusion_answer.inspect}, dataset expects " \
45
+ "#{expected_exclusion.inspect}"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Processing model §Validation (normative): pinned here.
4
+
5
+ require "spec_helper"
6
+
7
+ # The regex-translator conformance dataset (#496): the translator's
8
+ # answers AS DATA — translatable-or-not, and what the translated
9
+ # regex does to sample values — pinned by the Ruby suite and exported
10
+ # for the Rust/TS runners. A row failing here breaks the
11
+ # cross-language contract.
12
+ RSpec.describe "XSD-regex translator conformance dataset" do
13
+ let(:translator) { Moxml::XSD::SchemaRules::XsdRegex }
14
+
15
+ it "carries the dataset" do
16
+ expect(translator::CONFORMANCE_CASES.size).to be >= 20
17
+ end
18
+
19
+ it "answers every row exactly as the dataset expects" do
20
+ translator::CONFORMANCE_CASES.each do |pattern, value, matches, translatable|
21
+ regex = translator.translate(pattern)
22
+ if translatable
23
+ expect(regex).not_to be_nil,
24
+ "#{pattern.inspect}: dataset says translatable, got nil"
25
+ answer = regex.match?(value)
26
+ expect(answer).to eql(matches),
27
+ "#{pattern.inspect} on #{value.inspect}: got " \
28
+ "#{answer.inspect}, dataset expects #{matches.inspect}"
29
+ else
30
+ expect(regex).to be_nil,
31
+ "#{pattern.inspect}: dataset says NOT translatable, " \
32
+ "got #{regex.inspect}"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -82,6 +82,84 @@ RSpec.describe "Schema-set compilation" do
82
82
  end
83
83
  end
84
84
 
85
+ # #510: the strict re-check answers "not declared" against the
86
+ # head document's own declaration index; composition (a set, or a
87
+ # single document carrying include/import/redefine/override)
88
+ # means declarations may live in a member the index never saw —
89
+ # the re-check must stay silent (under-report, never
90
+ # false-report) and let the engine's set-wide answer stand.
91
+ describe "strict re-check across composed schemas (#510)" do
92
+ def write_wildcard_set(dir)
93
+ File.write(File.join(dir, "headw.xsd"), <<~XSD)
94
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
95
+ xmlns="urn:W" targetNamespace="urn:W">
96
+ <xs:import namespace="urn:D" schemaLocation="decl.xsd"/>
97
+ <xs:element name="foo">
98
+ <xs:complexType>
99
+ <xs:sequence>
100
+ <xs:any namespace="##other" processContents="strict"/>
101
+ </xs:sequence>
102
+ </xs:complexType>
103
+ </xs:element>
104
+ </xs:schema>
105
+ XSD
106
+ File.write(File.join(dir, "decl.xsd"), <<~XSD)
107
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
108
+ xmlns="urn:D" targetNamespace="urn:D">
109
+ <xs:element name="bar" type="xs:string"/>
110
+ </xs:schema>
111
+ XSD
112
+ [File.join(dir, "headw.xsd"), File.join(dir, "decl.xsd")]
113
+ end
114
+
115
+ def wildcard_instance
116
+ '<foo xmlns="urn:W" xmlns:d="urn:D"><d:bar>hi</d:bar></foo>'
117
+ end
118
+
119
+ it "does not false-report a member-declared admission on a set" do
120
+ skip "requires the leptris compile_set face (binding 1.9.386.0+)" unless
121
+ set_capable?
122
+
123
+ Dir.mktmpdir do |dir|
124
+ paths = write_wildcard_set(dir)
125
+ schema = ctx.xsd_set(*paths)
126
+ expect(schema.validate(wildcard_instance)).to be_empty
127
+ end
128
+ end
129
+
130
+ it "does not false-report on a single composing document" do
131
+ Dir.mktmpdir do |dir|
132
+ write_wildcard_set(dir)
133
+ schema = ctx.xsd_file(File.join(dir, "headw.xsd"))
134
+ expect(schema.validate(wildcard_instance)).to be_empty
135
+ end
136
+ end
137
+
138
+ it "keeps the re-check on a self-contained schema" do
139
+ Dir.mktmpdir do |dir|
140
+ path = File.join(dir, "solo.xsd")
141
+ File.write(path, <<~XSD)
142
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
143
+ xmlns="urn:S" targetNamespace="urn:S">
144
+ <xs:element name="solo">
145
+ <xs:complexType>
146
+ <xs:sequence>
147
+ <xs:any namespace="##other" processContents="strict"/>
148
+ </xs:sequence>
149
+ </xs:complexType>
150
+ </xs:element>
151
+ </xs:schema>
152
+ XSD
153
+ schema = ctx.xsd_file(path)
154
+ errors = schema.validate(
155
+ '<solo xmlns="urn:S"><u:x xmlns:u="urn:U"/></solo>',
156
+ )
157
+ expect(errors.first.message)
158
+ .to include("matches a strict wildcard but is not declared")
159
+ end
160
+ end
161
+ end
162
+
85
163
  it "raises on an empty set" do
86
164
  expect { ctx.xsd_set }.to raise_error(ArgumentError)
87
165
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.175
4
+ version: 0.5.177
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -557,6 +557,7 @@ files:
557
557
  - spec/moxml/element_spec.rb
558
558
  - spec/moxml/entity_preservation_spec.rb
559
559
  - spec/moxml/entity_reference_spec.rb
560
+ - spec/moxml/entity_registry_conformance_spec.rb
560
561
  - spec/moxml/entity_registry_spec.rb
561
562
  - spec/moxml/entity_spec.rb
562
563
  - spec/moxml/error_spec.rb
@@ -606,11 +607,13 @@ files:
606
607
  - spec/moxml/signature/key_extractor_spec.rb
607
608
  - spec/moxml/signature/model_spec.rb
608
609
  - spec/moxml/signature/round_trip_spec.rb
610
+ - spec/moxml/signature_autoload_spec.rb
609
611
  - spec/moxml/struct_plan_spec.rb
610
612
  - spec/moxml/struct_plan_typed_spec.rb
611
613
  - spec/moxml/text_spec.rb
612
614
  - spec/moxml/to_html_spec.rb
613
615
  - spec/moxml/version_spec.rb
616
+ - spec/moxml/wildcard_conformance_spec.rb
614
617
  - spec/moxml/xhtml_output_spec.rb
615
618
  - spec/moxml/xml_emitter_spec.rb
616
619
  - spec/moxml/xml_utils/.gitkeep
@@ -643,6 +646,7 @@ files:
643
646
  - spec/moxml/xsd_engine_meta_spec.rb
644
647
  - spec/moxml/xsd_error_contract_spec.rb
645
648
  - spec/moxml/xsd_handle_mismatch_spec.rb
649
+ - spec/moxml/xsd_regex_conformance_spec.rb
646
650
  - spec/moxml/xsd_regex_spec.rb
647
651
  - spec/moxml/xsd_release_spec.rb
648
652
  - spec/moxml/xsd_set_spec.rb