OSDN Git Service

OTPictureBox.Imageを直接更新した場合もSetImageFromTaskをキャンセルした扱いにする
authorKimura Youichi <kim.upsilon@bucyou.net>
Sat, 8 Jul 2023 16:12:22 +0000 (01:12 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Sat, 8 Jul 2023 16:14:54 +0000 (01:14 +0900)
OpenTween.Tests/OTPictureBoxTest.cs
OpenTween/OTPictureBox.cs

index 0dca848..e3487c5 100644 (file)
@@ -111,5 +111,54 @@ namespace OpenTween
 
             Assert.Equal(picbox.ErrorImage, ((PictureBox)picbox).Image);
         }
+
+        [WinFormsFact]
+        public async Task SetImageFromAsync_OutdatedTest()
+        {
+            using var picbox = new OTPictureBox();
+
+            // 1回目
+            var tcs1 = new TaskCompletionSource<MemoryImage>();
+            var setImageTask1 = picbox.SetImageFromTask(() => tcs1.Task);
+
+            // 2回目
+            var tcs2 = new TaskCompletionSource<MemoryImage>();
+            var setImageTask2 = picbox.SetImageFromTask(() => tcs2.Task);
+
+            Assert.Same(picbox.InitialImage, ((PictureBox)picbox).Image);
+
+            // 2回目のタスクが先に完了する
+            using var image2 = TestUtils.CreateDummyImage();
+            tcs2.SetResult(image2);
+            await setImageTask2;
+            Assert.Same(image2, picbox.Image);
+
+            // 1回目のタスクが完了したとしても、最後に呼んだ SetImageFromTask の画像を表示し続ける
+            using var image1 = TestUtils.CreateDummyImage();
+            tcs1.SetResult(image1);
+            await setImageTask1;
+            Assert.Same(image2, picbox.Image);
+        }
+
+        [WinFormsFact]
+        public async Task SetImageFromAsync_OutdatedByImageSetterTest()
+        {
+            using var picbox = new OTPictureBox();
+
+            // 1回目
+            var tcs1 = new TaskCompletionSource<MemoryImage>();
+            var setImageTask1 = picbox.SetImageFromTask(() => tcs1.Task);
+
+            // 2回目 (set_Image による同期的な更新)
+            using var image2 = TestUtils.CreateDummyImage();
+            picbox.Image = image2;
+            Assert.Same(image2, picbox.Image);
+
+            // 1回目のタスクが完了したとしても、最後にセットされた Image の画像を表示し続ける
+            using var image1 = TestUtils.CreateDummyImage();
+            tcs1.SetResult(image1);
+            await setImageTask1;
+            Assert.Same(image2, picbox.Image);
+        }
     }
 }
index 21a7753..f181b19 100644 (file)
@@ -44,6 +44,18 @@ namespace OpenTween
         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
         public new MemoryImage? Image
         {
+            get => this.ImageInternal;
+            set
+            {
+                this.ImageInternal = value;
+
+                // Image を直接更新した場合も SetImageFromTask による更新をキャンセルした扱いにする
+                Interlocked.Increment(ref this.currentImageTaskId);
+            }
+        }
+
+        private MemoryImage? ImageInternal
+        {
             get => this.memoryImage;
             set
             {
@@ -115,7 +127,7 @@ namespace OpenTween
                 var image = await Task.Run(imageTask);
 
                 if (id == this.currentImageTaskId)
-                    this.Image = image;
+                    this.ImageInternal = image;
             }
             catch (Exception)
             {