Commit deeface146800bfe8b91986ccd98464cac833236

Authored by Jay Berkenbilt
1 parent cfafac8d

Add automated test for shell wildcard expansion

Wildcard expansion is different in Windows from non-Windows and
sometimes requires special link options to work. Add tests that fail
if we link incorrectly.
.github/workflows/main.yml
... ... @@ -41,7 +41,6 @@ jobs:
41 41 name: distribution
42 42 path: distribution
43 43 Windows:
44   - # If updating this, see note in TODO about MSVC wildcard expansion.
45 44 runs-on: windows-2019
46 45 needs: Distfiles
47 46 strategy:
... ...
... ... @@ -4,8 +4,7 @@ Candidates for upcoming release
4 4 * Easy build/test
5 5 * #460: potential malware in fuzzer seed corpus
6 6 * Consider building workflow on a schedule to detect build rot. This
7   - may enable safe use of *-latest especially if Windows wildcard is
8   - testable.
  7 + should enable safe use of *-latest on runners.
9 8  
10 9 * Fuzz crashes
11 10 * See "New" below
... ... @@ -29,9 +28,6 @@ Candidates for upcoming release
29 28 because of changes in the build environment, library dependencies,
30 29 compiler upgrades, etc.
31 30  
32   -* Find a way to deal with MSVC wildcard expansion, even if it requires
33   - creating a separate step or adding code to build-windows.bat.
34   -
35 31 * See if we can work in Windows Build/External Libraries (below)
36 32  
37 33 * Remember to check work `qpdf` project for private issues
... ... @@ -220,34 +216,6 @@ Page splitting/merging
220 216  
221 217 * Form fields: should be similar to outlines.
222 218  
223   -MSVC Wildcard Expansion
224   -=======================
225   -
226   -(This section is referenced in azure_pipelines.yml and
227   -.github/workflows/main.yml.)
228   -
229   -The qpdf executable built with msvc is linked with setargv.obj or
230   -wsetargv.obj so that wildcard expansion works. It doesn't work exactly
231   -the way a UNIX system would work in that the program itself does the
232   -expansion (rather than the shell), which means that invoking qpdf.exe
233   -as built by msvc will expand "*.pdf" just as it will expand *.pdf. In
234   -some earlier versions, wildcard expansion didn't work with the msvc
235   -executable. The way to make this work appears to be different in some
236   -versions of MSVC than in others. As such, if upgrading MSVC or
237   -changing the build environment, the wildcard expansion behavior of the
238   -qpdf executable in Windows should be retested manually.
239   -
240   -Unfortunately, there is no automated test for wildcard expansion with
241   -MSVC because I can't figure out how to prevent qtest from expanding
242   -the wildcards before passing them in, and explicitly running "cmd /c
243   -..." from qtest doesn't seem to work in Azure Pipelines (haven't
244   -attempted in GitHub Actions), though I can make it work locally.
245   -
246   -Ideally, we should figure out a way to test this in CI by having a
247   -test that fails if wildcard expansion is broken. In the absence of
248   -this, it will be necessary to test the behavior manually in both mingw
249   -and msvc when run from cmd and from msys bash.
250   -
251 219 Performance
252 220 ===========
253 221  
... ...
qpdf/build.mk
... ... @@ -8,6 +8,7 @@ BINS_qpdf = \
8 8 test_pdf_doc_encoding \
9 9 test_pdf_unicode \
10 10 test_renumber \
  11 + test_shell_glob \
