Flutter Engine
image_decoder_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/lib/ui/painting/image_decoder.h"
6 
7 #include "flutter/common/task_runners.h"
8 #include "flutter/fml/mapping.h"
9 #include "flutter/fml/synchronization/waitable_event.h"
10 #include "flutter/lib/ui/painting/multi_frame_codec.h"
11 #include "flutter/runtime/dart_vm.h"
12 #include "flutter/runtime/dart_vm_lifecycle.h"
13 #include "flutter/testing/dart_isolate_runner.h"
14 #include "flutter/testing/elf_loader.h"
15 #include "flutter/testing/fixture_test.h"
16 #include "flutter/testing/test_dart_native_resolver.h"
17 #include "flutter/testing/test_gl_surface.h"
18 #include "flutter/testing/testing.h"
19 #include "third_party/skia/include/codec/SkCodec.h"
20 
21 namespace flutter {
22 namespace testing {
23 
24 class TestIOManager final : public IOManager {
25  public:
27  bool has_gpu_context = true)
28  : gl_surface_(SkISize::Make(1, 1)),
29  gl_context_(has_gpu_context ? gl_surface_.CreateGrContext() : nullptr),
30  weak_gl_context_factory_(
31  has_gpu_context
32  ? std::make_unique<fml::WeakPtrFactory<GrDirectContext>>(
33  gl_context_.get())
34  : nullptr),
35  unref_queue_(fml::MakeRefCounted<SkiaUnrefQueue>(
36  task_runner,
37  fml::TimeDelta::FromNanoseconds(0))),
38  runner_(task_runner),
39  is_gpu_disabled_sync_switch_(std::make_shared<fml::SyncSwitch>()),
40  weak_factory_(this) {
41  FML_CHECK(task_runner->RunsTasksOnCurrentThread())
42  << "The IO manager must be initialized its primary task runner. The "
43  "test harness may not be setup correctly/safely.";
44  weak_prototype_ = weak_factory_.GetWeakPtr();
45  }
46 
47  ~TestIOManager() override {
50  [&latch, queue = unref_queue_]() {
51  queue->Drain();
52  latch.Signal();
53  });
54  latch.Wait();
55  }
56 
57  // |IOManager|
59  return weak_prototype_;
60  }
61 
62  // |IOManager|
64  return weak_gl_context_factory_ ? weak_gl_context_factory_->GetWeakPtr()
66  }
67 
68  // |IOManager|
70  return unref_queue_;
71  }
72 
73  // |IOManager|
74  std::shared_ptr<fml::SyncSwitch> GetIsGpuDisabledSyncSwitch() override {
76  return is_gpu_disabled_sync_switch_;
77  }
78 
80 
81  private:
82  TestGLSurface gl_surface_;
83  sk_sp<GrDirectContext> gl_context_;
84  std::unique_ptr<fml::WeakPtrFactory<GrDirectContext>>
85  weak_gl_context_factory_;
86  fml::RefPtr<SkiaUnrefQueue> unref_queue_;
87  fml::WeakPtr<TestIOManager> weak_prototype_;
89  std::shared_ptr<fml::SyncSwitch> is_gpu_disabled_sync_switch_;
91 
92  FML_DISALLOW_COPY_AND_ASSIGN(TestIOManager);
93 };
94 
95 static sk_sp<SkData> OpenFixtureAsSkData(const char* name) {
96  auto fixtures_directory =
98  if (!fixtures_directory.is_valid()) {
99  return nullptr;
100  }
101 
102  auto fixture_mapping =
103  fml::FileMapping::CreateReadOnly(fixtures_directory, name);
104 
105  if (!fixture_mapping) {
106  return nullptr;
107  }
108 
109  SkData::ReleaseProc on_release = [](const void* ptr, void* context) -> void {
110  delete reinterpret_cast<fml::FileMapping*>(context);
111  };
112 
113  auto data = SkData::MakeWithProc(fixture_mapping->GetMapping(),
114  fixture_mapping->GetSize(), on_release,
115  fixture_mapping.get());
116 
117  if (!data) {
118  return nullptr;
119  }
120  // The data is now owned by Skia.
121  fixture_mapping.release();
122  return data;
123 }
124 
126 
127 TEST_F(ImageDecoderFixtureTest, CanCreateImageDecoder) {
129  auto thread_task_runner = CreateNewThread();
130  TaskRunners runners(GetCurrentTestName(), // label
131  thread_task_runner, // platform
132  thread_task_runner, // raster
133  thread_task_runner, // ui
134  thread_task_runner // io
135 
136  );
137 
139  runners.GetIOTaskRunner()->PostTask([&]() {
140  TestIOManager manager(runners.GetIOTaskRunner());
141  ImageDecoder decoder(std::move(runners), loop->GetTaskRunner(),
142  manager.GetWeakIOManager());
143  latch.Signal();
144  });
145  latch.Wait();
146 }
147 
148 TEST_F(ImageDecoderFixtureTest, InvalidImageResultsError) {
150  auto thread_task_runner = CreateNewThread();
151  TaskRunners runners(GetCurrentTestName(), // label
152  thread_task_runner, // platform
153  thread_task_runner, // raster
154  thread_task_runner, // ui
155  thread_task_runner // io
156  );
157 
159  thread_task_runner->PostTask([&]() {
160  TestIOManager manager(runners.GetIOTaskRunner());
161  ImageDecoder decoder(runners, loop->GetTaskRunner(),
162  manager.GetWeakIOManager());
163 
164  auto data = OpenFixtureAsSkData("ThisDoesNotExist.jpg");
165  ASSERT_FALSE(data);
166 
167  fml::RefPtr<ImageDescriptor> image_descriptor =
168  fml::MakeRefCounted<ImageDescriptor>(std::move(data),
169  std::unique_ptr<SkCodec>(nullptr));
170 
171  ImageDecoder::ImageResult callback = [&](SkiaGPUObject<SkImage> image) {
172  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
173  ASSERT_FALSE(image.get());
174  latch.Signal();
175  };
176  decoder.Decode(image_descriptor, 0, 0, callback);
177  });
178  latch.Wait();
179 }
180 
181 TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) {
183  TaskRunners runners(GetCurrentTestName(), // label
184  CreateNewThread("platform"), // platform
185  CreateNewThread("raster"), // raster
186  CreateNewThread("ui"), // ui
187  CreateNewThread("io") // io
188  );
189 
191 
192  std::unique_ptr<TestIOManager> io_manager;
193 
194  auto release_io_manager = [&]() {
195  io_manager.reset();
196  latch.Signal();
197  };
198  auto decode_image = [&]() {
199  std::unique_ptr<ImageDecoder> image_decoder =
200  std::make_unique<ImageDecoder>(runners, loop->GetTaskRunner(),
201  io_manager->GetWeakIOManager());
202 
203  auto data = OpenFixtureAsSkData("DashInNooglerHat.jpg");
204 
205  ASSERT_TRUE(data);
206  ASSERT_GE(data->size(), 0u);
207 
208  std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data);
209  ASSERT_TRUE(codec);
210 
211  auto descriptor =
212  fml::MakeRefCounted<ImageDescriptor>(std::move(data), std::move(codec));
213 
214  ImageDecoder::ImageResult callback = [&](SkiaGPUObject<SkImage> image) {
215  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
216  ASSERT_TRUE(image.get());
217  EXPECT_TRUE(io_manager->did_access_is_gpu_disabled_sync_switch_);
218  runners.GetIOTaskRunner()->PostTask(release_io_manager);
219  };
220  EXPECT_FALSE(io_manager->did_access_is_gpu_disabled_sync_switch_);
221  image_decoder->Decode(descriptor, descriptor->width(), descriptor->height(),
222  callback);
223  };
224 
225  auto setup_io_manager_and_decode = [&]() {
226  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
227  runners.GetUITaskRunner()->PostTask(decode_image);
228  };
229 
230  runners.GetIOTaskRunner()->PostTask(setup_io_manager_and_decode);
231  latch.Wait();
232 }
233 
234 TEST_F(ImageDecoderFixtureTest, ExifDataIsRespectedOnDecode) {
236  TaskRunners runners(GetCurrentTestName(), // label
237  CreateNewThread("platform"), // platform
238  CreateNewThread("raster"), // raster
239  CreateNewThread("ui"), // ui
240  CreateNewThread("io") // io
241  );
242 
244 
245  std::unique_ptr<IOManager> io_manager;
246 
247  auto release_io_manager = [&]() {
248  io_manager.reset();
249  latch.Signal();
250  };
251 
252  SkISize decoded_size = SkISize::MakeEmpty();
253  auto decode_image = [&]() {
254  std::unique_ptr<ImageDecoder> image_decoder =
255  std::make_unique<ImageDecoder>(runners, loop->GetTaskRunner(),
256  io_manager->GetWeakIOManager());
257 
258  auto data = OpenFixtureAsSkData("Horizontal.jpg");
259 
260  ASSERT_TRUE(data);
261  ASSERT_GE(data->size(), 0u);
262 
263  std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data);
264  ASSERT_TRUE(codec);
265 
266  auto descriptor =
267  fml::MakeRefCounted<ImageDescriptor>(std::move(data), std::move(codec));
268 
269  ImageDecoder::ImageResult callback = [&](SkiaGPUObject<SkImage> image) {
270  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
271  ASSERT_TRUE(image.get());
272  decoded_size = image.get()->dimensions();
273  runners.GetIOTaskRunner()->PostTask(release_io_manager);
274  };
275  image_decoder->Decode(descriptor, descriptor->width(), descriptor->height(),
276  callback);
277  };
278 
279  auto setup_io_manager_and_decode = [&]() {
280  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
281  runners.GetUITaskRunner()->PostTask(decode_image);
282  };
283 
284  runners.GetIOTaskRunner()->PostTask(setup_io_manager_and_decode);
285 
286  latch.Wait();
287 
288  ASSERT_EQ(decoded_size.width(), 600);
289  ASSERT_EQ(decoded_size.height(), 200);
290 }
291 
292 TEST_F(ImageDecoderFixtureTest, CanDecodeWithoutAGPUContext) {
294  TaskRunners runners(GetCurrentTestName(), // label
295  CreateNewThread("platform"), // platform
296  CreateNewThread("raster"), // raster
297  CreateNewThread("ui"), // ui
298  CreateNewThread("io") // io
299  );
300 
302 
303  std::unique_ptr<IOManager> io_manager;
304 
305  auto release_io_manager = [&]() {
306  io_manager.reset();
307  latch.Signal();
308  };
309 
310  auto decode_image = [&]() {
311  std::unique_ptr<ImageDecoder> image_decoder =
312  std::make_unique<ImageDecoder>(runners, loop->GetTaskRunner(),
313  io_manager->GetWeakIOManager());
314 
315  auto data = OpenFixtureAsSkData("DashInNooglerHat.jpg");
316 
317  ASSERT_TRUE(data);
318  ASSERT_GE(data->size(), 0u);
319 
320  std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data);
321  ASSERT_TRUE(codec);
322 
323  auto descriptor =
324  fml::MakeRefCounted<ImageDescriptor>(std::move(data), std::move(codec));
325 
326  ImageDecoder::ImageResult callback = [&](SkiaGPUObject<SkImage> image) {
327  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
328  ASSERT_TRUE(image.get());
329  runners.GetIOTaskRunner()->PostTask(release_io_manager);
330  };
331  image_decoder->Decode(descriptor, descriptor->width(), descriptor->height(),
332  callback);
333  };
334 
335  auto setup_io_manager_and_decode = [&]() {
336  io_manager =
337  std::make_unique<TestIOManager>(runners.GetIOTaskRunner(), false);
338  runners.GetUITaskRunner()->PostTask(decode_image);
339  };
340 
341  runners.GetIOTaskRunner()->PostTask(setup_io_manager_and_decode);
342 
343  latch.Wait();
344 }
345 
346 TEST_F(ImageDecoderFixtureTest, CanDecodeWithResizes) {
347  const auto image_dimensions =
348  SkImage::MakeFromEncoded(OpenFixtureAsSkData("DashInNooglerHat.jpg"))
349  ->dimensions();
350 
351  ASSERT_FALSE(image_dimensions.isEmpty());
352 
353  ASSERT_NE(image_dimensions.width(), image_dimensions.height());
354 
356  TaskRunners runners(GetCurrentTestName(), // label
357  CreateNewThread("platform"), // platform
358  CreateNewThread("raster"), // raster
359  CreateNewThread("ui"), // ui
360  CreateNewThread("io") // io
361  );
362 
364  std::unique_ptr<IOManager> io_manager;
365  std::unique_ptr<ImageDecoder> image_decoder;
366 
367  // Setup the IO manager.
368  runners.GetIOTaskRunner()->PostTask([&]() {
369  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
370  latch.Signal();
371  });
372  latch.Wait();
373 
374  // Setup the image decoder.
375  runners.GetUITaskRunner()->PostTask([&]() {
376  image_decoder = std::make_unique<ImageDecoder>(
377  runners, loop->GetTaskRunner(), io_manager->GetWeakIOManager());
378 
379  latch.Signal();
380  });
381  latch.Wait();
382 
383  // Setup a generic decoding utility that gives us the final decoded size.
384  auto decoded_size = [&](uint32_t target_width,
385  uint32_t target_height) -> SkISize {
386  SkISize final_size = SkISize::MakeEmpty();
387  runners.GetUITaskRunner()->PostTask([&]() {
388  auto data = OpenFixtureAsSkData("DashInNooglerHat.jpg");
389 
390  ASSERT_TRUE(data);
391  ASSERT_GE(data->size(), 0u);
392 
393  std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data);
394  ASSERT_TRUE(codec);
395 
396  auto descriptor = fml::MakeRefCounted<ImageDescriptor>(std::move(data),
397  std::move(codec));
398 
399  ImageDecoder::ImageResult callback = [&](SkiaGPUObject<SkImage> image) {
400  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
401  ASSERT_TRUE(image.get());
402  final_size = image.get()->dimensions();
403  latch.Signal();
404  };
405  image_decoder->Decode(descriptor, target_width, target_height, callback);
406  });
407  latch.Wait();
408  return final_size;
409  };
410 
411  ASSERT_EQ(SkISize::Make(3024, 4032), image_dimensions);
412  ASSERT_EQ(decoded_size(3024, 4032), image_dimensions);
413  ASSERT_EQ(decoded_size(100, 100), SkISize::Make(100, 100));
414 
415  // Destroy the IO manager
416  runners.GetIOTaskRunner()->PostTask([&]() {
417  io_manager.reset();
418  latch.Signal();
419  });
420  latch.Wait();
421 
422  // Destroy the image decoder
423  runners.GetUITaskRunner()->PostTask([&]() {
424  image_decoder.reset();
425  latch.Signal();
426  });
427  latch.Wait();
428 }
429 
430 TEST_F(ImageDecoderFixtureTest, CanResizeWithoutDecode) {
431  SkImageInfo info = {};
432  size_t row_bytes;
433  sk_sp<SkData> decompressed_data;
434  SkISize image_dimensions = SkISize::MakeEmpty();
435  {
436  auto image =
437  SkImage::MakeFromEncoded(OpenFixtureAsSkData("DashInNooglerHat.jpg"))
438  ->makeRasterImage();
439  image_dimensions = image->dimensions();
440  SkPixmap pixmap;
441  ASSERT_TRUE(image->peekPixels(&pixmap));
442  info = SkImageInfo::MakeN32Premul(image_dimensions);
443  row_bytes = pixmap.rowBytes();
444  decompressed_data =
445  SkData::MakeWithCopy(pixmap.writable_addr(), pixmap.computeByteSize());
446  }
447 
448  // This is not susceptible to changes in the underlying image decoder.
449  ASSERT_EQ(decompressed_data->size(), 48771072u);
450  ASSERT_EQ(decompressed_data->size(),
451  image_dimensions.width() * image_dimensions.height() * 4u);
452  ASSERT_EQ(row_bytes, image_dimensions.width() * 4u);
453  ASSERT_FALSE(image_dimensions.isEmpty());
454  ASSERT_NE(image_dimensions.width(), image_dimensions.height());
455 
457  TaskRunners runners(GetCurrentTestName(), // label
458  CreateNewThread("platform"), // platform
459  CreateNewThread("raster"), // raster
460  CreateNewThread("ui"), // ui
461  CreateNewThread("io") // io
462  );
463 
465  std::unique_ptr<IOManager> io_manager;
466  std::unique_ptr<ImageDecoder> image_decoder;
467 
468  // Setup the IO manager.
469  runners.GetIOTaskRunner()->PostTask([&]() {
470  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
471  latch.Signal();
472  });
473  latch.Wait();
474 
475  // Setup the image decoder.
476  runners.GetUITaskRunner()->PostTask([&]() {
477  image_decoder = std::make_unique<ImageDecoder>(
478  runners, loop->GetTaskRunner(), io_manager->GetWeakIOManager());
479 
480  latch.Signal();
481  });
482  latch.Wait();
483 
484  // Setup a generic decoding utility that gives us the final decoded size.
485  auto decoded_size = [&](uint32_t target_width,
486  uint32_t target_height) -> SkISize {
487  SkISize final_size = SkISize::MakeEmpty();
488  runners.GetUITaskRunner()->PostTask([&]() {
489  ASSERT_TRUE(decompressed_data);
490  ASSERT_GE(decompressed_data->size(), 0u);
491 
492  auto descriptor = fml::MakeRefCounted<ImageDescriptor>(decompressed_data,
493  info, row_bytes);
494 
495  ImageDecoder::ImageResult callback = [&](SkiaGPUObject<SkImage> image) {
496  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
497  ASSERT_TRUE(image.get());
498  final_size = image.get()->dimensions();
499  latch.Signal();
500  };
501  image_decoder->Decode(descriptor, target_width, target_height, callback);
502  });
503  latch.Wait();
504  return final_size;
505  };
506 
507  ASSERT_EQ(SkISize::Make(3024, 4032), image_dimensions);
508  ASSERT_EQ(decoded_size(3024, 4032), image_dimensions);
509  ASSERT_EQ(decoded_size(100, 100), SkISize::Make(100, 100));
510 
511  // Destroy the IO manager
512  runners.GetIOTaskRunner()->PostTask([&]() {
513  io_manager.reset();
514  latch.Signal();
515  });
516  latch.Wait();
517 
518  // Destroy the image decoder
519  runners.GetUITaskRunner()->PostTask([&]() {
520  image_decoder.reset();
521  latch.Signal();
522  });
523  latch.Wait();
524 }
525 
526 // Verifies https://skia-review.googlesource.com/c/skia/+/259161 is present in
527 // Flutter.
528 TEST(ImageDecoderTest,
529  VerifyCodecRepeatCountsForGifAndWebPAreConsistentWithLoopCounts) {
530  auto gif_mapping = OpenFixtureAsSkData("hello_loop_2.gif");
531  auto webp_mapping = OpenFixtureAsSkData("hello_loop_2.webp");
532 
533  ASSERT_TRUE(gif_mapping);
534  ASSERT_TRUE(webp_mapping);
535 
536  auto gif_codec = SkCodec::MakeFromData(gif_mapping);
537  auto webp_codec = SkCodec::MakeFromData(webp_mapping);
538 
539  ASSERT_TRUE(gif_codec);
540  ASSERT_TRUE(webp_codec);
541 
542  // Both fixtures have a loop count of 2 which should lead to the repeat count
543  // of 1
544  ASSERT_EQ(gif_codec->getRepetitionCount(), 1);
545  ASSERT_EQ(webp_codec->getRepetitionCount(), 1);
546 }
547 
548 TEST(ImageDecoderTest, VerifySimpleDecoding) {
549  auto data = OpenFixtureAsSkData("Horizontal.jpg");
550  auto image = SkImage::MakeFromEncoded(data);
551  ASSERT_TRUE(image != nullptr);
552  ASSERT_EQ(SkISize::Make(600, 200), image->dimensions());
553 
554  auto codec = SkCodec::MakeFromData(data);
555  ASSERT_TRUE(codec);
556  auto descriptor =
557  fml::MakeRefCounted<ImageDescriptor>(std::move(data), std::move(codec));
558 
559  ASSERT_EQ(
560  ImageFromCompressedData(descriptor, 6, 2, fml::tracing::TraceFlow(""))
561  ->dimensions(),
562  SkISize::Make(6, 2));
563 }
564 
565 TEST(ImageDecoderTest, VerifySubpixelDecodingPreservesExifOrientation) {
566  auto data = OpenFixtureAsSkData("Horizontal.jpg");
567  auto codec = SkCodec::MakeFromData(data);
568  ASSERT_TRUE(codec);
569  auto descriptor =
570  fml::MakeRefCounted<ImageDescriptor>(data, std::move(codec));
571 
572  auto image = SkImage::MakeFromEncoded(data);
573  ASSERT_TRUE(image != nullptr);
574  ASSERT_EQ(SkISize::Make(600, 200), image->dimensions());
575 
576  auto decode = [descriptor](uint32_t target_width, uint32_t target_height) {
577  return ImageFromCompressedData(descriptor, target_width, target_height,
579  };
580 
581  auto expected_data = OpenFixtureAsSkData("Horizontal.png");
582  ASSERT_TRUE(expected_data != nullptr);
583  ASSERT_FALSE(expected_data->isEmpty());
584 
585  auto assert_image = [&](auto decoded_image) {
586  ASSERT_EQ(decoded_image->dimensions(), SkISize::Make(300, 100));
587  ASSERT_TRUE(decoded_image->encodeToData(SkEncodedImageFormat::kPNG, 100)
588  ->equals(expected_data.get()));
589  };
590 
591  assert_image(decode(300, 100));
592 }
593 
595  MultiFrameCodecCanBeCollectedBeforeIOTasksFinish) {
596  // This test verifies that the MultiFrameCodec safely shares state between
597  // tasks on the IO and UI runners, and does not allow unsafe memory access if
598  // the UI object is collected while the IO thread still has pending decode
599  // work. This could happen in a real application if the engine is collected
600  // while a multi-frame image is decoding. To exercise this, the test:
601  // - Starts a Dart VM
602  // - Latches the IO task runner
603  // - Create a MultiFrameCodec for an animated gif pointed to a callback
604  // in the Dart fixture
605  // - Calls getNextFrame on the UI task runner
606  // - Collects the MultiFrameCodec object before unlatching the IO task
607  // runner.
608  // - Unlatches the IO task runner
609  auto settings = CreateSettingsForFixture();
610  auto vm_ref = DartVMRef::Create(settings);
611  auto vm_data = vm_ref.GetVMData();
612 
613  auto gif_mapping = OpenFixtureAsSkData("hello_loop_2.gif");
614 
615  ASSERT_TRUE(gif_mapping);
616 
617  auto gif_codec = std::shared_ptr<SkCodecImageGenerator>(
618  static_cast<SkCodecImageGenerator*>(
619  SkCodecImageGenerator::MakeFromEncodedCodec(gif_mapping).release()));
620  ASSERT_TRUE(gif_codec);
621 
622  TaskRunners runners(GetCurrentTestName(), // label
623  CreateNewThread("platform"), // platform
624  CreateNewThread("raster"), // raster
625  CreateNewThread("ui"), // ui
626  CreateNewThread("io") // io
627  );
628 
631  std::unique_ptr<TestIOManager> io_manager;
632 
633  // Setup the IO manager.
634  runners.GetIOTaskRunner()->PostTask([&]() {
635  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
636  latch.Signal();
637  });
638  latch.Wait();
639 
640  auto isolate =
641  RunDartCodeInIsolate(vm_ref, settings, runners, "main", {},
642  GetFixturesPath(), io_manager->GetWeakIOManager());
643 
644  // Latch the IO task runner.
645  runners.GetIOTaskRunner()->PostTask([&]() { io_latch.Wait(); });
646 
647  runners.GetUITaskRunner()->PostTask([&]() {
648  fml::AutoResetWaitableEvent isolate_latch;
650  EXPECT_TRUE(isolate->RunInIsolateScope([&]() -> bool {
651  Dart_Handle library = Dart_RootLibrary();
652  if (Dart_IsError(library)) {
653  isolate_latch.Signal();
654  return false;
655  }
656  Dart_Handle closure =
657  Dart_GetField(library, Dart_NewStringFromCString("frameCallback"));
658  if (Dart_IsError(closure) || !Dart_IsClosure(closure)) {
659  isolate_latch.Signal();
660  return false;
661  }
662 
663  codec = fml::MakeRefCounted<MultiFrameCodec>(std::move(gif_codec));
664  codec->getNextFrame(closure);
665  codec = nullptr;
666  isolate_latch.Signal();
667  return true;
668  }));
669  isolate_latch.Wait();
670 
671  EXPECT_FALSE(codec);
672 
673  io_latch.Signal();
674 
675  latch.Signal();
676  });
677  latch.Wait();
678 
679  // Destroy the IO manager
680  runners.GetIOTaskRunner()->PostTask([&]() {
681  io_manager.reset();
682  latch.Signal();
683  });
684  latch.Wait();
685 }
686 
687 } // namespace testing
688 } // namespace flutter
std::shared_ptr< fml::SyncSwitch > GetIsGpuDisabledSyncSwitch() override
static void RunNowOrPostTask(fml::RefPtr< fml::TaskRunner > runner, const fml::closure &task)
Definition: task_runner.cc:55
std::string GetCurrentTestName()
Gets the name of the currently running test. This is useful in generating logs or assets based on tes...
Definition: testing.cc:12
Definition: ref_ptr.h:252
fml::WeakPtr< GrDirectContext > GetResourceContext() const override
virtual void PostTask(const fml::closure &task)
Definition: task_runner.cc:24
TestIOManager(fml::RefPtr< fml::TaskRunner > task_runner, bool has_gpu_context=true)
static DartVMRef Create(Settings settings, fml::RefPtr< DartSnapshot > vm_snapshot=nullptr, fml::RefPtr< DartSnapshot > isolate_snapshot=nullptr)
Definition: ascii_trie.cc:9
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:94
std::function< void(SkiaGPUObject< SkImage >)> ImageResult
Definition: image_decoder.h:41
fml::RefPtr< flutter::SkiaUnrefQueue > GetSkiaUnrefQueue() const override
std::function< void()> closure
Definition: closure.h:14
static std::shared_ptr< ConcurrentMessageLoop > Create(size_t worker_count=std::thread::hardware_concurrency())
const char * GetFixturesPath()
Returns the directory containing the test fixture for the target if this target has fixtures configur...
fml::WeakPtr< IOManager > GetWeakIOManager() const override
TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies)
fml::RefPtr< fml::TaskRunner > GetIOTaskRunner() const
Definition: task_runners.cc:38
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
Definition: task_runners.cc:34
RefPtr< T > MakeRefCounted(Args &&... args)
Definition: ref_ptr.h:244
const char * name
Definition: fuchsia.cc:50
static sk_sp< SkData > OpenFixtureAsSkData(const char *name)
#define FML_CHECK(condition)
Definition: logging.h:68
virtual bool RunsTasksOnCurrentThread()
Definition: task_runner.cc:43
std::unique_ptr< AutoIsolateShutdown > RunDartCodeInIsolate(DartVMRef &vm_ref, const Settings &settings, const TaskRunners &task_runners, std::string entrypoint, const std::vector< std::string > &args, const std::string &fixtures_path, fml::WeakPtr< IOManager > io_manager)
static std::unique_ptr< FileMapping > CreateReadOnly(const std::string &path)
Definition: mapping.cc:18
sk_sp< SkImage > ImageFromCompressedData(fml::RefPtr< ImageDescriptor > descriptor, uint32_t target_width, uint32_t target_height, const fml::tracing::TraceFlow &flow)
TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithNoMutations)