1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_DROP_FRAME_TEST_H 17#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_DROP_FRAME_TEST_H 18 19#include "test/utils/test_util.h" 20 21namespace panda::ecmascript::tooling::test { 22class JsDropFrameTest : public TestEvents { 23public: 24 JsDropFrameTest() 25 { 26 channel_ = new JsDropFrameTestChannel(); 27 28 breakpoint = [this](const JSPtLocation &location) { 29 ASSERT_TRUE(location.GetMethodId().IsValid()); 30 ++breakpointCounter_; 31 debugger_->NotifyPaused(location, PauseReason::INSTRUMENTATION); 32 static_cast<JsDropFrameTestChannel *>(channel_)->IncreaseCheckVariableCounter(); 33 DropFrameIfChecked(); 34 TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location); 35 return true; 36 }; 37 38 singleStep = [this](const JSPtLocation &location) { 39 if (debugger_->NotifySingleStep(location)) { 40 debugger_->NotifyPaused({}, PauseReason::INSTRUMENTATION); 41 static_cast<JsDropFrameTestChannel *>(channel_)->IncreaseCheckVariableCounter(); 42 TestUtil::SuspendUntilContinue(DebugEvent::CHECK_COMPLETE); 43 DropFrameIfChecked(); 44 TestUtil::SuspendUntilContinue(DebugEvent::DROPFRAME); 45 return true; 46 } 47 return false; 48 }; 49 50 loadModule = [this](std::string_view moduleName) { 51 static_cast<JsDropFrameTestChannel *>(channel_)->Initial(vm_, runtime_); 52 runtime_->Enable(); 53 size_t breakpoint[POINTER_SIZE][LINE_COLUMN] = 54 {{26, 0}, {42, 0}, {57, 0}, {69, 0}, {87, 0}, {100, 0}, {117, 0}, 55 {143, 0}, {151, 0}, {164, 0}, {172, 0}, {182, 0}, {192, 0}, {206, 0}, 56 {217, 0}, {224, 0}, {230, 0}}; // 0-based 57 SetJSPtLocation(breakpoint[0], POINTER_SIZE, pointerLocations_); 58 InitBreakpointOpQueue(); 59 TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE); 60 ASSERT_EQ(moduleName, pandaFile_); 61 ASSERT_TRUE(debugger_->NotifyScriptParsed(0, pandaFile_)); 62 auto condFuncRef = FunctionRef::Undefined(vm_); 63 for (auto &iter : pointerLocations_) { 64 auto ret = debugInterface_->SetBreakpoint(iter, condFuncRef); 65 ASSERT_TRUE(ret); 66 } 67 return true; 68 }; 69 70 scenario = [this]() { 71 TestUtil::WaitForLoadModule(); 72 TestUtil::Continue(); 73 for (size_t index = 0; index < POINTER_SIZE; index++) { 74 for (size_t hitCount = 0; hitCount < breakpointHitTimes[index]; hitCount++) { 75 TestUtil::WaitForBreakpoint(pointerLocations_.at(index)); 76 if (hitCount == breakpointHitTimes[index] - 1) { 77 auto ret = debugInterface_->RemoveBreakpoint(pointerLocations_.at(index)); 78 ASSERT_TRUE(ret); 79 } 80 TestUtil::Continue(); 81 while (dropFrameChecked_) { 82 dropFrameChecked_ = false; 83 TestUtil::WaitForCheckComplete(); 84 TestUtil::Continue(); 85 TestUtil::WaitForDropframe(); 86 dropframeCounter_++; 87 TestUtil::Continue(); 88 } 89 } 90 } 91 ASSERT_EXITED(); 92 return true; 93 }; 94 95 vmDeath = [this]() { 96 ASSERT_EQ(breakpointCounter_, 59U); // a total of 59 times hitting breakpoints 97 ASSERT_EQ(dropframeCounter_, 47U); // a total of 47 times dropping frames 98 return true; 99 }; 100 } 101 102 std::pair<std::string, std::string> GetEntryPoint() override 103 { 104 return {pandaFile_, entryPoint_}; 105 } 106 ~JsDropFrameTest() 107 { 108 delete channel_; 109 channel_ = nullptr; 110 } 111 112private: 113 class JsDropFrameTestChannel : public TestChannel { 114 public: 115 JsDropFrameTestChannel() = default; 116 ~JsDropFrameTestChannel() = default; 117 void Initial(const EcmaVM *vm, RuntimeImpl *runtime) 118 { 119 vm_ = vm; 120 runtime_ = runtime; 121 } 122 123 void SendNotification(const PtBaseEvents &events) override 124 { 125 const static std::vector<std::function<bool(const PtBaseEvents &events)>> eventList = { 126 [](const PtBaseEvents &events) -> bool { 127 std::string sourceFile = DEBUGGER_JS_DIR "dropframe.js"; 128 auto parsed = static_cast<const ScriptParsed *>(&events); 129 ASSERT_EQ(parsed->GetName(), "Debugger.scriptParsed"); 130 ASSERT_EQ(parsed->GetUrl(), sourceFile); 131 return true; 132 }, 133 [this](const PtBaseEvents &events) -> bool { 134 std::cout << checkVariableCounter_ << std::endl; 135 auto paused = static_cast<const Paused *>(&events); 136 ASSERT_EQ(paused->GetName(), "Debugger.paused"); 137 auto allFrames = paused->GetCallFrames(); 138 std::map<std::string, std::string> truthVariableMap = variableMap_.at(checkVariableCounter_); 139 for (size_t index = 0; index < allFrames->size(); index++) { 140 auto frame = paused->GetCallFrames()->at(index).get(); 141 auto scopes = frame->GetScopeChain(); 142 for (uint32_t i = 0; i < scopes->size(); i++) { 143 auto scope = scopes->at(i).get(); 144 std::string scopeType = scope->GetType(); 145 if (scopeType != Scope::Type::Local() && scopeType != Scope::Type::Closure()) { 146 continue; 147 } 148 auto localId = scope->GetObject()->GetObjectId(); 149 GetPropertiesParams params; 150 params.SetObjectId(localId).SetOwnProperties(true); 151 std::vector<std::unique_ptr<PropertyDescriptor>> outPropertyDesc; 152 runtime_->GetProperties(params, &outPropertyDesc, {}, {}, {}); 153 for (const auto &property : outPropertyDesc) { 154 std::cout << "=====================================" << std::endl; 155 std::string name = property->GetName(); 156 std::cout << name << std::endl; 157 if (truthVariableMap.count(name)) { 158 auto value = property->GetValue(); 159 if (value->HasValue()) { 160 std::string valueStr = value->GetValue()->ToString(vm_)->ToString(vm_); 161 std::cout << valueStr << std::endl; 162 ASSERT_EQ(valueStr, truthVariableMap[name]); 163 } 164 } 165 } 166 } 167 } 168 return true; 169 } 170 }; 171 172 ASSERT_TRUE(eventList[index_](events)); 173 index_ |= 1; 174 } 175 176 void IncreaseCheckVariableCounter() 177 { 178 checkVariableCounter_++; 179 } 180 181 private: 182 NO_COPY_SEMANTIC(JsDropFrameTestChannel); 183 NO_MOVE_SEMANTIC(JsDropFrameTestChannel); 184 185 const std::vector<std::map<std::string, std::string>> variableMap_ = { 186 { { "a", "2" }, { "b", "3" }, { "c", "4" }, {"d", "2"} }, 187 { { "a", "2" }, { "b", "3" }, { "c", "3" }, {"d", "1"} }, 188 { { "a", "2" }, { "b", "2" }, { "c", "3" }, {"d", "0"} }, 189 { { "a", "1" }, { "b", "2" }, { "c", "3" } }, 190 { { "a", "2" }, { "b", "3" }, { "c", "4" }, {"d", "2"} }, 191 { { "a", "4" }, { "b", "5" }, { "c", "6" }, {"d", "2"} }, 192 { { "a", "6" }, { "b", "4" }, { "c", "5" }, {"d", "2"} }, 193 { { "a", "6" }, { "b", "4" }, { "c", "5" }, {"d", "1"} }, 194 { { "a", "2" }, { "b", "3" }, { "c", "4" } }, 195 { { "a", "4" }, { "b", "5" }, { "c", "6" }, {"d", "2"} }, 196 { { "a", "8" }, { "b", "13" }, { "c", "78" }, {"d", "1"} }, 197 { { "a", "4" }, { "b", "5" }, { "c", "6" }, {"d", "1"} }, 198 { { "a", "8" }, { "b", "13" }, { "c", "78" }, {"d", "1"} }, 199 { { "a", "16" }, { "b", "29" }, { "c", "2262" }, {"d", "182"} }, 200 { { "a", "8" }, { "b", "13" }, { "c", "78" }, {"d", "182"} }, 201 { { "a", "4" }, { "b", "5" }, { "c", "6" } }, 202 { { "a", "8" }, { "b", "13" }, { "c", "78" }, {"d", "1"} }, 203 { { "a", "32" }, { "b", "61" }, { "c", "4214" }, {"d", "1"} }, 204 { { "a", "16" }, { "b", "29" }, { "c", "2262" }, {"d", "1"} }, 205 { { "a", "32" }, { "b", "61" }, { "c", "4214" }, {"d", "1"} }, 206 { { "a", "64" }, { "b", "125" }, { "c", "12214" }, {"d", "29"} }, 207 { { "a", "128" }, { "b", "253" }, { "c", "44598" }, {"d", "1769"} }, 208 { { "a", "256" }, { "b", "509" }, { "c", "174902" }, {"d", "221125"} }, 209 { { "a", "128" }, { "b", "253" }, { "c", "44598" }, {"d", "221125"} }, 210 { { "a", "16" }, { "b", "29" }, { "c", "2262" } }, 211 { { "a", "32" }, { "b", "61" }, { "c", "4214" }, {"d", "1"} }, 212 { { "a", "256" }, { "b", "1277" }, { "c", "176435" } }, 213 { { "a", "256" }, { "b", "509" }, { "c", "174902" } }, 214 { { "a", "256" }, { "b", "509" }, { "c", "174902" } }, 215 { { "a", "256" }, { "b", "1277" }, { "c", "176435" } }, 216 { { "a", "256" }, { "b", "2301" }, { "c", "178992" } }, 217 { { "a", "512" }, { "b", "3837" }, { "c", "183341" } }, 218 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } }, 219 { { "a", "512" }, { "b", "3837" }, { "c", "183341" } }, 220 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } }, 221 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" }, {"s", "2"} }, 222 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" }, {"s", "2"} }, 223 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } }, 224 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } }, 225 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } }, 226 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" }, {"s", "2"} }, 227 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" }, {"s", "3"} }, 228 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } }, 229 { { "a", "1024" }, { "b", "7933" }, { "c", "192298" }, {"s", "3"} }, 230 { { "a", "10" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} }, 231 { { "a", "9" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} }, 232 { { "a", "8" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} }, 233 { { "a", "10" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} }, 234 { { "a", "11" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} }, 235 { { "a", "12" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} }, 236 { { "a", "7" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} }, 237 { { "a", "10" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} }, 238 { { "a", "19" }, { "b", "2" }, { "c", "3" }, {"d", "1"} }, 239 { { "a", "19" }, { "b", "2" }, { "c", "3" }, {"d", "1"} }, 240 { { "a", "19" }, { "b", "2" }, { "c", "3" }, {"d", "1"} }, 241 { { "a", "20" }, { "b", "3" }, { "c", "4" }, {"d", "3"} }, 242 { { "a", "19" }, { "b", "2" }, { "c", "3" }, {"d", "2"} }, 243 { { "a", "20" }, { "b", "3" }, { "c", "4" }, {"d", "3"} }, 244 { { "a", "23" }, { "b", "6" }, { "c", "7" }, {"d", "7"} }, 245 { { "a", "20" }, { "b", "3" }, { "c", "4" }, {"d", "4"} }, 246 { { "a", "23" }, { "b", "6" }, { "c", "7" }, {"d", "7"} }, 247 { { "a", "29" }, { "b", "12" }, { "c", "13" }, {"d", "14"} }, 248 { { "a", "39" }, { "b", "22" }, { "c", "23" }, {"d", "25"} }, 249 { { "a", "29" }, { "b", "12" }, { "c", "13" }, {"d", "15"} }, 250 { { "a", "39" }, { "b", "22" }, { "c", "23" }, {"d", "25"} }, 251 { { "a", "54" }, { "b", "37" }, { "c", "38" }, {"d", "41"} }, 252 { { "a", "54" }, { "b", "37" }, { "c", "38" }, {"d", "42"} }, 253 { { "a", "19" }, { "b", "2" }, { "c", "3" } }, 254 { { "a", "54" }, { "b", "37" }, { "c", "38" }, {"d", "42"} }, 255 { { "a", "76" }, { "b", "60" }, { "c", "62" }, {"d", "1"}, {"e", "77"} }, 256 { { "a", "75" }, { "b", "58" }, { "c", "59" }, {"d", "1"}, {"e", "1"} }, 257 { { "a", "75" }, { "b", "58" }, { "c", "59" }, {"d", "1"} }, 258 { { "a", "75" }, { "b", "58" }, { "c", "59" } }, 259 { { "a", "76" }, { "b", "60" }, { "c", "62" }, {"d", "1"}, {"e", "77"} }, 260 { { "a", "81" }, { "b", "65" }, { "c", "67" }, {"d", "5"} }, 261 { { "a", "76" }, { "b", "60" }, { "c", "62" }, {"d", "5"} }, 262 { { "a", "75" }, { "b", "58" }, { "c", "59" } }, 263 { { "a", "81" }, { "b", "65" }, { "c", "67" }, {"d", "5"} }, 264 { { "a", "87" }, { "b", "71" }, { "c", "73" }, {"d", "6"} }, 265 { { "a", "81" }, { "b", "65" }, { "c", "67" }, {"d", "6"} }, 266 { { "a", "87" }, { "b", "71" }, { "c", "73" }, {"d", "6"} }, 267 { { "a", "88" }, { "b", "73" }, { "c", "76" }, {"d", "7"}, {"e", "84"} }, 268 { { "a", "87" }, { "b", "71" }, { "c", "73" }, {"d", "7"}, {"e", "1"} }, 269 { { "a", "87" }, { "b", "71" }, { "c", "73" }, {"d", "7"} }, 270 { { "a", "75" }, { "b", "58" }, { "c", "59" } }, 271 { { "a", "88" }, { "b", "73" }, { "c", "76" }, {"d", "7"}, {"e", "84"} }, 272 { { "a", "88" }, { "b", "73" }, { "c", "76" }, {"d", "11"} }, 273 { { "a", "75" }, { "b", "58" }, { "c", "59" } }, 274 { { "a", "88" }, { "b", "73" }, { "c", "76" }, {"d", "11"} }, 275 { { "a", "6" }, { "b", "13" }, { "c", "3" }, {"d", "11"}, {"e", "11"} }, 276 { { "a", "1" }, { "b", "2" }, { "c", "3" }, {"d", "10"}, {"e", "1"} }, 277 { { "a", "1" }, { "b", "2" }, { "c", "3" }, {"d", "10"} }, 278 { { "a", "1" }, { "b", "2" }, { "c", "3" }, {"d", "10"} }, 279 { { "a", "1" }, { "b", "2" }, { "c", "3" } }, 280 { { "a", "6" }, { "b", "13" }, { "c", "3" }, {"d", "11"}, {"e", "11"} }, 281 { { "a", "7" }, { "b", "14" }, { "c", "14" }, {"d", "11"} }, 282 { { "a", "1" }, { "b", "2" }, { "c", "3" }, {"d", "10"} }, 283 { { "a", "1" }, { "b", "2" }, { "c", "3" } }, 284 { { "a", "7" }, { "b", "14" }, { "c", "14" }, {"d", "11"} }, 285 { { "a", "8" }, { "b", "16" }, { "c", "18" }, {"d", "11"} }, 286 { { "a", "7" }, { "b", "14" }, { "c", "15" }, {"d", "11"} }, 287 { { "a", "1" }, { "b", "2" }, { "c", "3" } }, 288 { { "a", "8" }, { "b", "16" }, { "c", "18" }, {"d", "11"} }, 289 { { "a", "8" }, { "b", "16" }, { "c", "18" }, {"d", "19"} }, 290 { { "a", "1" }, { "b", "2" }, { "c", "3" } }, 291 { { "a", "8" }, { "b", "16" }, { "c", "18" }, {"d", "19"} } 292 }; 293 294 int32_t index_ {0}; 295 const EcmaVM *vm_ {nullptr}; 296 RuntimeImpl *runtime_ {nullptr}; 297 size_t checkVariableCounter_ {0}; 298 }; 299 300 enum breakpointOpType { 301 RESUME = 1, 302 DROPLASTFRAME = 2 303 }; 304 305 static constexpr size_t LINE_COLUMN = 2; 306 static constexpr size_t POINTER_SIZE = 17; 307 308 std::string pandaFile_ = DEBUGGER_ABC_DIR "dropframe.abc"; 309 std::string sourceFile_ = DEBUGGER_JS_DIR "dropframe.js"; 310 std::string entryPoint_ = "_GLOBAL::func_main_0"; 311 size_t breakpointCounter_ = 0; 312 size_t dropframeCounter_ = 0; 313 bool dropFrameChecked_ = false; 314 std::vector<JSPtLocation> pointerLocations_; 315 size_t breakpointHitTimes[POINTER_SIZE] = {2, 2, 4, 6, 6, 4, 5, 10, 2, 2, 4, 2, 2, 2, 2, 2, 2}; 316 std::queue<std::pair<uint8_t, uint8_t>> breakpointOp; 317 318 void InitBreakpointOpQueue() 319 { 320 breakpointOp.push({1, breakpointOpType::DROPLASTFRAME}); 321 breakpointOp.push({1, breakpointOpType::DROPLASTFRAME}); 322 breakpointOp.push({1, breakpointOpType::DROPLASTFRAME}); 323 breakpointOp.push({3, breakpointOpType::DROPLASTFRAME}); 324 breakpointOp.push({3, breakpointOpType::DROPLASTFRAME}); 325 breakpointOp.push({3, breakpointOpType::DROPLASTFRAME}); 326 breakpointOp.push({5, breakpointOpType::DROPLASTFRAME}); 327 breakpointOp.push({7, breakpointOpType::DROPLASTFRAME}); 328 breakpointOp.push({7, breakpointOpType::DROPLASTFRAME}); 329 breakpointOp.push({9, breakpointOpType::DROPLASTFRAME}); 330 breakpointOp.push({13, breakpointOpType::DROPLASTFRAME}); 331 breakpointOp.push({13, breakpointOpType::DROPLASTFRAME}); 332 breakpointOp.push({15, breakpointOpType::DROPLASTFRAME}); 333 breakpointOp.push({15, breakpointOpType::DROPLASTFRAME}); 334 breakpointOp.push({19, breakpointOpType::DROPLASTFRAME}); 335 breakpointOp.push({21, breakpointOpType::DROPLASTFRAME}); 336 breakpointOp.push({21, breakpointOpType::DROPLASTFRAME}); 337 breakpointOp.push({21, breakpointOpType::DROPLASTFRAME}); 338 breakpointOp.push({21, breakpointOpType::DROPLASTFRAME}); 339 breakpointOp.push({23, breakpointOpType::DROPLASTFRAME}); 340 breakpointOp.push({25, breakpointOpType::DROPLASTFRAME}); 341 breakpointOp.push({25, breakpointOpType::DROPLASTFRAME}); 342 breakpointOp.push({28, breakpointOpType::DROPLASTFRAME}); 343 breakpointOp.push({30, breakpointOpType::DROPLASTFRAME}); 344 breakpointOp.push({32, breakpointOpType::DROPLASTFRAME}); 345 breakpointOp.push({34, breakpointOpType::DROPLASTFRAME}); 346 breakpointOp.push({37, breakpointOpType::DROPLASTFRAME}); 347 breakpointOp.push({40, breakpointOpType::DROPLASTFRAME}); 348 breakpointOp.push({42, breakpointOpType::DROPLASTFRAME}); 349 breakpointOp.push({42, breakpointOpType::DROPLASTFRAME}); 350 breakpointOp.push({42, breakpointOpType::DROPLASTFRAME}); 351 breakpointOp.push({44, breakpointOpType::DROPLASTFRAME}); 352 breakpointOp.push({44, breakpointOpType::DROPLASTFRAME}); 353 breakpointOp.push({46, breakpointOpType::DROPLASTFRAME}); 354 breakpointOp.push({48, breakpointOpType::DROPLASTFRAME}); 355 breakpointOp.push({48, breakpointOpType::DROPLASTFRAME}); 356 breakpointOp.push({48, breakpointOpType::DROPLASTFRAME}); 357 breakpointOp.push({50, breakpointOpType::DROPLASTFRAME}); 358 breakpointOp.push({52, breakpointOpType::DROPLASTFRAME}); 359 breakpointOp.push({52, breakpointOpType::DROPLASTFRAME}); 360 breakpointOp.push({52, breakpointOpType::DROPLASTFRAME}); 361 breakpointOp.push({52, breakpointOpType::DROPLASTFRAME}); 362 breakpointOp.push({54, breakpointOpType::DROPLASTFRAME}); 363 breakpointOp.push({54, breakpointOpType::DROPLASTFRAME}); 364 breakpointOp.push({56, breakpointOpType::DROPLASTFRAME}); 365 breakpointOp.push({56, breakpointOpType::DROPLASTFRAME}); 366 breakpointOp.push({58, breakpointOpType::DROPLASTFRAME}); 367 } 368 369 void DropFrameIfChecked() 370 { 371 while (!dropFrameChecked_ && !breakpointOp.empty() && breakpointOp.front().first == breakpointCounter_) { 372 if (breakpointOp.front().second == breakpointOpType::DROPLASTFRAME) { 373 PtJson paramsJson; 374 paramsJson.Add("droppedDepth", 1U); 375 std::unique_ptr<DropFrameParams> params = DropFrameParams::Create(paramsJson); 376 debugger_->DropFrame(*params); 377 dropFrameChecked_ = true; 378 } 379 breakpointOp.pop(); 380 } 381 } 382 383 void SetJSPtLocation(size_t *arr, size_t number, std::vector<JSPtLocation> &locations) 384 { 385 for (size_t i = 0; i < number; i++) { 386 JSPtLocation location = TestUtil::GetLocation(sourceFile_.c_str(), arr[i * LINE_COLUMN], 387 arr[i * LINE_COLUMN + 1], pandaFile_.c_str()); 388 locations.push_back(location); 389 } 390 }; 391}; 392 393std::unique_ptr<TestEvents> GetJsDropFrameTest() 394{ 395 return std::make_unique<JsDropFrameTest>(); 396} 397} // namespace panda::ecmascript::tooling::test 398 399#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_DROP_FRAME_TEST_H 400