11 12 test_tokenizer \
12 13 test_unicode_filenames \
13 14 test_xref
... ... @@ -23,15 +24,14 @@ TC_SRCS_qpdf = $(wildcard libqpdf/*.cc) $(wildcard qpdf/*.cc)
23 24  
24 25 # -----
25 26  
26   -XCXXFLAGS_qpdf_qpdf := $(WINDOWS_WMAIN_COMPILE)
27   -XLDFLAGS_qpdf_qpdf := $(WINDOWS_WMAIN_LINK)
28   -XLINK_FLAGS_qpdf_qpdf := $(WINDOWS_WMAIN_XLINK_FLAGS)
29   -XCXXFLAGS_qpdf_test_unicode_filenames := $(WINDOWS_WMAIN_COMPILE)
30   -XLDFLAGS_qpdf_test_unicode_filenames := $(WINDOWS_WMAIN_LINK)
31   -XLINK_FLAGS_qpdf_test_unicode_filenames := $(WINDOWS_WMAIN_XLINK_FLAGS)
32   -XCXXFLAGS_qpdf_fix-qdf := $(WINDOWS_WMAIN_COMPILE)
33   -XLDFLAGS_qpdf_fix-qdf := $(WINDOWS_WMAIN_LINK)
34   -XLINK_FLAGS_qpdf_fix-qdf := $(WINDOWS_WMAIN_XLINK_FLAGS)
  27 +define use_wmain
  28 + XCXXFLAGS_qpdf_$(1) := $(WINDOWS_WMAIN_COMPILE)
  29 + XLDFLAGS_qpdf_$(1) := $(WINDOWS_WMAIN_LINK)
  30 + XLINK_FLAGS_qpdf_$(1) := $(WINDOWS_WMAIN_XLINK_FLAGS)
  31 +endef
  32 +
  33 +$(foreach B,qpdf test_unicode_filenames fix-qdf test_shell_glob,\
  34 + $(eval $(call use_wmain,$(B))))
35 35  
36 36 $(foreach B,$(BINS_qpdf),$(eval \
37 37 OBJS_$(B) = $(call src_to_obj,qpdf/$(B).cc)))
... ...
qpdf/qtest/qpdf.test
... ... @@ -191,6 +191,17 @@ foreach my $d (['auto-ü', 1], ['auto-öπ', 2])
191 191  
192 192 show_ntests();
193 193 # ----------
  194 +$td->notify("--- Windows shell globbing ---");
  195 +
  196 +$td->runtest("shell wildcard expansion",
  197 + {$td->COMMAND => "test_shell_glob 'good*.pdf'"},
  198 + {$td->STRING => "PASSED\n", $td->EXIT_STATUS => 0},
  199 + $td->NORMALIZE_NEWLINES);
  200 +
  201 +$n_tests += 1;
  202 +
  203 +show_ntests();
  204 +# ----------
194 205 $td->notify("--- Replace Input ---");
195 206 $n_tests += 8;
196 207  
... ...
qpdf/test_shell_glob.cc 0 → 100644
  1 +#include <iostream>
  2 +#include <cstring>
  3 +#include <qpdf/QUtil.hh>
  4 +
  5 +int realmain(int argc, char* argv[])
  6 +{
  7 + // In Windows, shell globbing is handled by the runtime, so
  8 + // passing '*' as argument results in wildcard expansion. In
  9 + // non-Windows, globbing is done by the shell, so passing '*'
  10 + // shows up as '*'. In Windows with MSVC, it is necessary to link
  11 + // a certain way for this to work. To test this, we invoke this
  12 + // program with a single quoted argument containing a shell glob
  13 + // expression. In Windows, we expect to see multiple arguments,
  14 + // none of which contain '*'. Otherwise, we expected to see the
  15 + // exact glob string that was passed in. The effectiveness of this
  16 + // test was exercised by manually breaking the link options for
  17 + // msvc and seeing that the test fails under that condition.
  18 +
  19 + bool found_star = false;
  20 + for (int i = 1; i < argc; ++i)
  21 + {
  22 + if (strchr(argv[i], '*') != nullptr)
  23 + {
  24 + found_star = true;
  25 + break;
  26 + }
  27 + }
  28 +#ifdef _WIN32
  29 + bool passed = ((! found_star) && (argc > 2));
  30 +#else
  31 + bool passed = (found_star && (argc == 2));
  32 +#endif
  33 + if (passed)
  34 + {
  35 + std::cout << "PASSED" << std::endl;
  36 + }
  37 + else
  38 + {
  39 + std::cout << "FAILED" << std::endl;
  40 + for (int i = 1; i < argc; ++i)
  41 + {
  42 + std::cout << argv[i] << std::endl;
  43 + }
  44 + }
  45 + return 0;
  46 +}
  47 +
  48 +
  49 +#ifdef WINDOWS_WMAIN
  50 +
  51 +extern "C"
  52 +int wmain(int argc, wchar_t* argv[])
  53 +{
  54 + return QUtil::call_main_from_wmain(argc, argv, realmain);
  55 +}
  56 +
  57 +#else
  58 +
  59 +int main(int argc, char* argv[])
  60 +{
  61 + return realmain(argc, argv);
  62 +}
  63 +
  64 +#endif
... ...