1use std::{
2 ffi::OsStr,
3 path::PathBuf,
4 process::{Child, Stdio},
5};
6
7use super::{ExclusiveOption, SubCommand, Unselected};
8
9use crate::global::GlobalOpts;
10use crate::spawn::ParameterizedSpawn;
11
12#[derive(Debug, Clone, Copy, Default)]
14pub struct FullDescription;
15
16impl ExclusiveOption for FullDescription {
17 fn inject_args(&self, command: &mut std::process::Command) {
18 command.arg("-l");
19 }
20}
21
22#[derive(Debug, Clone, Copy, Default)]
24pub struct TruncatedDescription;
25
26impl ExclusiveOption for TruncatedDescription {
27 fn inject_args(&self, command: &mut std::process::Command) {
28 command.arg("-L");
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
34pub enum User {
35 User(String),
37 #[cfg(not(feature = "lt2016_1"))]
39 Me,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum Status {
45 Pending,
47 Submitted,
49 Shelved,
51}
52
53impl Status {
54 pub fn as_str(&self) -> &'static str {
56 match self {
57 Status::Pending => "pending",
58 Status::Submitted => "submitted",
59 Status::Shelved => "shelved",
60 }
61 }
62}
63
64#[cfg_attr(
65 feature = "lt2015_2",
66 doc = "`p4 [g-opts] changes [-i -t -l -L -f] [-c client] [-m max] [-s status] [-u user] [file[RevRange] ...]`"
67)]
68#[cfg_attr(
69 all(feature = "lt2016_1", not(feature = "lt2015_2")),
70 doc = "`p4 [g-opts] changes [-i -t -l -L -f] [-c client] [ -e changelist#][-m max] [-s status] [-u user][file[RevRange] ...]`"
71)]
72#[cfg_attr(
73 all(feature = "lt2017_2", not(feature = "lt2016_1")),
74 doc = "`p4 [g-opts] changes [-i -t -l -L -f] [-c client] [ -e changelist#][-m max] [-s status] [-u user | --me][file[RevRange] ...]`"
75)]
76#[cfg_attr(
77 all(feature = "lt2022_2", not(feature = "lt2017_2")),
78 doc = "`p4 [g-opts] changes [-i -t -l -L -f] [-c client] [ -e changelist#][-m max] [-r] [-s status] [-u user | --me] [file[RevRange] ...]`"
79)]
80#[cfg_attr(
81 not(feature = "lt2022_2"),
82 doc = "`p4 [g-opts] changes [-i -t -l -L -f] [-c client] [ -e changelist#][-m max] [-r] [-s status] [-u user | --me] [file[RevRange] ...] [--stream | --nostream]`"
83)]
84#[derive(Debug, Clone, Default)]
93pub struct Changes<L = Unselected> {
94 bin: PathBuf,
95
96 global_opts: GlobalOpts,
97
98 #[cfg(not(feature = "lt2015_2"))]
99 min_change_list: Option<String>,
100
101 include_restricted: bool,
102
103 include_integrated: bool,
104
105 include_time: bool,
106
107 long_output: L,
108
109 limit: Option<u64>,
110
111 #[cfg(not(feature = "lt2017_2"))]
112 reverse_order: bool,
113
114 filter_status: Option<Status>,
115
116 filter_users: Option<Vec<User>>,
117
118 filter_clients: Option<Vec<String>>,
119
120 #[cfg(not(feature = "lt2025_1"))]
121 client_case_insensitive: bool,
122
123 #[cfg(not(feature = "lt2022_2"))]
124 stream: Option<bool>,
125}
126
127impl Changes<Unselected> {
128 pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
132 Self {
133 bin: bin.into(),
134 global_opts,
135 ..Self::default()
136 }
137 }
138
139 pub fn long_output_full(self) -> Changes<FullDescription> {
147 Changes {
148 bin: self.bin,
149 global_opts: self.global_opts,
150 #[cfg(not(feature = "lt2015_2"))]
151 min_change_list: self.min_change_list,
152 include_restricted: self.include_restricted,
153 include_integrated: self.include_integrated,
154 include_time: self.include_time,
155 long_output: FullDescription,
156 limit: self.limit,
157 #[cfg(not(feature = "lt2017_2"))]
158 reverse_order: self.reverse_order,
159 filter_status: self.filter_status,
160 filter_users: self.filter_users,
161 filter_clients: self.filter_clients,
162 #[cfg(not(feature = "lt2025_1"))]
163 client_case_insensitive: self.client_case_insensitive,
164 #[cfg(not(feature = "lt2022_2"))]
165 stream: self.stream,
166 }
167 }
168
169 pub fn long_output_truncated(self) -> Changes<TruncatedDescription> {
178 Changes {
179 bin: self.bin,
180 global_opts: self.global_opts,
181 #[cfg(not(feature = "lt2015_2"))]
182 min_change_list: self.min_change_list,
183 include_restricted: self.include_restricted,
184 include_integrated: self.include_integrated,
185 include_time: self.include_time,
186 long_output: TruncatedDescription,
187 limit: self.limit,
188 #[cfg(not(feature = "lt2017_2"))]
189 reverse_order: self.reverse_order,
190 filter_status: self.filter_status,
191 filter_users: self.filter_users,
192 filter_clients: self.filter_clients,
193 #[cfg(not(feature = "lt2025_1"))]
194 client_case_insensitive: self.client_case_insensitive,
195 #[cfg(not(feature = "lt2022_2"))]
196 stream: self.stream,
197 }
198 }
199}
200
201impl<L: ExclusiveOption, S, I> ParameterizedSpawn<(S,)> for Changes<L>
202where
203 S: IntoIterator<Item = I>,
204 I: AsRef<OsStr>,
205{
206 type Output = Child;
207 type Error = std::io::Error;
208
209 fn spawn_with(&mut self, (files,): (S,)) -> Result<Self::Output, Self::Error> {
216 self.setup_command(&self.bin)
217 .args(files)
218 .stdout(Stdio::piped())
219 .stderr(Stdio::piped())
220 .spawn()
221 }
222}
223
224impl<L: ExclusiveOption> Changes<L> {
225 #[cfg_attr(
230 feature = "lt2014_2",
231 doc = "See the [Global Options](GlobalOpts) section."
232 )]
233 #[cfg_attr(
234 all(feature = "lt2015_1", not(feature = "lt2014_2")),
235 doc = "See the [“Global Options”](GlobalOpts) section."
236 )]
237 #[cfg_attr(
238 all(feature = "lt2017_1", not(feature = "lt2015_1")),
239 doc = "See [“Global Options”](GlobalOpts)."
240 )]
241 #[cfg_attr(
242 all(feature = "lt2018_2", not(feature = "lt2017_1")),
243 doc = "See [Global Options](GlobalOpts)."
244 )]
245 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
246 pub fn get_global_opts(&self) -> &GlobalOpts {
247 &self.global_opts
248 }
249
250 #[cfg_attr(
255 feature = "lt2014_2",
256 doc = "See the [Global Options](GlobalOpts) section."
257 )]
258 #[cfg_attr(
259 all(feature = "lt2015_1", not(feature = "lt2014_2")),
260 doc = "See the [“Global Options”](GlobalOpts) section."
261 )]
262 #[cfg_attr(
263 all(feature = "lt2017_1", not(feature = "lt2015_1")),
264 doc = "See [“Global Options”](GlobalOpts)."
265 )]
266 #[cfg_attr(
267 all(feature = "lt2018_2", not(feature = "lt2017_1")),
268 doc = "See [Global Options](GlobalOpts)."
269 )]
270 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
271 pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
272 self.global_opts = v;
273 self
274 }
275
276 #[cfg_attr(
281 feature = "lt2014_2",
282 doc = "See the [Global Options](GlobalOpts) section."
283 )]
284 #[cfg_attr(
285 all(feature = "lt2015_1", not(feature = "lt2014_2")),
286 doc = "See the [“Global Options”](GlobalOpts) section."
287 )]
288 #[cfg_attr(
289 all(feature = "lt2017_1", not(feature = "lt2015_1")),
290 doc = "See [“Global Options”](GlobalOpts)."
291 )]
292 #[cfg_attr(
293 all(feature = "lt2018_2", not(feature = "lt2017_1")),
294 doc = "See [Global Options](GlobalOpts)."
295 )]
296 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
297 pub fn global_opts(mut self, v: GlobalOpts) -> Self {
298 self.global_opts = v;
299 self
300 }
301
302 pub fn get_filter_clients(&self) -> Option<&[String]> {
309 self.filter_clients.as_deref()
310 }
311
312 pub fn set_filter_client(&mut self, v: impl Into<String>) -> &mut Self {
319 self.filter_clients
320 .get_or_insert_with(Vec::new)
321 .push(v.into());
322 self
323 }
324
325 pub fn filter_client(mut self, v: impl Into<String>) -> Self {
332 self.filter_clients
333 .get_or_insert_with(Vec::new)
334 .push(v.into());
335 self
336 }
337
338 #[cfg(not(feature = "lt2015_2"))]
345 pub fn get_min_change_list(&self) -> Option<&String> {
346 self.min_change_list.as_ref()
347 }
348
349 #[cfg(not(feature = "lt2015_2"))]
356 pub fn set_min_change_list(&mut self, v: impl Into<String>) -> &mut Self {
357 self.min_change_list = Some(v.into());
358 self
359 }
360
361 #[cfg(not(feature = "lt2015_2"))]
368 pub fn min_change_list(mut self, v: impl Into<String>) -> Self {
369 self.min_change_list = Some(v.into());
370 self
371 }
372
373 pub fn get_include_restricted(&self) -> bool {
379 self.include_restricted
380 }
381
382 pub fn set_include_restricted(&mut self, v: bool) -> &mut Self {
388 self.include_restricted = v;
389 self
390 }
391
392 pub fn include_restricted(mut self, v: bool) -> Self {
398 self.include_restricted = v;
399 self
400 }
401
402 pub fn get_include_integrated(&self) -> bool {
409 self.include_integrated
410 }
411
412 pub fn set_include_integrated(&mut self, v: bool) -> &mut Self {
419 self.include_integrated = v;
420 self
421 }
422
423 pub fn include_integrated(mut self, v: bool) -> Self {
430 self.include_integrated = v;
431 self
432 }
433
434 pub fn get_limit(&self) -> Option<u64> {
440 self.limit
441 }
442
443 pub fn set_limit(&mut self, v: u64) -> &mut Self {
449 self.limit = Some(v);
450 self
451 }
452
453 pub fn limit(mut self, v: u64) -> Self {
459 self.limit = Some(v);
460 self
461 }
462
463 #[cfg(not(feature = "lt2017_2"))]
470 pub fn get_reverse_order(&self) -> bool {
471 self.reverse_order
472 }
473
474 #[cfg(not(feature = "lt2017_2"))]
481 pub fn set_reverse_order(&mut self, v: bool) -> &mut Self {
482 self.reverse_order = v;
483 self
484 }
485
486 #[cfg(not(feature = "lt2017_2"))]
493 pub fn reverse_order(mut self, v: bool) -> Self {
494 self.reverse_order = v;
495 self
496 }
497
498 pub fn get_status(&self) -> Option<Status> {
505 self.filter_status
506 }
507
508 pub fn set_status(&mut self, v: Status) -> &mut Self {
515 self.filter_status = Some(v);
516 self
517 }
518
519 pub fn status(mut self, v: Status) -> Self {
526 self.filter_status = Some(v);
527 self
528 }
529
530 pub fn get_include_time(&self) -> bool {
536 self.include_time
537 }
538
539 pub fn set_include_time(&mut self, v: bool) -> &mut Self {
545 self.include_time = v;
546 self
547 }
548
549 pub fn include_time(mut self, v: bool) -> Self {
555 self.include_time = v;
556 self
557 }
558
559 #[cfg_attr(feature = "lt2016_1", doc = "-u user")]
562 #[cfg_attr(not(feature = "lt2016_1"), doc = "-u user / --me")]
563 #[cfg_attr(
565 feature = "lt2016_1",
566 doc = "List only changes made from the named user. This option can be repeated to filter for multiple users."
567 )]
568 #[cfg_attr(
569 not(feature = "lt2016_1"),
570 doc = "List only changes made from the named user, or, with [`User::Me`], the current user (equivalent to `-u $P4USER`). This option can be repeated to filter for multiple users."
571 )]
572 pub fn get_filter_users(&self) -> Option<&[User]> {
573 self.filter_users.as_deref()
574 }
575
576 pub fn set_filter_user(&mut self, v: impl Into<String>) -> &mut Self {
583 self.filter_users
584 .get_or_insert_with(Vec::new)
585 .push(User::User(v.into()));
586 self
587 }
588
589 pub fn filter_user(mut self, v: impl Into<String>) -> Self {
596 self.filter_users
597 .get_or_insert_with(Vec::new)
598 .push(User::User(v.into()));
599 self
600 }
601
602 #[cfg(not(feature = "lt2016_1"))]
608 pub fn set_filter_me(&mut self) -> &mut Self {
609 self.filter_users
610 .get_or_insert_with(Vec::new)
611 .push(User::Me);
612 self
613 }
614
615 #[cfg(not(feature = "lt2016_1"))]
621 pub fn filter_me(mut self) -> Self {
622 self.filter_users
623 .get_or_insert_with(Vec::new)
624 .push(User::Me);
625 self
626 }
627
628 #[cfg(not(feature = "lt2025_1"))]
635 pub fn get_client_case_insensitive(&self) -> bool {
636 self.client_case_insensitive
637 }
638
639 #[cfg(not(feature = "lt2025_1"))]
646 pub fn set_client_case_insensitive(&mut self, v: bool) -> &mut Self {
647 self.client_case_insensitive = v;
648 self
649 }
650
651 #[cfg(not(feature = "lt2025_1"))]
658 pub fn client_case_insensitive(mut self, v: bool) -> Self {
659 self.client_case_insensitive = v;
660 self
661 }
662
663 #[cfg(not(feature = "lt2022_2"))]
671 pub fn get_stream(&self) -> Option<bool> {
672 self.stream
673 }
674
675 #[cfg(not(feature = "lt2022_2"))]
683 pub fn set_stream(&mut self, v: bool) -> &mut Self {
684 self.stream = Some(v);
685 self
686 }
687
688 #[cfg(not(feature = "lt2022_2"))]
696 pub fn stream(mut self, v: bool) -> Self {
697 self.stream = Some(v);
698 self
699 }
700}
701
702impl<L: ExclusiveOption> SubCommand for Changes<L> {
703 fn name(&self) -> &str {
704 "changes"
705 }
706
707 fn inject_local_args(&self, command: &mut std::process::Command) {
708 if let Some(ref clients) = self.filter_clients {
709 for client in clients {
710 command.arg("-c").arg(client);
711 }
712 }
713 #[cfg(not(feature = "lt2025_1"))]
714 if self.client_case_insensitive {
715 command.arg("--client-case-insensitive");
716 }
717 #[cfg(not(feature = "lt2015_2"))]
718 if let Some(ref min_change) = self.min_change_list {
719 command.arg("-e").arg(min_change);
720 }
721 if self.include_restricted {
722 command.arg("-f");
723 }
724 if self.include_integrated {
725 command.arg("-i");
726 }
727 self.long_output.inject_args(command);
728 if let Some(max) = self.limit {
729 command.arg("-m").arg(max.to_string());
730 }
731 #[cfg(not(feature = "lt2017_2"))]
732 if self.reverse_order {
733 command.arg("-r");
734 }
735 if let Some(status) = self.filter_status {
736 command.arg("-s").arg(status.as_str());
737 }
738 if self.include_time {
739 command.arg("-t");
740 }
741 if let Some(ref users) = self.filter_users {
742 for user in users {
743 match user {
744 User::User(name) => {
745 command.arg("-u").arg(name);
746 }
747 #[cfg(not(feature = "lt2016_1"))]
748 User::Me => {
749 command.arg("--me");
750 }
751 }
752 }
753 }
754 #[cfg(not(feature = "lt2022_2"))]
755 if let Some(stream) = self.stream {
756 if stream {
757 command.arg("--stream");
758 } else {
759 command.arg("--nostream");
760 }
761 }
762 }
763
764 fn global_opts(&self) -> Option<&GlobalOpts> {
765 Some(&self.global_opts)
766 }
767}
768
769#[cfg(test)]
770mod tests {
771 use super::*;
772 use crate::cmd::args_of;
773
774 #[test]
775 fn without_options() {
776 let changes = Changes::new("p4", GlobalOpts::default());
777 let cmd = changes.setup_command("p4");
778 assert_eq!(args_of(&cmd), vec!["changes"]);
779 }
780
781 #[test]
782 fn with_files() {
783 let changes = Changes::new("p4", GlobalOpts::default());
784 let mut cmd = changes.setup_command("p4");
785 cmd.arg("//depot/project/...");
786 assert_eq!(args_of(&cmd), vec!["changes", "//depot/project/..."]);
787 }
788
789 #[test]
790 fn client() {
791 let changes = Changes::new("p4", GlobalOpts::default()).filter_client("eds_elm");
792 let cmd = changes.setup_command("p4");
793 assert_eq!(args_of(&cmd), vec!["changes", "-c", "eds_elm"]);
794 }
795
796 #[test]
797 fn multiple_clients() {
798 let changes = Changes::new("p4", GlobalOpts::default())
799 .filter_client("eds_elm")
800 .filter_client("build_ws");
801 let cmd = changes.setup_command("p4");
802 assert_eq!(
803 args_of(&cmd),
804 vec!["changes", "-c", "eds_elm", "-c", "build_ws"]
805 );
806 }
807
808 #[cfg(not(feature = "lt2015_2"))]
809 #[test]
810 fn min_change_list() {
811 let changes = Changes::new("p4", GlobalOpts::default()).min_change_list("800");
812 let cmd = changes.setup_command("p4");
813 assert_eq!(args_of(&cmd), vec!["changes", "-e", "800"]);
814 }
815
816 #[test]
817 fn include_restricted() {
818 let changes = Changes::new("p4", GlobalOpts::default()).include_restricted(true);
819 let cmd = changes.setup_command("p4");
820 assert_eq!(args_of(&cmd), vec!["changes", "-f"]);
821 }
822
823 #[test]
824 fn include_integrated() {
825 let changes = Changes::new("p4", GlobalOpts::default()).include_integrated(true);
826 let cmd = changes.setup_command("p4");
827 assert_eq!(args_of(&cmd), vec!["changes", "-i"]);
828 }
829
830 #[test]
831 fn long_output_full() {
832 let changes = Changes::new("p4", GlobalOpts::default()).long_output_full();
833 let cmd = changes.setup_command("p4");
834 assert_eq!(args_of(&cmd), vec!["changes", "-l"]);
835 }
836
837 #[test]
838 fn long_output_truncated() {
839 let changes = Changes::new("p4", GlobalOpts::default()).long_output_truncated();
840 let cmd = changes.setup_command("p4");
841 assert_eq!(args_of(&cmd), vec!["changes", "-L"]);
842 }
843
844 #[test]
845 fn limit() {
846 let changes = Changes::new("p4", GlobalOpts::default()).limit(5);
847 let cmd = changes.setup_command("p4");
848 assert_eq!(args_of(&cmd), vec!["changes", "-m", "5"]);
849 }
850
851 #[cfg(not(feature = "lt2017_2"))]
852 #[test]
853 fn reverse_order() {
854 let changes = Changes::new("p4", GlobalOpts::default()).reverse_order(true);
855 let cmd = changes.setup_command("p4");
856 assert_eq!(args_of(&cmd), vec!["changes", "-r"]);
857 }
858
859 #[test]
860 fn status_pending() {
861 let changes = Changes::new("p4", GlobalOpts::default()).status(Status::Pending);
862 let cmd = changes.setup_command("p4");
863 assert_eq!(args_of(&cmd), vec!["changes", "-s", "pending"]);
864 }
865
866 #[test]
867 fn status_submitted() {
868 let changes = Changes::new("p4", GlobalOpts::default()).status(Status::Submitted);
869 let cmd = changes.setup_command("p4");
870 assert_eq!(args_of(&cmd), vec!["changes", "-s", "submitted"]);
871 }
872
873 #[test]
874 fn status_shelved() {
875 let changes = Changes::new("p4", GlobalOpts::default()).status(Status::Shelved);
876 let cmd = changes.setup_command("p4");
877 assert_eq!(args_of(&cmd), vec!["changes", "-s", "shelved"]);
878 }
879
880 #[test]
881 fn include_time() {
882 let changes = Changes::new("p4", GlobalOpts::default()).include_time(true);
883 let cmd = changes.setup_command("p4");
884 assert_eq!(args_of(&cmd), vec!["changes", "-t"]);
885 }
886
887 #[test]
888 fn user_name() {
889 let changes = Changes::new("p4", GlobalOpts::default()).filter_user("edk");
890 let cmd = changes.setup_command("p4");
891 assert_eq!(args_of(&cmd), vec!["changes", "-u", "edk"]);
892 }
893
894 #[test]
895 fn multiple_users() {
896 let changes = Changes::new("p4", GlobalOpts::default())
897 .filter_user("maria")
898 .filter_user("edk");
899 let cmd = changes.setup_command("p4");
900 assert_eq!(args_of(&cmd), vec!["changes", "-u", "maria", "-u", "edk"]);
901 }
902
903 #[cfg(not(feature = "lt2016_1"))]
904 #[test]
905 fn user_me() {
906 let changes = Changes::new("p4", GlobalOpts::default()).filter_me();
907 let cmd = changes.setup_command("p4");
908 assert_eq!(args_of(&cmd), vec!["changes", "--me"]);
909 }
910
911 #[cfg(not(feature = "lt2016_1"))]
912 #[test]
913 fn multiple_users_with_me() {
914 let changes = Changes::new("p4", GlobalOpts::default())
915 .filter_user("maria")
916 .filter_me();
917 let cmd = changes.setup_command("p4");
918 assert_eq!(args_of(&cmd), vec!["changes", "-u", "maria", "--me"]);
919 }
920
921 #[cfg(not(feature = "lt2025_1"))]
922 #[test]
923 fn client_case_insensitive() {
924 let changes = Changes::new("p4", GlobalOpts::default())
925 .filter_client("eds_elm")
926 .client_case_insensitive(true);
927 let cmd = changes.setup_command("p4");
928 assert_eq!(
929 args_of(&cmd),
930 vec!["changes", "-c", "eds_elm", "--client-case-insensitive"]
931 );
932 }
933
934 #[cfg(not(feature = "lt2022_2"))]
935 #[test]
936 fn stream_spec() {
937 let changes = Changes::new("p4", GlobalOpts::default()).stream(true);
938 let cmd = changes.setup_command("p4");
939 assert_eq!(args_of(&cmd), vec!["changes", "--stream"]);
940 }
941
942 #[cfg(not(feature = "lt2022_2"))]
943 #[test]
944 fn no_stream_spec() {
945 let changes = Changes::new("p4", GlobalOpts::default()).stream(false);
946 let cmd = changes.setup_command("p4");
947 assert_eq!(args_of(&cmd), vec!["changes", "--nostream"]);
948 }
949
950 #[test]
951 fn all_options_order() {
952 let changes = Changes::new("p4", GlobalOpts::default())
953 .filter_client("eds_elm")
954 .include_restricted(true)
955 .include_integrated(true)
956 .long_output_full()
957 .limit(5)
958 .status(Status::Submitted)
959 .include_time(true)
960 .filter_user("edk");
961 #[cfg(not(feature = "lt2015_2"))]
962 let changes = changes.min_change_list("800");
963 #[cfg(not(feature = "lt2017_2"))]
964 let changes = changes.reverse_order(true);
965 #[cfg(not(feature = "lt2022_2"))]
966 let changes = changes.stream(true);
967 let cmd = changes.setup_command("p4");
968 let mut expected = vec!["changes", "-c", "eds_elm"];
969 #[cfg(not(feature = "lt2015_2"))]
970 expected.extend(["-e", "800"]);
971 expected.extend(["-f", "-i", "-l", "-m", "5"]);
972 #[cfg(not(feature = "lt2017_2"))]
973 expected.push("-r");
974 expected.extend(["-s", "submitted", "-t", "-u", "edk"]);
975 #[cfg(not(feature = "lt2022_2"))]
976 expected.push("--stream");
977 assert_eq!(args_of(&cmd), expected);
978 }
979
980 #[test]
981 fn long_output_full_preserves_other_options() {
982 let changes = Changes::new("p4", GlobalOpts::default())
983 .include_restricted(true)
984 .include_time(true)
985 .long_output_full();
986 let cmd = changes.setup_command("p4");
987 assert_eq!(args_of(&cmd), vec!["changes", "-f", "-l", "-t"]);
988 }
989}