Skip to content

Commit 5dfb469

Browse files
committed
- add tests
1 parent b8fb2b2 commit 5dfb469

File tree

1 file changed

+110
-1
lines changed

1 file changed

+110
-1
lines changed

src/Controls/tests/Core.UnitTests/CommandTests.cs

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using System.Windows.Input;
5+
using Microsoft.Maui.Controls.Internals;
26
using Xunit;
37

48
namespace Microsoft.Maui.Controls.Core.UnitTests
@@ -251,5 +255,110 @@ public void ExecuteDoesNotRunIfValueTypeAndSetToNull()
251255
command.Execute(null); // "null is not a valid value for int"
252256
Assert.True(executions == 0, "the command should not have executed");
253257
}
258+
259+
[Theory]
260+
[InlineData(typeof(Button), true)]
261+
[InlineData(typeof(Button), false)]
262+
[InlineData(typeof(RefreshView), true)]
263+
[InlineData(typeof(RefreshView), false)]
264+
[InlineData(typeof(TextCell), true)]
265+
[InlineData(typeof(TextCell), false)]
266+
[InlineData(typeof(ImageButton), true)]
267+
[InlineData(typeof(ImageButton), false)]
268+
[InlineData(typeof(MenuItem), true)]
269+
[InlineData(typeof(MenuItem), false)]
270+
[InlineData(typeof(SearchBar), true)]
271+
[InlineData(typeof(SearchBar), false)]
272+
[InlineData(typeof(SearchHandler), true)]
273+
[InlineData(typeof(SearchHandler), false)]
274+
public async Task CommandsSubscribedToCanExecuteCollect(Type controlType, bool useWeakEventHandler)
275+
{
276+
// Create a view model with a Command
277+
ICommand command;
278+
279+
if (!useWeakEventHandler)
280+
command = new CommandWithoutWeakEventHandler();
281+
else
282+
command = new Command(() => { });
283+
284+
List<WeakReference> weakReferences = new List<WeakReference>();
285+
286+
// Create a button in a separate scope to ensure no references remain
287+
{
288+
var control = (BindableObject)Activator.CreateInstance(controlType);
289+
switch (control)
290+
{
291+
case Button b:
292+
b.Command = command;
293+
break;
294+
case RefreshView r:
295+
r.Command = command;
296+
break;
297+
case TextCell t:
298+
t.Command = command;
299+
break;
300+
case ImageButton i:
301+
i.Command = command;
302+
break;
303+
case MenuItem m:
304+
m.Command = command;
305+
break;
306+
case SearchBar s:
307+
s.SearchCommand = command;
308+
break;
309+
case SearchHandler sh:
310+
sh.Command = command;
311+
sh.ClearPlaceholderCommand = command;
312+
break;
313+
}
314+
315+
// Create a weak reference to the button
316+
weakReferences.Add(new WeakReference(control));
317+
318+
if (control is ICommandElement commandElement)
319+
{
320+
// Add weak references to the command and its cleanup tracker
321+
weakReferences.Add(new WeakReference(commandElement.CleanupTracker));
322+
weakReferences.Add(new WeakReference(commandElement.CleanupTracker.Proxy));
323+
}
324+
else if(control is SearchHandler searchHandler)
325+
{
326+
// Add weak references to the command and its cleanup tracker
327+
weakReferences.Add(new WeakReference(searchHandler.CommandSubscription));
328+
weakReferences.Add(new WeakReference(searchHandler.CommandSubscription.Proxy));
329+
weakReferences.Add(new WeakReference(searchHandler.ClearPlaceholderCommandSubscription));
330+
weakReferences.Add(new WeakReference(searchHandler.ClearPlaceholderCommandSubscription.Proxy));
331+
}
332+
333+
await TestHelpers.Collect();
334+
await TestHelpers.Collect();
335+
336+
// Make sure everything is still alive if the button is still in scope
337+
// We need to reference the button here again to keep it alive
338+
// awaiting a Task appears to move us to a new scope and causes the button to be collected
339+
Assert.NotNull(control);
340+
341+
foreach (var weakRef in weakReferences)
342+
{
343+
Assert.True(weakRef.IsAlive);
344+
}
345+
}
346+
347+
foreach (var weakRef in weakReferences)
348+
{
349+
Assert.False(await weakRef.WaitForCollect());
350+
}
351+
}
352+
353+
class CommandWithoutWeakEventHandler : ICommand
354+
{
355+
public event EventHandler CanExecuteChanged;
356+
357+
public bool CanExecute(object parameter) => true;
358+
359+
public void Execute(object parameter) { }
360+
361+
public void ChangeCanExecute() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
362+
}
254363
}
255-
}
364+
}

0 commit comments

Comments
 (0)