@@ -594,4 +594,254 @@ public static function provideGetRawTokensTrueTests(): iterable
594594 yield [['app/console ' , '--no-ansi ' , 'foo:bar ' , 'foo:bar ' ], ['foo:bar ' ]];
595595 yield [['app/console ' , '--no-ansi ' , 'foo:bar ' , '-- ' , 'argument ' ], ['-- ' , 'argument ' ]];
596596 }
597+
598+ /**
599+ * @dataProvider unparseProvider
600+ */
601+ public function testUnparse (
602+ ?InputDefinition $ inputDefinition ,
603+ ArgvInput $ input ,
604+ ?array $ parsedOptions ,
605+ array $ expected ,
606+ )
607+ {
608+ if (null !== $ inputDefinition ) {
609+ $ input ->bind ($ inputDefinition );
610+ }
611+
612+ $ actual = null === $ parsedOptions ? $ input ->unparse () : $ input ->unparse ($ parsedOptions );
613+
614+ self ::assertEquals ($ expected , $ actual );
615+ }
616+
617+ public static function unparseProvider (): iterable
618+ {
619+ yield 'empty input and empty definition ' => [
620+ new InputDefinition (),
621+ new ArgvInput ([]),
622+ [],
623+ [],
624+ ];
625+
626+ yield 'empty input and definition with default values: ignore default values ' => [
627<
67DE
/code>+ new InputDefinition ([
628+ new InputArgument (
629+ 'argWithDefaultValue ' ,
630+ InputArgument::OPTIONAL ,
631+ 'Argument with a default value ' ,
632+ 'arg1DefaultValue ' ,
633+ ),
634+ new InputOption (
635+ 'optWithDefaultValue ' ,
636+ null ,
637+ InputOption::VALUE_REQUIRED ,
638+ 'Option with a default value ' ,
639+ 'opt1DefaultValue ' ,
640+ ),
641+ ]),
642+ new ArgvInput ([]),
643+ [],
644+ [],
645+ ];
646+
647+ $ completeInputDefinition = new InputDefinition ([
648+ new InputArgument (
649+ 'requiredArgWithoutDefaultValue ' ,
650+ InputArgument::REQUIRED ,
651+ 'Argument without a default value ' ,
652+ ),
653+ new InputArgument (
654+ 'optionalArgWithDefaultValue ' ,
655+ InputArgument::OPTIONAL ,
656+ 'Argument with a default value ' ,
657+ 'argDefaultValue ' ,
658+ ),
659+ new InputOption (
660+ 'optWithoutDefaultValue ' ,
661+ null ,
662+ InputOption::VALUE_REQUIRED ,
663+ 'Option without a default value ' ,
664+ ),
665+ new InputOption (
666+ 'optWithDefaultValue ' ,
667+ null ,
668+ InputOption::VALUE_REQUIRED ,
669+ 'Option with a default value ' ,
670+ 'optDefaultValue ' ,
671+ ),
672+ ]);
673+
674+ yield 'arguments & options: returns all passed options but ignore default values ' => [
675+ $ completeInputDefinition ,
676+ new ArgvInput (['argValue ' , '--optWithoutDefaultValue=optValue ' ]),
677+ [],
678+ ['--optWithoutDefaultValue=optValue ' ],
679+ ];
680+
681+ yield 'arguments & options; explicitly pass the default values: the default values are returned ' => [
682+ $ completeInputDefinition ,
683+ new ArgvInput (['argValue ' , 'argDefaultValue ' , '--optWithoutDefaultValue=optValue ' , '--optWithDefaultValue=optDefaultValue ' ]),
684+ [],
685+ [
686+ '--optWithoutDefaultValue=optValue ' ,
687+ '--optWithDefaultValue=optDefaultValue ' ,
688+ ],
689+ ];
690+
691+ yield 'arguments & options; parsing an argument name instead of an option name: that option is ignored ' => [
692+ $ completeInputDefinition ,
693+ new ArgvInput (['argValue ' ]),
694+ ['requiredArgWithoutDefaultValue ' ],
695+ [],
696+ ];
697+
698+ yield 'arguments & options; non passed option: it is ignored ' => [
699+ $ completeInputDefinition ,
700+ new ArgvInput (['argValue ' ]),
701+ ['optWithDefaultValue ' ],
702+ [],
703+ ];
704+
705+ $ createSingleOptionScenario = static fn (
706+ InputOption $ option ,
707+ array $ input ,
708+ array $ expected
709+ ) => [
710+ new InputDefinition ([$ option ]),
711+ new ArgvInput (['appName ' , ...$ input ]),
712+ [],
713+ $ expected ,
714+ ];
715+
716+ yield 'option without value ' => $ createSingleOptionScenario (
717+ new InputOption (
718+ 'opt ' ,
719+ null ,
720+ InputOption::VALUE_NONE ,
721+ ),
722+ ['--opt ' ],
723+ ['--opt ' ],
724+ );
725+
726+ yield 'option without value by shortcut ' => $ createSingleOptionScenario (
727+ new InputOption (
728+ 'opt ' ,
729+ 'o ' ,
730+ InputOption::VALUE_NONE ,
731+ ),
732+ ['-o ' ],
733+ ['--opt ' ],
734+ );
735+
736+ yield 'option with value required ' => $ createSingleOptionScenario (
737+ new InputOption (
738+ 'opt ' ,
739+ null ,
740+ InputOption::VALUE_REQUIRED ,
741+ ),
742+ ['--opt=foo ' ],
743+ ['--opt=foo ' ],
744+ );
745+
746+ yield 'option with non string value (bool) ' => $ createSingleOptionScenario (
747+ new InputOption (
748+ 'opt ' ,
749+ null ,
750+ InputOption::VALUE_REQUIRED ,
751+ ),
752+ ['--opt=1 ' ],
753+ ['--opt=1 ' ],
754+ );
755+
756+ yield 'option with non string value (int) ' => $ createSingleOptionScenario (
757+ new InputOption (
758+ 'opt ' ,
759+ null ,
760+ InputOption::VALUE_REQUIRED ,
761+ ),
762+ ['--opt=20 ' ],
763+ ['--opt=20 ' ],
764+ );
765+
766+ yield 'option with non string value (float) ' => $ createSingleOptionScenario (
767+ new InputOption (
768+ 'opt ' ,
769+ null ,
770+ InputOption::VALUE_REQUIRED ,
771+ ),
772+ ['--opt=5.3 ' ],
773+ ['--opt= \'5.3 \'' ],
774+ );
775+
776+ yield 'option with non string value (array of strings) ' => $ createSingleOptionScenario (
777+ new InputOption (
778+ 'opt ' ,
779+ null ,
780+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY ,
781+ ),
782+ ['--opt=v1 ' , '--opt=v2 ' , '--opt=v3 ' ],
783+ ['--opt=v1--opt=v2--opt=v3 ' ],
784+ );
785+
786+ yield 'negatable option (positive) ' => $ createSingleOptionScenario (
787+ new InputOption (
788+ 'opt ' ,
789+ null ,
790+ InputOption::VALUE_NEGATABLE ,
791+ ),
792+ ['--opt ' ],
793+ ['--opt ' ],
794+ );
795+
796+ yield 'negatable option (negative) ' => $ createSingleOptionScenario (
797+ new InputOption (
798+ 'opt ' ,
799+ null ,
800+ InputOption::VALUE_NEGATABLE ,
801+ ),
802+ ['--no-opt ' ],
803+ ['--no-opt ' ],
804+ );
805+
806+ $ createEscapeOptionTokenScenario = static fn (
807+ string $ optionValue ,
808+ ?string $ expected
809+ ) => [
810+ new InputDefinition ([
811+ new InputOption (
812+ 'opt ' ,
813+ null ,
814+ InputOption::VALUE_REQUIRED ,
815+ ),
816+ ]),
817+ new ArgvInput (['appName ' , '--opt= ' .$ optionValue ]),
818+ [],
819+ ['--opt= ' .$ expected ],
820+ ];
821+
822+ yield 'escape token; string token ' => $ createEscapeOptionTokenScenario (
823+ 'foo ' ,
824+ 'foo ' ,
825+ );
826+
827+ yield 'escape token; escaped string token ' => $ createEscapeOptionTokenScenario (
828+ '"foo" ' ,
829+ escapeshellarg ('"foo" ' ),
830+ );
831+
832+ yield 'escape token; escaped string token with both types of quotes ' => $ createEscapeOptionTokenScenario (
833+ '"o_id in( \'20 \')" ' ,
834+ escapeshellarg ('"o_id in( \'20 \')" ' ),
835+ );
836+
837+ yield 'escape token; string token with spaces ' => $ createEscapeOptionTokenScenario (
838+ 'a b c d ' ,
839+ escapeshellarg ('a b c d ' ),
840+ );
841+
842+ yield 'escape token; string token with line return ' => $ createEscapeOptionTokenScenario (
843+ "A \nB'C " ,
844+ escapeshellarg ("A \nB'C " ),
845+ );
846+ }
597847}
0 commit comments