master
cpp 1,050 lines 32.1 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WindowsUpdateTests.cpp
8
9 Abstract:
10
11 This file contains unit tests for WindowsUpdateIntegration.cpp.
12 These tests use mock COM objects injected via the WindowsUpdateClassFactory
13 abstraction so no real Windows Update service calls are made.
14
15 --*/
16
17 #include "precomp.h"
18 #include "Common.h"
19 #include "WindowsUpdateIntegration.h"
20
21 using wsl::windows::common::WindowsUpdateClassFactory;
22 using wsl::windows::common::WindowsUpdateContext;
23 namespace WRL = Microsoft::WRL;
24
25 namespace {
26
27 // Stubs the 4 IDispatch pure virtual methods. All WUA interfaces derive from IDispatch.
28 #define STUB_IDISPATCH() \
29 STDMETHOD(GetTypeInfoCount)(UINT*) override \
30 { \
31 return E_NOTIMPL; \
32 } \
33 STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo**) override \
34 { \
35 return E_NOTIMPL; \
36 } \
37 STDMETHOD(GetIDsOfNames)(REFIID, LPOLESTR*, UINT, LCID, DISPID*) override \
38 { \
39 return E_NOTIMPL; \
40 } \
41 STDMETHOD(Invoke)(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*) override \
42 { \
43 return E_NOTIMPL; \
44 }
45
46 // ---------------------------------------------------------------------------
47 // MockUpdateCollection — implements IUpdateCollection
48 // ---------------------------------------------------------------------------
49 struct MockUpdateCollection : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, IUpdateCollection>
50 {
51 STUB_IDISPATCH()
52
53 std::vector<wil::com_ptr<IUpdate>> items;
54 LONG addCallCount = 0;
55
56 STDMETHOD(get_Count)(LONG* retval) override
57 {
58 *retval = static_cast<LONG>(items.size());
59 return S_OK;
60 }
61
62 STDMETHOD(get_Item)(LONG index, IUpdate** retval) override
63 {
64 if (index < 0 || static_cast<size_t>(index) >= items.size())
65 {
66 return E_INVALIDARG;
67 }
68 *retval = items[index].get();
69 (*retval)->AddRef();
70 return S_OK;
71 }
72
73 STDMETHOD(Add)(IUpdate* value, LONG*) override
74 {
75 wil::com_ptr<IUpdate> u = value;
76 items.push_back(std::move(u));
77 ++addCallCount;
78 return S_OK;
79 }
80
81 STDMETHOD(Clear)() override
82 {
83 items.clear();
84 return S_OK;
85 }
86
87 STDMETHOD(put_Item)(LONG, IUpdate*) override
88 {
89 return E_NOTIMPL;
90 }
91 STDMETHOD(get__NewEnum)(IUnknown**) override
92 {
93 return E_NOTIMPL;
94 }
95 STDMETHOD(get_ReadOnly)(VARIANT_BOOL*) override
96 {
97 return E_NOTIMPL;
98 }
99 STDMETHOD(Copy)(IUpdateCollection**) override
100 {
101 return E_NOTIMPL;
102 }
103 STDMETHOD(Insert)(LONG, IUpdate*) override
104 {
105 return E_NOTIMPL;
106 }
107 STDMETHOD(RemoveAt)(LONG) override
108 {
109 return E_NOTIMPL;
110 }
111 };
112
113 // ---------------------------------------------------------------------------
114 // MockUpdate — implements IUpdate
115 // ---------------------------------------------------------------------------
116 struct MockUpdate : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, IUpdate>
117 {
118 STUB_IDISPATCH()
119
120 VARIANT_BOOL isDownloaded = VARIANT_FALSE;
121
122 STDMETHOD(get_IsDownloaded)(VARIANT_BOOL* retval) override
123 {
124 *retval = isDownloaded;
125 return S_OK;
126 }
127
128 STDMETHOD(get_Title)(BSTR*) override
129 {
130 return E_NOTIMPL;
131 }
132 STDMETHOD(get_AutoSelectOnWebSites)(VARIANT_BOOL*) override
133 {
134 return E_NOTIMPL;
135 }
136 STDMETHOD(get_BundledUpdates)(IUpdateCollection**) override
137 {
138 return E_NOTIMPL;
139 }
140 STDMETHOD(get_CanRequireSource)(VARIANT_BOOL*) override
141 {
142 return E_NOTIMPL;
143 }
144 STDMETHOD(get_Categories)(ICategoryCollection**) override
145 {
146 return E_NOTIMPL;
147 }
148 STDMETHOD(get_Deadline)(VARIANT*) override
149 {
150 return E_NOTIMPL;
151 }
152 STDMETHOD(get_DeltaCompressedContentAvailable)(VARIANT_BOOL*) override
153 {
154 return E_NOTIMPL;
155 }
156 STDMETHOD(get_DeltaCompressedContentPreferred)(VARIANT_BOOL*) override
157 {
158 return E_NOTIMPL;
159 }
160 STDMETHOD(get_Description)(BSTR*) override
161 {
162 return E_NOTIMPL;
163 }
164 STDMETHOD(get_EulaAccepted)(VARIANT_BOOL*) override
165 {
166 return E_NOTIMPL;
167 }
168 STDMETHOD(get_EulaText)(BSTR*) override
169 {
170 return E_NOTIMPL;
171 }
172 STDMETHOD(get_HandlerID)(BSTR*) override
173 {
174 return E_NOTIMPL;
175 }
176 STDMETHOD(get_Identity)(IUpdateIdentity**) override
177 {
178 return E_NOTIMPL;
179 }
180 STDMETHOD(get_Image)(IImageInformation**) override
181 {
182 return E_NOTIMPL;
183 }
184 STDMETHOD(get_InstallationBehavior)(IInstallationBehavior**) override
185 {
186 return E_NOTIMPL;
187 }
188 STDMETHOD(get_IsBeta)(VARIANT_BOOL*) override
189 {
190 return E_NOTIMPL;
191 }
192 STDMETHOD(get_IsHidden)(VARIANT_BOOL*) override
193 {
194 return E_NOTIMPL;
195 }
196 STDMETHOD(put_IsHidden)(VARIANT_BOOL) override
197 {
198 return E_NOTIMPL;
199 }
200 STDMETHOD(get_IsInstalled)(VARIANT_BOOL*) override
201 {
202 return E_NOTIMPL;
203 }
204 STDMETHOD(get_IsMandatory)(VARIANT_BOOL*) override
205 {
206 return E_NOTIMPL;
207 }
208 STDMETHOD(get_IsUninstallable)(VARIANT_BOOL*) override
209 {
210 return E_NOTIMPL;
211 }
212 STDMETHOD(get_Languages)(IStringCollection**) override
213 {
214 return E_NOTIMPL;
215 }
216 STDMETHOD(get_LastDeploymentChangeTime)(DATE*) override
217 {
218 return E_NOTIMPL;
219 }
220 STDMETHOD(get_MaxDownloadSize)(DECIMAL*) override
221 {
222 return E_NOTIMPL;
223 }
224 STDMETHOD(get_MinDownloadSize)(DECIMAL*) override
225 {
226 return E_NOTIMPL;
227 }
228 STDMETHOD(get_MoreInfoUrls)(IStringCollection**) override
229 {
230 return E_NOTIMPL;
231 }
232 STDMETHOD(get_MsrcSeverity)(BSTR*) override
233 {
234 return E_NOTIMPL;
235 }
236 STDMETHOD(get_RecommendedCpuSpeed)(LONG*) override
237 {
238 return E_NOTIMPL;
239 }
240 STDMETHOD(get_RecommendedHardDiskSpace)(LONG*) override
241 {
242 return E_NOTIMPL;
243 }
244 STDMETHOD(get_RecommendedMemory)(LONG*) override
245 {
246 return E_NOTIMPL;
247 }
248 STDMETHOD(get_ReleaseNotes)(BSTR*) override
249 {
250 return E_NOTIMPL;
251 }
252 STDMETHOD(get_SecurityBulletinIDs)(IStringCollection**) override
253 {
254 return E_NOTIMPL;
255 }
256 STDMETHOD(get_SupersededUpdateIDs)(IStringCollection**) override
257 {
258 return E_NOTIMPL;
259 }
260 STDMETHOD(get_SupportUrl)(BSTR*) override
261 {
262 return E_NOTIMPL;
263 }
264 STDMETHOD(get_Type)(UpdateType*) override
265 {
266 return E_NOTIMPL;
267 }
268 STDMETHOD(get_UninstallationNotes)(BSTR*) override
269 {
270 return E_NOTIMPL;
271 }
272 STDMETHOD(get_UninstallationBehavior)(IInstallationBehavior**) override
273 {
274 return E_NOTIMPL;
275 }
276 STDMETHOD(get_UninstallationSteps)(IStringCollection**) override
277 {
278 return E_NOTIMPL;
279 }
280 STDMETHOD(get_KBArticleIDs)(IStringCollection**) override
281 {
282 return E_NOTIMPL;
283 }
284 STDMETHOD(AcceptEula)() override
285 {
286 return E_NOTIMPL;
287 }
288 STDMETHOD(get_DeploymentAction)(DeploymentAction*) override
289 {
290 return E_NOTIMPL;
291 }
292 STDMETHOD(CopyFromCache)(BSTR, VARIANT_BOOL) override
293 {
294 return E_NOTIMPL;
295 }
296 STDMETHOD(get_DownloadPriority)(DownloadPriority*) override
297 {
298 return E_NOTIMPL;
299 }
300 STDMETHOD(get_DownloadContents)(IUpdateDownloadContentCollection**) override
301 {
302 return E_NOTIMPL;
303 }
304 };
305
306 // ---------------------------------------------------------------------------
307 // MockSearchResult — implements ISearchResult
308 // ---------------------------------------------------------------------------
309 struct MockSearchResult : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, ISearchResult>
310 {
311 STUB_IDISPATCH()
312
313 OperationResultCode resultCode = OperationResultCode::orcSucceeded;
314 wil::com_ptr_nothrow<MockUpdateCollection> updates = wil::MakeOrThrow<MockUpdateCollection>();
315
316 STDMETHOD(get_ResultCode)(OperationResultCode* retval) override
317 {
318 *retval = resultCode;
319 return S_OK;
320 }
321
322 STDMETHOD(get_Updates)(IUpdateCollection** retval) override
323 {
324 return updates.query_to(retval);
325 }
326
327 STDMETHOD(get_RootCategories)(ICategoryCollection**) override
328 {
329 return E_NOTIMPL;
330 }
331 STDMETHOD(get_Warnings)(IUpdateExceptionCollection**) override
332 {
333 return E_NOTIMPL;
334 }
335 };
336
337 // ---------------------------------------------------------------------------
338 // MockUpdateSearcher — implements IUpdateSearcher
339 // ---------------------------------------------------------------------------
340 struct MockUpdateSearcher : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, IUpdateSearcher>
341 {
342 STUB_IDISPATCH()
343
344 wil::com_ptr_nothrow<MockSearchResult> searchResult = wil::MakeOrThrow<MockSearchResult>();
345
346 STDMETHOD(Search)(BSTR, ISearchResult** retval) override
347 {
348 return searchResult.query_to(retval);
349 }
350
351 STDMETHOD(get_CanAutomaticallyUpgradeService)(VARIANT_BOOL*) override
352 {
353 return E_NOTIMPL;
354 }
355 STDMETHOD(put_CanAutomaticallyUpgradeService)(VARIANT_BOOL) override
356 {
357 return E_NOTIMPL;
358 }
359 STDMETHOD(get_ClientApplicationID)(BSTR*) override
360 {
361 return E_NOTIMPL;
362 }
363 STDMETHOD(put_ClientApplicationID)(BSTR) override
364 {
365 return E_NOTIMPL;
366 }
367 STDMETHOD(get_IncludePotentiallySupersededUpdates)(VARIANT_BOOL*) override
368 {
369 return E_NOTIMPL;
370 }
371 STDMETHOD(put_IncludePotentiallySupersededUpdates)(VARIANT_BOOL) override
372 {
373 return E_NOTIMPL;
374 }
375 STDMETHOD(get_ServerSelection)(ServerSelection*) override
376 {
377 return E_NOTIMPL;
378 }
379 STDMETHOD(put_ServerSelection)(ServerSelection) override
380 {
381 return E_NOTIMPL;
382 }
383 STDMETHOD(BeginSearch)(BSTR, IUnknown*, VARIANT, ISearchJob**) override
384 {
385 return E_NOTIMPL;
386 }
387 STDMETHOD(EndSearch)(ISearchJob*, ISearchResult**) override
388 {
389 return E_NOTIMPL;
390 }
391 STDMETHOD(EscapeString)(BSTR, BSTR*) override
392 {
393 return E_NOTIMPL;
394 }
395 STDMETHOD(QueryHistory)(LONG, LONG, IUpdateHistoryEntryCollection**) override
396 {
397 return E_NOTIMPL;
398 }
399 STDMETHOD(get_Online)(VARIANT_BOOL*) override
400 {
401 return E_NOTIMPL;
402 }
403 STDMETHOD(put_Online)(VARIANT_BOOL) override
404 {
405 return E_NOTIMPL;
406 }
407 STDMETHOD(GetTotalHistoryCount)(LONG*) override
408 {
409 return E_NOTIMPL;
410 }
411 STDMETHOD(get_ServiceID)(BSTR*) override
412 {
413 return E_NOTIMPL;
414 }
415 STDMETHOD(put_ServiceID)(BSTR) override
416 {
417 return E_NOTIMPL;
418 }
419 };
420
421 // ---------------------------------------------------------------------------
422 // MockDownloadJob — implements IDownloadJob
423 // ---------------------------------------------------------------------------
424 struct MockDownloadJob : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, IDownloadJob>
425 {
426 STUB_IDISPATCH()
427
428 STDMETHOD(CleanUp)() override
429 {
430 return S_OK;
431 }
432 STDMETHOD(get_AsyncState)(VARIANT*) override
433 {
434 return E_NOTIMPL;
435 }
436 STDMETHOD(get_IsCompleted)(VARIANT_BOOL*) override
437 {
438 return E_NOTIMPL;
439 }
440 STDMETHOD(get_Updates)(IUpdateCollection**) override
441 {
442 return E_NOTIMPL;
443 }
444 STDMETHOD(GetProgress)(IDownloadProgress**) override
445 {
446 return E_NOTIMPL;
447 }
448 STDMETHOD(RequestAbort)() override
449 {
450 return E_NOTIMPL;
451 }
452 };
453
454 // ---------------------------------------------------------------------------
455 // MockDownloadResult — implements IDownloadResult
456 // ---------------------------------------------------------------------------
457 struct MockDownloadResult : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, IDownloadResult>
458 {
459 STUB_IDISPATCH()
460
461 HRESULT downloadHResult = S_OK;
462
463 STDMETHOD(get_HResult)(HRESULT* retval) override
464 {
465 *retval = downloadHResult;
466 return S_OK;
467 }
468
469 STDMETHOD(get_ResultCode)(OperationResultCode*) override
470 {
471 return E_NOTIMPL;
472 }
473 STDMETHOD(GetUpdateResult)(LONG, IUpdateDownloadResult**) override
474 {
475 return E_NOTIMPL;
476 }
477 };
478
479 // ---------------------------------------------------------------------------
480 // MockUpdateDownloader — implements IUpdateDownloader
481 // ---------------------------------------------------------------------------
482 struct MockUpdateDownloader : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, IUpdateDownloader>
483 {
484 STUB_IDISPATCH()
485
486 wil::com_ptr_nothrow<MockDownloadResult> downloadResult = wil::MakeOrThrow<MockDownloadResult>();
487 wil::com_ptr<IUpdateCollection> capturedCollection;
488 bool beginDownloadCalled = false;
489
490 STDMETHOD(put_Updates)(IUpdateCollection* value) override
491 {
492 capturedCollection = value;
493 return S_OK;
494 }
495
496 STDMETHOD(BeginDownload)(IUnknown*, IUnknown* completedCallback, VARIANT, IDownloadJob** retval) override
497 {
498 beginDownloadCalled = true;
499 *retval = wil::MakeOrThrow<MockDownloadJob>().Detach();
500 if (completedCallback)
501 {
502 wil::com_ptr<IDownloadCompletedCallback> cb;
503 if (SUCCEEDED(completedCallback->QueryInterface(IID_PPV_ARGS(&cb))))
504 {
505 cb->Invoke(*retval, nullptr);
506 }
507 }
508 return S_OK;
509 }
510
511 STDMETHOD(EndDownload)(IDownloadJob*, IDownloadResult** retval) override
512 {
513 return downloadResult.query_to(retval);
514 }
515
516 STDMETHOD(get_ClientApplicationID)(BSTR*) override
517 {
518 return E_NOTIMPL;
519 }
520 STDMETHOD(put_ClientApplicationID)(BSTR) override
521 {
522 return E_NOTIMPL;
523 }
524 STDMETHOD(get_IsForced)(VARIANT_BOOL*) override
525 {
526 return E_NOTIMPL;
527 }
528 STDMETHOD(put_IsForced)(VARIANT_BOOL) override
529 {
530 return E_NOTIMPL;
531 }
532 STDMETHOD(get_Priority)(DownloadPriority*) override
533 {
534 return E_NOTIMPL;
535 }
536 STDMETHOD(put_Priority)(DownloadPriority) override
537 {
538 return E_NOTIMPL;
539 }
540 STDMETHOD(get_Updates)(IUpdateCollection**) override
541 {
542 return E_NOTIMPL;
543 }
544 STDMETHOD(Download)(IDownloadResult**) override
545 {
546 return E_NOTIMPL;
547 }
548 };
549
550 // ---------------------------------------------------------------------------
551 // MockInstallationJob — implements IInstallationJob
552 // ---------------------------------------------------------------------------
553 struct MockInstallationJob : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, IInstallationJob>
554 {
555 STUB_IDISPATCH()
556
557 STDMETHOD(CleanUp)() override
558 {
559 return S_OK;
560 }
561 STDMETHOD(get_AsyncState)(VARIANT*) override
562 {
563 return E_NOTIMPL;
564 }
565 STDMETHOD(get_IsCompleted)(VARIANT_BOOL*) override
566 {
567 return E_NOTIMPL;
568 }
569 STDMETHOD(get_Updates)(IUpdateCollection**) override
570 {
571 return E_NOTIMPL;
572 }
573 STDMETHOD(GetProgress)(IInstallationProgress**) override
574 {
575 return E_NOTIMPL;
576 }
577 STDMETHOD(RequestAbort)() override
578 {
579 return E_NOTIMPL;
580 }
581 };
582
583 // ---------------------------------------------------------------------------
584 // MockInstallationResult — implements IInstallationResult
585 // ---------------------------------------------------------------------------
586 struct MockInstallationResult : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, IInstallationResult>
587 {
588 STUB_IDISPATCH()
589
590 HRESULT installHResult = S_OK;
591
592 STDMETHOD(get_HResult)(HRESULT* retval) override
593 {
594 *retval = installHResult;
595 return S_OK;
596 }
597
598 STDMETHOD(get_RebootRequired)(VARIANT_BOOL*) override
599 {
600 return E_NOTIMPL;
601 }
602 STDMETHOD(get_ResultCode)(OperationResultCode*) override
603 {
604 return E_NOTIMPL;
605 }
606 STDMETHOD(GetUpdateResult)(LONG, IUpdateInstallationResult**) override
607 {
608 return E_NOTIMPL;
609 }
610 };
611
612 // ---------------------------------------------------------------------------
613 // MockUpdateInstaller — implements IUpdateInstaller
614 // ---------------------------------------------------------------------------
615 struct MockUpdateInstaller : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, IUpdateInstaller>
616 {
617 STUB_IDISPATCH()
618
619 wil::com_ptr_nothrow<MockInstallationResult> installResult = wil::MakeOrThrow<MockInstallationResult>();
620 wil::com_ptr<IUpdateCollection> capturedCollection;
621 bool beginInstallCalled = false;
622
623 STDMETHOD(put_Updates)(IUpdateCollection* value) override
624 {
625 capturedCollection = value;
626 return S_OK;
627 }
628
629 STDMETHOD(BeginInstall)(IUnknown*, IUnknown* completedCallback, VARIANT, IInstallationJob** retval) override
630 {
631 beginInstallCalled = true;
632 *retval = wil::MakeOrThrow<MockInstallationJob>().Detach();
633 if (completedCallback)
634 {
635 wil::com_ptr<IInstallationCompletedCallback> cb;
636 if (SUCCEEDED(completedCallback->QueryInterface(IID_PPV_ARGS(&cb))))
637 {
638 cb->Invoke(*retval, nullptr);
639 }
640 }
641 return S_OK;
642 }
643
644 STDMETHOD(EndInstall)(IInstallationJob*, IInstallationResult** retval) override
645 {
646 return installResult.query_to(retval);
647 }
648
649 STDMETHOD(get_ClientApplicationID)(BSTR*) override
650 {
651 return E_NOTIMPL;
652 }
653 STDMETHOD(put_ClientApplicationID)(BSTR) override
654 {
655 return E_NOTIMPL;
656 }
657 STDMETHOD(get_IsForced)(VARIANT_BOOL*) override
658 {
659 return E_NOTIMPL;
660 }
661 STDMETHOD(put_IsForced)(VARIANT_BOOL) override
662 {
663 return E_NOTIMPL;
664 }
665 STDMETHOD(get_ParentHwnd)(HWND*) override
666 {
667 return E_NOTIMPL;
668 }
669 STDMETHOD(put_ParentHwnd)(HWND) override
670 {
671 return E_NOTIMPL;
672 }
673 STDMETHOD(put_ParentWindow)(IUnknown*) override
674 {
675 return E_NOTIMPL;
676 }
677 STDMETHOD(get_ParentWindow)(IUnknown**) override
678 {
679 return E_NOTIMPL;
680 }
681 STDMETHOD(get_Updates)(IUpdateCollection**) override
682 {
683 return E_NOTIMPL;
684 }
685 STDMETHOD(BeginUninstall)(IUnknown*, IUnknown*, VARIANT, IInstallationJob**) override
686 {
687 return E_NOTIMPL;
688 }
689 STDMETHOD(EndUninstall)(IInstallationJob*, IInstallationResult**) override
690 {
691 return E_NOTIMPL;
692 }
693 STDMETHOD(Install)(IInstallationResult**) override
694 {
695 return E_NOTIMPL;
696 }
697 STDMETHOD(RunWizard)(BSTR, IInstallationResult**) override
698 {
699 return E_NOTIMPL;
700 }
701 STDMETHOD(get_IsBusy)(VARIANT_BOOL*) override
702 {
703 return E_NOTIMPL;
704 }
705 STDMETHOD(Uninstall)(IInstallationResult**) override
706 {
707 return E_NOTIMPL;
708 }
709 STDMETHOD(get_AllowSourcePrompts)(VARIANT_BOOL*) override
710 {
711 return E_NOTIMPL;
712 }
713 STDMETHOD(put_AllowSourcePrompts)(VARIANT_BOOL) override
714 {
715 return E_NOTIMPL;
716 }
717 STDMETHOD(get_RebootRequiredBeforeInstallation)(VARIANT_BOOL*) override
718 {
719 return E_NOTIMPL;
720 }
721 };
722
723 // ---------------------------------------------------------------------------
724 // MockUpdateSession — implements IUpdateSession
725 // ---------------------------------------------------------------------------
726 struct MockUpdateSession : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom>, IUpdateSession>
727 {
728 STUB_IDISPATCH()
729
730 wil::com_ptr_nothrow<MockUpdateSearcher> searcher = wil::MakeOrThrow<MockUpdateSearcher>();
731 wil::com_ptr_nothrow<MockUpdateDownloader> downloader = wil::MakeOrThrow<MockUpdateDownloader>();
732 wil::com_ptr_nothrow<MockUpdateInstaller> installer = wil::MakeOrThrow<MockUpdateInstaller>();
733
734 STDMETHOD(put_ClientApplicationID)(BSTR) override
735 {
736 return S_OK;
737 }
738
739 STDMETHOD(CreateUpdateSearcher)(IUpdateSearcher** retval) override
740 {
741 return searcher.query_to(retval);
742 }
743
744 STDMETHOD(CreateUpdateDownloader)(IUpdateDownloader** retval) override
745 {
746 return downloader.query_to(retval);
747 }
748
749 STDMETHOD(CreateUpdateInstaller)(IUpdateInstaller** retval) override
750 {
751 return installer.query_to(retval);
752 }
753
754 STDMETHOD(get_ClientApplicationID)(BSTR*) override
755 {
756 return E_NOTIMPL;
757 }
758 STDMETHOD(get_ReadOnly)(VARIANT_BOOL*) override
759 {
760 return E_NOTIMPL;
761 }
762 STDMETHOD(get_WebProxy)(IWebProxy**) override
763 {
764 return E_NOTIMPL;
765 }
766 STDMETHOD(put_WebProxy)(IWebProxy*) override
767 {
768 return E_NOTIMPL;
769 }
770 };
771
772 // ---------------------------------------------------------------------------
773 // MockWindowsUpdateClassFactory
774 // ---------------------------------------------------------------------------
775 struct MockWindowsUpdateClassFactory : public WindowsUpdateClassFactory
776 {
777 wil::com_ptr<MockUpdateSession> session = wil::MakeOrThrow<MockUpdateSession>();
778 // Tracks the most-recently created collection (used as toDownload in DownloadUpdates).
779 mutable wil::com_ptr<MockUpdateCollection> lastCreatedCollection;
780
781 wil::com_ptr<IUpdateSession> CreateUpdateSession() const override
782 {
783 return session.query<IUpdateSession>();
784 }
785
786 wil::com_ptr<IUpdateCollection> CreateUpdateCollection() const override
787 {
788 auto col = wil::MakeOrThrow<MockUpdateCollection>();
789 lastCreatedCollection = col;
790 wil::com_ptr<IUpdateCollection> result;
791 col.CopyTo(IID_IUpdateCollection, result.put_void());
792 return result;
793 }
794 };
795
796 // Captures the HRESULT thrown by a wil::ResultException, or S_OK if no exception.
797 static HRESULT CaptureHResult(const std::function<void()>& fn)
798 {
799 try
800 {
801 fn();
802 return S_OK;
803 }
804 catch (const wil::ResultException& e)
805 {
806 return e.GetErrorCode();
807 }
808 }
809
810 // Adds a MockUpdate with the given isDownloaded state to the search result collection.
811 static wil::com_ptr<MockUpdate> AddMockUpdate(MockUpdateCollection* col, VARIANT_BOOL isDownloaded)
812 {
813 auto u = wil::MakeOrThrow<MockUpdate>();
814 u->isDownloaded = isDownloaded;
815 wil::com_ptr<IUpdate> update;
816 u.CopyTo(IID_IUpdate, update.put_void());
817 col->items.push_back(std::move(update));
818 return u;
819 }
820
821 } // namespace
822
823 class WindowsUpdateTests
824 {
825 WSL_TEST_CLASS(WindowsUpdateTests)
826
827 // -----------------------------------------------------------------------
828 // SearchForUpdates tests
829 // -----------------------------------------------------------------------
830
831 TEST_METHOD(SearchForUpdates_NoUpdates)
832 {
833 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
834 WindowsUpdateContext ctx(std::move(factory));
835
836 VERIFY_ARE_EQUAL(0u, ctx.SearchForUpdates());
837 VERIFY_ARE_EQUAL(0u, ctx.GetUpdateCount());
838 }
839
840 TEST_METHOD(SearchForUpdates_UpdatesFound)
841 {
842 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
843 auto* fp = factory.get();
844 auto* col = fp->session->searcher->searchResult->updates.get();
845 AddMockUpdate(col, VARIANT_FALSE);
846 AddMockUpdate(col, VARIANT_FALSE);
847 AddMockUpdate(col, VARIANT_FALSE);
848
849 WindowsUpdateContext ctx(std::move(factory));
850
851 VERIFY_ARE_EQUAL(3u, ctx.SearchForUpdates());
852 VERIFY_ARE_EQUAL(3u, ctx.GetUpdateCount());
853 }
854
855 TEST_METHOD(SearchForUpdates_SucceededWithErrors)
856 {
857 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
858 auto* fp = factory.get();
859 fp->session->searcher->searchResult->resultCode = OperationResultCode::orcSucceededWithErrors;
860 AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_FALSE);
861
862 WindowsUpdateContext ctx(std::move(factory));
863
864 // orcSucceededWithErrors must succeed — the update count is still returned.
865 VERIFY_ARE_EQUAL(1u, ctx.SearchForUpdates());
866 }
867
868 TEST_METHOD(SearchForUpdates_Failed)
869 {
870 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
871 factory->session->searcher->searchResult->resultCode = OperationResultCode::orcFailed;
872
873 WindowsUpdateContext ctx(std::move(factory));
874
875 VERIFY_ARE_EQUAL(WSLC_E_WU_SEARCH_FAILED, CaptureHResult([&] { ctx.SearchForUpdates(); }));
876 }
877
878 // -----------------------------------------------------------------------
879 // DownloadUpdates tests
880 // -----------------------------------------------------------------------
881
882 TEST_METHOD(DownloadUpdates_AllAlreadyDownloaded)
883 {
884 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
885 auto* fp = factory.get();
886 auto* col = fp->session->searcher->searchResult->updates.get();
887 AddMockUpdate(col, VARIANT_TRUE);
888 AddMockUpdate(col, VARIANT_TRUE);
889
890 WindowsUpdateContext ctx(std::move(factory));
891 ctx.SearchForUpdates();
892
893 std::vector<uint32_t> progressCalls;
894 ctx.DownloadUpdates([&](uint32_t p) { progressCalls.push_back(p); });
895
896 // All updates were already downloaded: BeginDownload must not be called,
897 // and progress(100) must be reported to signal completion.
898 VERIFY_IS_FALSE(fp->session->downloader->beginDownloadCalled);
899 VERIFY_ARE_EQUAL(0L, fp->lastCreatedCollection->addCallCount);
900 VERIFY_ARE_EQUAL(1u, progressCalls.size());
901 VERIFY_ARE_EQUAL(100u, progressCalls[0]);
902 }
903
904 TEST_METHOD(DownloadUpdates_SomeNeedDownloading)
905 {
906 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
907 auto* fp = factory.get();
908 auto* col = fp->session->searcher->searchResult->updates.get();
909 AddMockUpdate(col, VARIANT_TRUE); // already downloaded — skip
910 AddMockUpdate(col, VARIANT_FALSE); // needs download
911 AddMockUpdate(col, VARIANT_FALSE); // needs download
912
913 WindowsUpdateContext ctx(std::move(factory));
914 ctx.SearchForUpdates();
915 ctx.DownloadUpdates();
916
917 VERIFY_IS_TRUE(fp->session->downloader->beginDownloadCalled);
918 VERIFY_ARE_EQUAL(2L, fp->lastCreatedCollection->addCallCount);
919 }
920
921 TEST_METHOD(DownloadUpdates_DownloadFails)
922 {
923 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
924 auto* fp = factory.get();
925 AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_FALSE);
926 fp->session->downloader->downloadResult->downloadHResult = E_FAIL;
927
928 WindowsUpdateContext ctx(std::move(factory));
929 ctx.SearchForUpdates();
930
931 VERIFY_ARE_EQUAL(E_FAIL, CaptureHResult([&] { ctx.DownloadUpdates(); }));
932 }
933
934 // -----------------------------------------------------------------------
935 // InstallUpdates tests
936 // -----------------------------------------------------------------------
937
938 TEST_METHOD(InstallUpdates_Success)
939 {
940 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
941 auto* fp = factory.get();
942 AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_TRUE);
943
944 WindowsUpdateContext ctx(std::move(factory));
945 ctx.SearchForUpdates();
946
947 // Should not throw.
948 ctx.InstallUpdates();
949
950 VERIFY_IS_TRUE(fp->session->installer->beginInstallCalled);
951 VERIFY_IS_NOT_NULL(fp->session->installer->capturedCollection.get());
952 }
953
954 TEST_METHOD(InstallUpdates_Fails)
955 {
956 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
957 auto* fp = factory.get();
958 AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_TRUE);
959 fp->session->installer->installResult->installHResult = E_ACCESSDENIED;
960
961 WindowsUpdateContext ctx(std::move(factory));
962 ctx.SearchForUpdates();
963
964 VERIFY_ARE_EQUAL(E_ACCESSDENIED, CaptureHResult([&] { ctx.InstallUpdates(); }));
965 }
966
967 // -----------------------------------------------------------------------
968 // RunUpdateFlow tests
969 // -----------------------------------------------------------------------
970
971 TEST_METHOD(RunUpdateFlow_NoUpdates_ProgressGoesTo100)
972 {
973 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
974 auto* fp = factory.get();
975
976 WindowsUpdateContext ctx(std::move(factory));
977
978 std::vector<uint32_t> progressCalls;
979 ctx.RunUpdateFlow(WindowsUpdateContext::UpdateOptions::None, [&](uint32_t p) { progressCalls.push_back(p); });
980
981 // progress(0) at the start, progress(100) because there are no updates.
982 VERIFY_ARE_EQUAL(2u, progressCalls.size());
983 VERIFY_ARE_EQUAL(0u, progressCalls[0]);
984 VERIFY_ARE_EQUAL(100u, progressCalls[1]);
985
986 // No download or install should have been triggered.
987 VERIFY_IS_FALSE(fp->session->downloader->beginDownloadCalled);
988 VERIFY_IS_FALSE(fp->session->installer->beginInstallCalled);
989 }
990
991 TEST_METHOD(RunUpdateFlow_UpdatesFound_DownloadThenInstall)
992 {
993 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
994 auto* fp = factory.get();
995 AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_FALSE);
996
997 WindowsUpdateContext ctx(std::move(factory));
998
999 std::vector<uint32_t> progressCalls;
1000 ctx.RunUpdateFlow(WindowsUpdateContext::UpdateOptions::None, [&](uint32_t p) { progressCalls.push_back(p); });
1001
1002 // progress(0) is emitted at the start.
1003 VERIFY_IS_FALSE(progressCalls.empty());
1004 VERIFY_ARE_EQUAL(0u, progressCalls[0]);
1005
1006 // Both download and install phases ran.
1007 VERIFY_IS_TRUE(fp->session->downloader->beginDownloadCalled);
1008 VERIFY_IS_TRUE(fp->session->installer->beginInstallCalled);
1009 }
1010
1011 TEST_METHOD(RunUpdateFlow_DownloadProgressScaling)
1012 {
1013 // Verify that download progress values are scaled into the 0–DownloadProgressPercent range.
1014 // The download lambda is: progress((percent * DownloadProgressPercent) / 100)
1015 // For percent=50: expected outer value = (50 * 70) / 100 = 35
1016 VERIFY_ARE_EQUAL(35u, (50u * WindowsUpdateContext::DownloadProgressPercent) / 100u);
1017
1018 // Verify that install progress values are offset and scaled into the remaining range.
1019 // The install lambda is: progress(DownloadProgressPercent + (percent * InstallProgressPercent) / 100)
1020 // For percent=100: expected outer value = 70 + (100 * 30) / 100 = 100
1021 VERIFY_ARE_EQUAL(100u, WindowsUpdateContext::DownloadProgressPercent + (100u * WindowsUpdateContext::InstallProgressPercent) / 100u);
1022 }
1023
1024 TEST_METHOD(RunUpdateFlow_DownloadFails_Propagates)
1025 {
1026 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
1027 auto* fp = factory.get();
1028 AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_FALSE);
1029 fp->session->downloader->downloadResult->downloadHResult = E_FAIL;
1030
1031 WindowsUpdateContext ctx(std::move(factory));
1032
1033 VERIFY_ARE_EQUAL(E_FAIL, CaptureHResult([&] { ctx.RunUpdateFlow(WindowsUpdateContext::UpdateOptions::None); }));
1034
1035 // Install should not have been called after download failure.
1036 VERIFY_IS_FALSE(fp->session->installer->beginInstallCalled);
1037 }
1038
1039 TEST_METHOD(RunUpdateFlow_NoProgress_DoesNotCrash)
1040 {
1041 // Verifies that passing no progress callback does not crash.
1042 auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
1043 AddMockUpdate(factory->session->searcher->searchResult->updates.get(), VARIANT_TRUE);
1044
1045 WindowsUpdateContext ctx(std::move(factory));
1046
1047 // Should complete without crashing even with no progress callback.
1048 ctx.RunUpdateFlow(WindowsUpdateContext::UpdateOptions::None);
1049 }
1050 };