BindingsTestRunner.cleanupURL=function(url){if(!url.startsWith('debugger://')){return url;}
return url.replace(/VM\d+/g,'VM[XXX]');};BindingsTestRunner.dumpWorkspace=function(previousSnapshot){const uiSourceCodes=self.Workspace.workspace.uiSourceCodes().slice();let urls=uiSourceCodes.map(code=>code.url());urls=urls.map(BindingsTestRunner.cleanupURL);urls.sort(String.caseInsensetiveComparator);const isAdded=new Array(urls.length).fill(false);let removedLines=[];if(previousSnapshot){const diff=Diff.Diff.lineDiff(previousSnapshot,urls);const removedEntries=diff.filter(entry=>entry[0]===Diff.Diff.Operation.Delete).map(entry=>entry[1]);removedLines=[].concat.apply([],removedEntries);let index=0;for(const entry of diff){if(entry[0]===Diff.Diff.Operation.Delete){continue;}
if(entry[0]===Diff.Diff.Operation.Equal){index+=entry[1].length;continue;}
for(const line of entry[1]){isAdded[index++]=true;}}
const addedEntries=diff.filter(entry=>entry[0]===Diff.Diff.Operation.Insert).map(entry=>entry[1]);addedLines=[].concat.apply([],addedEntries);}
TestRunner.addResult(`Removed: ${removedLines.length} uiSourceCodes`);for(const url of removedLines){TestRunner.addResult('[-] '+url);}
TestRunner.addResult(`Workspace: ${urls.length} uiSourceCodes.`);for(let i=0;i<urls.length;++i){const url=urls[i];const prefix=(isAdded[i]?'[+] ':'    ');TestRunner.addResult(prefix+url);}
return urls;};BindingsTestRunner.attachFrame=function(frameId,url,evalSourceURL){let evalSource=`(${attachFrame.toString()})('${frameId}', '${url}')`;if(evalSourceURL){evalSource+='//# sourceURL='+evalSourceURL;}
return TestRunner.evaluateInPageAsync(evalSource);function attachFrame(frameId,url){const frame=document.createElement('iframe');frame.src=url;frame.id=frameId;document.body.appendChild(frame);return new Promise(x=>frame.onload=x);}};BindingsTestRunner.detachFrame=function(frameId,evalSourceURL){let evalSource=`(${detachFrame.toString()})('${frameId}')`;if(evalSourceURL){evalSource+='//# sourceURL='+evalSourceURL;}
return TestRunner.evaluateInPageAnonymously(evalSource);function detachFrame(frameId){const frame=document.getElementById(frameId);frame.remove();}};BindingsTestRunner.navigateFrame=function(frameId,navigateURL,evalSourceURL){let evalSource=`(${navigateFrame.toString()})('${frameId}', '${navigateURL}')`;if(evalSourceURL){evalSource+='//# sourceURL='+evalSourceURL;}
return TestRunner.evaluateInPageAsync(evalSource);function navigateFrame(frameId,url){const frame=document.getElementById(frameId);frame.src=url;return new Promise(x=>frame.onload=x);}};BindingsTestRunner.attachShadowDOM=function(id,templateSelector,evalSourceURL){let evalSource=`(${createShadowDOM.toString()})('${id}', '${templateSelector}')`;if(evalSourceURL){evalSource+='//# sourceURL='+evalSourceURL;}
return TestRunner.evaluateInPageAnonymously(evalSource);function createShadowDOM(id,templateSelector){const shadowHost=document.createElement('div');shadowHost.setAttribute('id',id);const shadowRoot=shadowHost.attachShadow({mode:'open'});const t=document.querySelector(templateSelector);const instance=t.content.cloneNode(true);shadowRoot.appendChild(instance);document.body.appendChild(shadowHost);}};BindingsTestRunner.detachShadowDOM=function(id,evalSourceURL){let evalSource=`(${removeShadowDOM.toString()})('${id}')`;if(evalSourceURL){evalSource+='//# sourceURL='+evalSourceURL;}
return TestRunner.evaluateInPageAnonymously(evalSource);function removeShadowDOM(id){document.querySelector('#'+id).remove();}};BindingsTestRunner.waitForStyleSheetRemoved=function(urlSuffix){let fulfill;const promise=new Promise(x=>fulfill=x);TestRunner.cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetRemoved,onStyleSheetRemoved);return promise;function onStyleSheetRemoved(event){const styleSheetHeader=event.data;if(!styleSheetHeader.resourceURL().endsWith(urlSuffix)){return;}
TestRunner.cssModel.removeEventListener(SDK.CSSModel.Events.StyleSheetRemoved,onStyleSheetRemoved);fulfill();}};TestRunner.addSniffer(Bindings.CompilerScriptMapping.prototype,'_sourceMapAttachedForTest',onSourceMap,true);TestRunner.addSniffer(Bindings.SASSSourceMapping.prototype,'_sourceMapAttachedForTest',onSourceMap,true);const sourceMapCallbacks=new Map();function onSourceMap(sourceMap){for(const urlSuffix of sourceMapCallbacks.keys()){if(sourceMap.url().endsWith(urlSuffix)){const callback=sourceMapCallbacks.get(urlSuffix);callback.call(null);sourceMapCallbacks.delete(urlSuffix);}}}
BindingsTestRunner.waitForSourceMap=function(sourceMapURLSuffix){let fulfill;const promise=new Promise(x=>fulfill=x);sourceMapCallbacks.set(sourceMapURLSuffix,fulfill);return promise;};const locationPool=new Bindings.LiveLocationPool();const nameSymbol=Symbol('LiveLocationNameForTest');const createdSymbol=Symbol('LiveLocationCreated');BindingsTestRunner.createDebuggerLiveLocation=function(name,urlSuffix,lineNumber,columnNumber){const script=TestRunner.debuggerModel.scripts().find(script=>script.sourceURL.endsWith(urlSuffix));const rawLocation=TestRunner.debuggerModel.createRawLocation(script,lineNumber||0,columnNumber||0);return self.Bindings.debuggerWorkspaceBinding.createLiveLocation(rawLocation,updateDelegate.bind(null,name),locationPool);};BindingsTestRunner.createCSSLiveLocation=function(name,urlSuffix,lineNumber,columnNumber){const header=TestRunner.cssModel.styleSheetHeaders().find(header=>header.resourceURL().endsWith(urlSuffix));const rawLocation=new SDK.CSSLocation(header,lineNumber||0,columnNumber||0);return self.Bindings.cssWorkspaceBinding.createLiveLocation(rawLocation,updateDelegate.bind(null,name),locationPool);};function updateDelegate(name,liveLocation){liveLocation[nameSymbol]=name;const hint=(liveLocation[createdSymbol]?'[ UPDATE ]':'[ CREATE ]');liveLocation[createdSymbol]=true;BindingsTestRunner.dumpLocation(liveLocation,hint);}
BindingsTestRunner.dumpLocation=function(liveLocation,hint){hint=hint||'[  GET   ]';const prefix=`${hint}  LiveLocation-${liveLocation[nameSymbol]}: `;const uiLocation=liveLocation.uiLocation();if(!uiLocation){TestRunner.addResult(prefix+'null');return;}
TestRunner.addResult(prefix+BindingsTestRunner.cleanupURL(uiLocation.uiSourceCode.url())+':'+uiLocation.lineNumber+':'+
uiLocation.columnNumber);};;Host.InspectorFrontendHost.isolatedFileSystem=function(name){return BindingsTestRunner.TestFileSystem._instances[name];};BindingsTestRunner.TestFileSystem=function(fileSystemPath){this.root=new BindingsTestRunner.TestFileSystem.Entry(this,'',true,null);this.fileSystemPath=fileSystemPath;};BindingsTestRunner.TestFileSystem._instances={};BindingsTestRunner.TestFileSystem.prototype={dumpAsText:function(){const result=[];dfs(this.root,'');result[0]=this.fileSystemPath;return result.join('\n');function dfs(node,indent){result.push(indent+node.name);const newIndent=indent+'    ';for(const child of node._children){dfs(child,newIndent);}}},reportCreatedPromise:function(type){return new Promise(fulfill=>this.reportCreated(fulfill,type));},reportCreated:function(callback,type){const fileSystemPath=this.fileSystemPath;BindingsTestRunner.TestFileSystem._instances[this.fileSystemPath]=this;Host.InspectorFrontendHost.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.FileSystemAdded,{fileSystem:{fileSystemPath:this.fileSystemPath,fileSystemName:this.fileSystemPath,type}});self.Persistence.isolatedFileSystemManager.addEventListener(Persistence.IsolatedFileSystemManager.Events.FileSystemAdded,created);function created(event){const fileSystem=event.data;if(fileSystem.path()!==fileSystemPath){return;}
self.Persistence.isolatedFileSystemManager.removeEventListener(Persistence.IsolatedFileSystemManager.Events.FileSystemAdded,created);callback(fileSystem);}},reportRemoved:function(){delete BindingsTestRunner.TestFileSystem._instances[this.fileSystemPath];Host.InspectorFrontendHost.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.FileSystemRemoved,this.fileSystemPath);},addFile:function(path,content,lastModified){const pathTokens=path.split('/');let node=this.root;const folders=pathTokens.slice(0,pathTokens.length-1);const fileName=pathTokens.peekLast();for(const folder of folders){let dir=node._childrenMap[folder];if(!dir){dir=node.mkdir(folder);}
node=dir;}
const file=node.addFile(fileName,content);if(lastModified){file._timestamp=lastModified;}
return file;}};BindingsTestRunner.TestFileSystem.Entry=function(fileSystem,name,isDirectory,parent){this._fileSystem=fileSystem;this.name=name;this._children=[];this._childrenMap={};this.isDirectory=isDirectory;this._timestamp=1000000;this._parent=parent;};BindingsTestRunner.TestFileSystem.Entry.prototype={get fullPath(){return(this.parent?this.parent.fullPath+'/'+this.name:'');},remove:function(success,failure){this._parent._removeChild(this,success,failure);},_removeChild:function(child,success,failure){const index=this._children.indexOf(child);if(index===-1){failure('Failed to remove file: file not found.');return;}
const fullPath=this._fileSystem.fileSystemPath+child.fullPath;this._children.splice(index,1);delete this._childrenMap[child.name];child.parent=null;Host.InspectorFrontendHost.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,{changed:[],added:[],removed:[fullPath]});success();},mkdir:function(name){const child=new BindingsTestRunner.TestFileSystem.Entry(this._fileSystem,name,true,this);this._childrenMap[name]=child;this._children.push(child);child.parent=this;return child;},addFile:function(name,content){const child=new BindingsTestRunner.TestFileSystem.Entry(this._fileSystem,name,false,this);this._childrenMap[name]=child;this._children.push(child);child.parent=this;child.content=new Blob([content],{type:'text/plain'});const fullPath=this._fileSystem.fileSystemPath+child.fullPath;Host.InspectorFrontendHost.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,{changed:[],added:[fullPath],removed:[]});return child;},setContent:function(content){this.content=new Blob([content],{type:'text/plain'});this._timestamp+=1000;const fullPath=this._fileSystem.fileSystemPath+this.fullPath;Host.InspectorFrontendHost.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,{changed:[fullPath],added:[],removed:[]});},createReader:function(){return new BindingsTestRunner.TestFileSystem.Reader(this._children);},createWriter:function(success,failure){success(new BindingsTestRunner.TestFileSystem.Writer(this));},file:function(callback){callback(this.content);},getDirectory:function(path,noop,callback,errorCallback){this.getEntry(path,noop,callback,errorCallback);},getFile:function(path,noop,callback,errorCallback){this.getEntry(path,noop,callback,errorCallback);},_createEntry:function(path,options,callback,errorCallback){const tokens=path.split('/');const name=tokens.pop();let parentEntry=this;for(const token of tokens){parentEntry=parentEntry._childrenMap[token];}
let entry=parentEntry._childrenMap[name];if(entry&&options.exclusive){errorCallback(new DOMException('File exists: '+path,'InvalidModificationError'));return;}
if(!entry){entry=parentEntry.addFile(name,'');}
callback(entry);},getEntry:function(path,options,callback,errorCallback){if(path.startsWith('/')){path=path.substring(1);}
if(options&&options.create){this._createEntry(path,options,callback,errorCallback);return;}
if(!path){callback(this);return;}
let entry=this;for(const token of path.split('/')){entry=entry._childrenMap[token];if(!entry){break;}}
(entry?callback(entry):errorCallback(new DOMException('Path not found: '+path,'NotFoundError')));},getMetadata:function(success,failure){success({modificationTime:new Date(this._timestamp),size:(this.isDirectory?0:this.content.size)});},moveTo:function(parent,newName,callback,errorCallback){this._parent._children.remove(this);delete this._parent._childrenMap[this.name];this._parent=parent;this._parent._children.push(this);this.name=newName;this._parent._childrenMap[this.name]=this;callback(this);},getParent:function(callback,errorCallback){callback(this._parent);}};BindingsTestRunner.TestFileSystem.Reader=function(children){this._children=children;};BindingsTestRunner.TestFileSystem.Reader.prototype={readEntries:function(callback){const children=this._children;this._children=[];callback(children);}};BindingsTestRunner.TestFileSystem.Writer=function(entry){this._entry=entry;this._modificationTimesDelta=500;};BindingsTestRunner.TestFileSystem.Writer.prototype={write:function(blob){this._entry._timestamp+=this._modificationTimesDelta;this._entry.content=blob;if(this.onwriteend){this.onwriteend();}},truncate:function(num){this._entry._timestamp+=this._modificationTimesDelta;this._entry.content=this._entry.content.slice(0,num);if(this.onwriteend){this.onwriteend();}}};;BindingsTestRunner.addFiles=function(testFileSystem,files){for(const filePath in files){const file=files[filePath];testFileSystem.addFile(filePath,file.content,(file.time?file.time.getTime():0));}};let timeOverrides;let originalRequestMetadata;BindingsTestRunner.overrideNetworkModificationTime=function(urlToTime){if(!timeOverrides){timeOverrides=new Map();originalRequestMetadata=TestRunner.override(Bindings.ContentProviderBasedProject.prototype,'requestMetadata',overrideTime,true);}
for(const url in urlToTime){timeOverrides.set(url,urlToTime[url]);}
function overrideTime(uiSourceCode){if(!timeOverrides.has(uiSourceCode.url())){return originalRequestMetadata.call(this,uiSourceCode);}
const override=timeOverrides.get(uiSourceCode.url());return originalRequestMetadata.call(this,uiSourceCode).then(onOriginalMetadata.bind(null,override));}
function onOriginalMetadata(timeOverride,metadata){if(!timeOverride&&!metadata){return null;}
return new Workspace.UISourceCodeMetadata(timeOverride,(metadata?metadata.contentSize:null));}};BindingsTestRunner.AutomappingTest=function(workspace){this._workspace=workspace;this._networkProject=new Bindings.ContentProviderBasedProject(this._workspace,'AUTOMAPPING',Workspace.projectTypes.Network,'simple website');if(workspace!==self.Workspace.workspace){new Persistence.FileSystemWorkspaceBinding(self.Persistence.isolatedFileSystemManager,this._workspace);}
this._failedBindingsCount=0;this._automapping=new Persistence.Automapping(this._workspace,this._onStatusAdded.bind(this),this._onStatusRemoved.bind(this));TestRunner.addSniffer(this._automapping,'_onBindingFailedForTest',this._onBindingFailed.bind(this),true);TestRunner.addSniffer(this._automapping,'_onSweepHappenedForTest',this._onSweepHappened.bind(this),true);};BindingsTestRunner.AutomappingTest.prototype={removeResources:function(urls){for(const url of urls){this._networkProject.removeFile(url);}},addNetworkResources:function(assets){for(const url in assets){const asset=assets[url];const contentType=asset.contentType||Common.resourceTypes.Script;const contentProvider=Common.StaticContentProvider.fromString(url,contentType,asset.content);const metadata=(typeof asset.content==='string'||asset.time?new Workspace.UISourceCodeMetadata(asset.time,asset.content.length):null);const uiSourceCode=this._networkProject.createUISourceCode(url,contentType);this._networkProject.addUISourceCodeWithProvider(uiSourceCode,contentProvider,metadata);}},waitUntilMappingIsStabilized:function(){const promise=new Promise(x=>this._stabilizedCallback=x);this._checkStabilized();return promise;},_onSweepHappened:function(){this._failedBindingsCount=0;this._checkStabilized();},_onStatusRemoved:function(status){TestRunner.addResult('Binding removed: '+status);this._checkStabilized();},_onStatusAdded:function(status){TestRunner.addResult('Binding created: '+status);this._checkStabilized();},_onBindingFailed:function(){++this._failedBindingsCount;this._checkStabilized();},_checkStabilized:function(){if(!this._stabilizedCallback||this._automapping._sweepThrottler._process){return;}
const networkUISourceCodes=this._workspace.uiSourceCodesForProjectType(Workspace.projectTypes.Network);const stabilized=this._failedBindingsCount+this._automapping._statuses.size===networkUISourceCodes.length;if(stabilized){TestRunner.addResult('Mapping has stabilized.');const callback=this._stabilizedCallback;delete this._stabilizedCallback;callback.call(null);}}};;Persistence.PersistenceBinding.prototype.toString=function(){const lines=['{','       network: '+this.network.url(),'    fileSystem: '+this.fileSystem.url(),'}'];return lines.join('\n');};Persistence.AutomappingStatus.prototype.toString=function(){const lines=['{','       network: '+this.network.url(),'    fileSystem: '+this.fileSystem.url(),'    exactMatch: '+this.exactMatch,'}'];return lines.join('\n');};BindingsTestRunner.waitForBinding=function(fileName){const uiSourceCodes=self.Workspace.workspace.uiSourceCodes();for(const uiSourceCode of uiSourceCodes){const binding=self.Persistence.persistence.binding(uiSourceCode);if(!binding){continue;}
if(uiSourceCode.name()===fileName){return Promise.resolve(binding);}}
return TestRunner.waitForEvent(Persistence.Persistence.Events.BindingCreated,self.Persistence.persistence,binding=>binding.network.name()===fileName||binding.fileSystem.name()===fileName);};BindingsTestRunner.addFooJSFile=function(fs){return fs.root.mkdir('devtools').mkdir('persistence').mkdir('resources').addFile('foo.js','\n\nwindow.foo = ()=>\'foo\';\n');};BindingsTestRunner.initializeTestMapping=function(){return new TestMapping(self.Persistence.persistence);};class TestMapping{constructor(persistence){this._persistence=persistence;persistence.addNetworkInterceptor(()=>true);this._bindings=new Set();}
async addBinding(urlSuffix){if(this._findBinding(urlSuffix)){TestRunner.addResult(`FAILED TO ADD BINDING: binding already exists for ${urlSuffix}`);TestRunner.completeTest();return;}
const networkUISourceCode=await TestRunner.waitForUISourceCode(urlSuffix,Workspace.projectTypes.Network);const fileSystemUISourceCode=await TestRunner.waitForUISourceCode(urlSuffix,Workspace.projectTypes.FileSystem);const binding=new Persistence.PersistenceBinding(networkUISourceCode,fileSystemUISourceCode);this._bindings.add(binding);await this._persistence.addBindingForTest(binding);}
_findBinding(urlSuffix){for(const binding of this._bindings){if(binding.network.url().endsWith(urlSuffix)){return binding;}}
return null;}
async removeBinding(urlSuffix){const binding=this._findBinding(urlSuffix);if(!binding){TestRunner.addResult(`FAILED TO REMOVE BINDING: binding does not exist for ${urlSuffix}`);TestRunner.completeTest();return;}
this._bindings.delete(binding);await this._persistence.removeBindingForTest(binding);}
async dispose(){for(const binding of this._bindings){await this._persistence.removeBindingForTest(binding);}
this._bindings.clear();}};BindingsTestRunner.createOverrideProject=async function(folderPath){const testFileSystem=new BindingsTestRunner.TestFileSystem(folderPath);const isolatedFileSystem=await testFileSystem.reportCreatedPromise('overrides');isolatedFileSystem._type='overrides';const project=self.Workspace.workspace.project(Persistence.FileSystemWorkspaceBinding.projectId(isolatedFileSystem.path()));console.assert(project);return{isolatedFileSystem,project,testFileSystem};};BindingsTestRunner.setOverridesEnabled=function(enabled){self.Common.settings.moduleSetting('persistenceNetworkOverridesEnabled').set(enabled);};;