From 59496b7ebaf0a2489083cae99a02a2ea4a497f03 Mon Sep 17 00:00:00 2001 From: Shay Dratler Date: Wed, 29 May 2019 22:07:00 +0300 Subject: [PATCH 1/4] New Feature Major : + Adding to DevTools Profiler Minor : + Adding extractLong and extractInstant to JsonInputConverter --- .../selenium/devtools/profiler/Profiler.java | 163 +++++++++++++++++ .../model/ConsoleProfileFinished.java | 105 +++++++++++ .../profiler/model/ConsoleProfileStarted.java | 88 +++++++++ .../profiler/model/CoverageRange.java | 96 ++++++++++ .../profiler/model/FunctionCoverage.java | 106 +++++++++++ .../devtools/profiler/model/Location.java | 92 ++++++++++ .../profiler/model/PositionTickInfo.java | 81 +++++++++ .../devtools/profiler/model/Profile.java | 161 +++++++++++++++++ .../devtools/profiler/model/ProfileNode.java | 170 ++++++++++++++++++ .../profiler/model/ScriptCoverage.java | 118 ++++++++++++ .../profiler/model/ScriptTypeProfile.java | 108 +++++++++++ .../devtools/profiler/model/TypeObject.java | 51 ++++++ .../profiler/model/TypeProfileEntry.java | 90 ++++++++++ .../selenium/json/JsonInputConverter.java | 12 +- .../devtools/ChromeDevToolsProfilerTest.java | 129 +++++++++++++ 15 files changed, 1569 insertions(+), 1 deletion(-) create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/Profiler.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileFinished.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileStarted.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/FunctionCoverage.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptCoverage.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptTypeProfile.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/TypeObject.java create mode 100644 java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java create mode 100644 java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/Profiler.java b/java/client/src/org/openqa/selenium/devtools/profiler/Profiler.java new file mode 100644 index 0000000000000..897a028dc5180 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/Profiler.java @@ -0,0 +1,163 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler; + +import static org.openqa.selenium.devtools.ConverterFunctions.map; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.reflect.TypeToken; +import java.util.List; +import java.util.Optional; +import org.openqa.selenium.Beta; +import org.openqa.selenium.devtools.Command; +import org.openqa.selenium.devtools.Event; +import org.openqa.selenium.devtools.profiler.model.ConsoleProfileFinished; +import org.openqa.selenium.devtools.profiler.model.ConsoleProfileStarted; +import org.openqa.selenium.devtools.profiler.model.Profile; +import org.openqa.selenium.devtools.profiler.model.ScriptCoverage; +import org.openqa.selenium.devtools.profiler.model.ScriptTypeProfile; + +public class Profiler { + + /** + * Disable Profiling + */ + public static Command disable() { + return new Command<>("Profiler.disable", ImmutableMap.of()); + } + + /** + * Enable Profiling + */ + public static Command enable() { + return new Command<>("Profiler.enable", ImmutableMap.of()); + } + + /** + * start Profiling process + */ + public static Command start() { + return new Command<>("Profiler.start", ImmutableMap.of()); + } + + /** + * stop Profiling process + * + * @return {@link Profile} with sampled values + */ + public static Command stop() { + return new Command<>("Profiler.stop", ImmutableMap.of(), map("profile", Profile.class)); + } + + /** + * Collect coverage data for the current isolate. The coverage data may be incomplete due to garbage collection. + * + * @return Array {@link ScriptCoverage} + */ + public static Command> getBestEffortCoverage() { + return new Command<>( + "Profiler.getBestEffortCoverage", ImmutableMap.of(), map("result", new TypeToken>() { + }.getType())); + } + + /** + * Changes CPU profiler sampling interval. Must be called before CPU profiles recording started. + * + * @param interval - New sampling interval in microseconds. + */ + public static Command setSamplingInterval(int interval) { + return new Command<>("Profiler.setSamplingInterval", ImmutableMap.of("interval", interval)); + } + + /** + * Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code coverage may be + * incomplete. Enabling prevents running optimized code and resets execution counters. + * + * @param callCount - Collect accurate call counts beyond simple 'covered' or 'not covered'. + * @param detailed - Collect block-based coverage. + */ + public static Command startPreciseCoverage( + Optional callCount, Optional detailed) { + Builder mapBuilder = ImmutableMap.builder(); + callCount.ifPresent(value -> mapBuilder.put("callCount", value)); + detailed.ifPresent(value -> mapBuilder.put("detailed", value)); + return new Command<>("Profiler.startPreciseCoverage", mapBuilder.build()); + } + + /** + * Enable type profile + */ + @Beta + public static Command startTypeProfile() { + return new Command<>("Profiler.startTypeProfile", ImmutableMap.of()); + } + + /** + * Disable precise code coverage. Disabling releases unnecessary execution count records and allows executing + * optimized code. + */ + public static Command stopPreciseCoverage() { + return new Command("Profiler.stopPreciseCoverage", ImmutableMap.of()); + } + + /** + * Disable type profile. Disabling releases type profile data collected so far.EXPERIMENTAL + */ + @Beta + public static Command stopTypeProfile() { + return new Command<>("Profiler.stopTypeProfile", ImmutableMap.of()); + } + + /** + * Collect coverage data for the current isolate, and resets execution counters. Precise code coverage needs to have + * started. + */ + public static Command> takePreciseCoverage() { + return new Command<>( + "Profiler.takePreciseCoverage", + ImmutableMap.of(), + map("result", new TypeToken>() { + }.getType())); + } + + /** + * Collect type profile.EXPERIMENTAL + * + * @return - Type profile for all scripts since startTypeProfile() was turned on. + */ + @Beta + public static Command> takeTypeProfile() { + return new Command<>( + "Profiler.takeTypeProfile", + ImmutableMap.of(), + map("result", new TypeToken>() { + }.getType())); + } + + public static Event consoleProfileFinished() { + return new Event<>("Profiler.consoleProfileFinished", map("id", ConsoleProfileFinished.class)); + } + + /** + * Sent when new profile recording is started using console.profile() call. + */ + public static Event consoleProfileStarted() { + return new Event<>("Profiler.consoleProfileStarted", map("id", ConsoleProfileStarted.class)); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileFinished.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileFinished.java new file mode 100644 index 0000000000000..eac3eb34b7f39 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileFinished.java @@ -0,0 +1,105 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import java.util.Objects; +import org.openqa.selenium.json.JsonInput; + +public class ConsoleProfileFinished { + + private String id; + /** + * Location of console.profileEnd(). + */ + private Location location; + + private Profile profile; + + /** + * Profile title passed as an argument to console.profile(). + * + *

Optional + */ + private String title; + + public ConsoleProfileFinished(String id, Location location, Profile profile, String title) { + this.setId(id); + this.setLocation(location); + this.setProfile(profile); + this.setTitle(title); + } + + public static ConsoleProfileFinished fromJson(JsonInput input) { + String id = input.nextString(); + Location location = null; + Profile profile = null; + String title = null; + while (input.hasNext()) { + switch (input.nextName()) { + case "location": + location = Location.fronJson(input); + break; + case "profile": + profile = Profile.fromJson(input); + break; + case "title": + title = input.nextString(); + break; + default: + input.skipValue(); + break; + } + } + return new ConsoleProfileFinished(id, location, profile, title); + } + + public String getId() { + return id; + } + + public void setId(String id) { + Objects.requireNonNull(id, "id is require"); + this.id = id; + } + + public Location getLocation() { + return location; + } + + public void setLocation(Location location) { + Objects.requireNonNull(location, "location is require"); + this.location = location; + } + + public Profile getProfile() { + return profile; + } + + public void setProfile(Profile profile) { + Objects.requireNonNull(profile, "profile is require"); + this.profile = profile; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileStarted.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileStarted.java new file mode 100644 index 0000000000000..bc9f39e61b017 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileStarted.java @@ -0,0 +1,88 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import java.util.Objects; +import org.openqa.selenium.json.JsonInput; + +public class ConsoleProfileStarted { + + private String id; + /** + * Location of console.profileEnd(). + */ + private Location location; + /** + * Profile title passed as an argument to console.profile(). + * + *

Optional + */ + private String title; + + public ConsoleProfileStarted(String id, Location location, String title) { + this.setId(id); + this.setLocation(location); + this.setTitle(title); + } + + public static ConsoleProfileStarted fromJson(JsonInput input) { + String id = input.nextString(); + Location location = null; + String title = null; + while (input.hasNext()) { + switch (input.nextName()) { + case "location": + location = Location.fronJson(input); + break; + case "title": + title = input.nextString(); + break; + default: + input.skipValue(); + break; + } + } + return new ConsoleProfileStarted(id, location, title); + } + + public String getId() { + return id; + } + + public void setId(String id) { + Objects.requireNonNull(id, "id is require"); + this.id = id; + } + + public Location getLocation() { + return location; + } + + public void setLocation(Location location) { + Objects.requireNonNull(location, "location is require"); + this.location = location; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java new file mode 100644 index 0000000000000..563d564a80176 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java @@ -0,0 +1,96 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import static org.openqa.selenium.json.JsonInputConverter.extractInt; + +import org.openqa.selenium.json.JsonInput; + +/** + * Coverage data for a source range. + */ +public class CoverageRange { + + /** + * JavaScript script source offset for the range start. + */ + private int startOffset; + /** + * JavaScript script source offset for the range end. + */ + private int endOffset; + /** + * Collected execution count of the source range. + */ + private int count; + + public CoverageRange(int startOffset, int endOffset, int count) { + this.setStartOffset(startOffset); + this.setEndOffset(endOffset); + this.setCount(count); + } + + public static CoverageRange fronJson(JsonInput input) { + int startOffset = 0; + int endOffset = 0; + int count = 0; + input.beginObject(); + while (input.hasNext()) { + switch (input.nextName()) { + case "startOffset": + startOffset = extractInt(input); + break; + case "endOffset": + endOffset = extractInt(input); + break; + case "count": + count = extractInt(input); + break; + default: + input.skipValue(); + break; + } + } + input.endObject(); + return new CoverageRange(startOffset, endOffset, count); + } + + public int getStartOffset() { + return startOffset; + } + + public void setStartOffset(int startOffset) { + this.startOffset = startOffset; + } + + public int getEndOffset() { + return endOffset; + } + + public void setEndOffset(int endOffset) { + this.endOffset = endOffset; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/FunctionCoverage.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/FunctionCoverage.java new file mode 100644 index 0000000000000..ea1817c047b66 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/FunctionCoverage.java @@ -0,0 +1,106 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.openqa.selenium.devtools.DevToolsException; +import org.openqa.selenium.json.JsonInput; + +public class FunctionCoverage { + + /** + * JavaScript function name. + */ + private String functionName; + /** + * JavaScript function name. + */ + private List ranges; + /** + * Whether coverage data for this function has block granularity. + */ + private Boolean isBlockCoverage; + + public FunctionCoverage(String functionName, List ranges, boolean isBlockCoverage) { + this.setFunctionName(functionName); + this.setRanges(ranges); + this.setBlockCoverage(isBlockCoverage); + } + + public static FunctionCoverage fromJson(JsonInput input) { + + String functionName = null; + List ranges = null; + Boolean isBlockCoverage = null; + input.beginObject(); + while (input.hasNext()) { + switch (input.nextName()) { + case "functionName": + functionName = input.nextString(); + break; + case "ranges": + ranges = new ArrayList<>(); + input.beginArray(); + while (input.hasNext()) { + ranges.add(CoverageRange.fronJson(input)); + } + input.endArray(); + break; + case "isBlockCoverage": + isBlockCoverage = input.nextBoolean(); + break; + default: + input.skipValue(); + break; + } + } + input.endObject(); + return new FunctionCoverage(functionName, ranges, isBlockCoverage); + } + + public String getFunctionName() { + return functionName; + } + + public void setFunctionName(String functionName) { + Objects.requireNonNull(functionName, "functionName is require"); + this.functionName = functionName; + } + + public List getRanges() { + return ranges; + } + + public void setRanges(List ranges) { + Objects.requireNonNull(ranges, "ranges is require"); + if (ranges.isEmpty()) { + throw new DevToolsException("ranges is require"); + } + this.ranges = ranges; + } + + public boolean isBlockCoverage() { + return isBlockCoverage; + } + + public void setBlockCoverage(Boolean blockCoverage) { + isBlockCoverage = blockCoverage; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java new file mode 100644 index 0000000000000..7859a148a76e5 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java @@ -0,0 +1,92 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import java.util.Objects; +import org.openqa.selenium.json.JsonInput; +import org.openqa.selenium.json.JsonInputConverter; + +/** + * Location in the source code. + */ +public class Location { + + /** + * Script identifier as reported in the Debugger.scriptParsed. + */ + private String scriptId; + /** + * Line number in the script (0-based). + */ + private int lineNumber; + /** + * Column number in the script (0-based). Optional + */ + private Integer columnNumber; + + public Location(String scriptId, int lineNumber, Integer columnNumber) { + this.setScriptId(scriptId); + this.setLineNumber(lineNumber); + this.setColumnNumber(columnNumber); + } + + public static Location fronJson(JsonInput input) { + String scriptId = input.nextString(); + int lineNumber = -1; + Integer columnNumber = null; + while (input.hasNext()) { + switch (input.nextName()) { + case "lineNumber": + lineNumber = JsonInputConverter.extractInt(input); + break; + case "columnNumber": + columnNumber = JsonInputConverter.extractInt(input); + break; + default: + input.skipValue(); + break; + } + } + return new Location(scriptId, lineNumber, columnNumber); + } + + public String getScriptId() { + return scriptId; + } + + public void setScriptId(String scriptId) { + Objects.requireNonNull(scriptId, "scriptId is require"); + this.scriptId = scriptId; + } + + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + public Integer getColumnNumber() { + return columnNumber; + } + + public void setColumnNumber(Integer columnNumber) { + this.columnNumber = columnNumber; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java new file mode 100644 index 0000000000000..632170362b18e --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java @@ -0,0 +1,81 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import java.util.Objects; +import org.openqa.selenium.json.JsonInput; +import org.openqa.selenium.json.JsonInputConverter; + +/** + * Specifies a number of samples attributed to a certain source position. + */ +public class PositionTickInfo { + + /** + * Source line number (1-based). + */ + private int line; + /** + * Number of samples attributed to the source line. + */ + private int ticks; + + public PositionTickInfo(int line, int ticks) { + this.setLine(line); + this.setTicks(ticks); + } + + public static PositionTickInfo fromJson(JsonInput input) { + int line = JsonInputConverter.extractInt(input); + int ticks = 0; + while (input.hasNext()) { + switch (input.nextName()) { + case "ticks": + ticks = JsonInputConverter.extractInt(input); + break; + default: + input.skipValue(); + break; + } + } + return new PositionTickInfo(line, ticks); + } + + public int getLine() { + return line; + } + + public void setLine(int line) { + this.line = line; + } + + public int getTicks() { + return ticks; + } + + public void setTicks(int ticks) { + this.ticks = ticks; + } + + @Override + public boolean equals(Object obj) { + Objects.requireNonNull(obj, "obj is mandatory for equals method"); + return this.getLine() == ((PositionTickInfo) obj).getLine() + && this.getTicks() == ((PositionTickInfo) obj).getTicks(); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java new file mode 100644 index 0000000000000..2aa8350b31707 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java @@ -0,0 +1,161 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.openqa.selenium.devtools.DevToolsException; +import org.openqa.selenium.json.JsonInput; +import org.openqa.selenium.json.JsonInputConverter; + +/** + * Recorded profile. + */ +public class Profile { + + /** + * The list of profile nodes. First item is the root node. + */ + private List nodes; + /** + * Profiling start timestamp in microseconds. + */ + private Instant startTime; + /** + * Profiling end timestamp in microseconds. + */ + private Instant endTime; + /** + * Ids of samples top nodes. Optional + */ + private List samples; + /** + * Time intervals between adjacent samples in microseconds. The first delta is relative to the. profile startTime. + * Optional + */ + private List timeDeltas; + + public Profile( + List nodes, + Instant startTime, + Instant endTime, + List samples, + List timeDeltas) { + this.setNodes(nodes); + this.setStartTime(startTime); + this.setEndTime(endTime); + this.setSamples(samples); + this.setTimeDeltas(timeDeltas); + } + + public static Profile fromJson(JsonInput input) { + List nodes = null; + Instant startTime = null; + Instant endTime = null; + List samples = null; + List timeDeltas = null; + input.beginObject(); + while (input.hasNext()) { + switch (input.nextName()) { + case "nodes": + nodes = new ArrayList<>(); + input.beginArray(); + while (input.hasNext()) { + nodes.add(ProfileNode.fromJson(input)); + } + input.endArray(); + break; + case "startTime": + startTime = JsonInputConverter.extractInstant(input); + break; + case "endTime": + endTime = JsonInputConverter.extractInstant(input); + break; + case "samples": + samples = new ArrayList<>(); + input.beginArray(); + while (input.hasNext()) { + samples.add(JsonInputConverter.extractInt(input)); + } + input.endArray(); + break; + case "timeDeltas": + timeDeltas = new ArrayList<>(); + input.beginArray(); + while (input.hasNext()) { + timeDeltas.add(JsonInputConverter.extractInt(input)); + } + input.endArray(); + break; + default: + input.skipValue(); + break; + } + } + input.endObject(); + return new Profile(nodes, startTime, endTime, samples, timeDeltas); + } + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + Objects.requireNonNull(nodes, "nodes are require for Profile object"); + if (nodes.isEmpty()) { + throw new DevToolsException("Nodes cannot be Empty Object"); + } + this.nodes = nodes; + } + + public Instant getStartTime() { + return startTime; + } + + public void setStartTime(Instant startTime) { + Objects.requireNonNull(nodes, "startTime is require for Profile object"); + this.startTime = startTime; + } + + public Instant getEndTime() { + return endTime; + } + + public void setEndTime(Instant endTime) { + Objects.requireNonNull(nodes, "endTime is require for Profile object"); + this.endTime = endTime; + } + + public List getSamples() { + return samples; + } + + public void setSamples(List samples) { + this.samples = samples; + } + + public List getTimeDeltas() { + return timeDeltas; + } + + public void setTimeDeltas(List timeDeltas) { + this.timeDeltas = timeDeltas; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java new file mode 100644 index 0000000000000..067ebd4bc67af --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java @@ -0,0 +1,170 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import static org.openqa.selenium.json.JsonInputConverter.extractInt; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.openqa.selenium.devtools.network.model.CallFrame; +import org.openqa.selenium.json.JsonInput; + +public class ProfileNode { + + /** + * Unique id of the node. + */ + private int id; + /** + * Function location. + */ + private CallFrame callFrame; + + /** + * Number of samples where this node was on top of the call stack. Optional + */ + private Integer hitCount; + + /** + * Children node id Optional + */ + private List children; + + /** + * The reason of being not optimized. The function may be deoptimized or marked as don't optimize. Optional + */ + private String deoptReason; + + /** + * An array of source position ticks. Optional + */ + private List positionTicks; + + public ProfileNode( + int id, + CallFrame callFrame, + Integer hitCount, + List children, + String deoptReason, + List positionTicks) { + + this.setId(id); + this.setCallFrame(callFrame); + this.setHitCount(hitCount); + this.setChildren(children); + this.setDeoptReason(deoptReason); + this.setPositionTicks(positionTicks); + } + + public static ProfileNode fromJson(JsonInput input) { + int id = -1; + CallFrame callFrame = null; + Integer hitCount = null; + List children = null; + String dropReason = null; + List positionTicks = null; + input.beginObject(); + while (input.hasNext()) { + switch (input.nextName()) { + case "id": + id = extractInt(input); + break; + case "callFrame": + callFrame = CallFrame.parseCallFrame(input); + break; + case "hitCount": + hitCount = extractInt(input); + break; + case "children": + children = new ArrayList<>(); + input.beginArray(); + while (input.hasNext()) { + children.add(extractInt(input)); + } + input.endArray(); + break; + case "dropReason": + dropReason = input.nextString(); + break; + case "positionTicks": + positionTicks = new ArrayList<>(); + input.beginArray(); + while (input.hasNext()) { + positionTicks.add(PositionTickInfo.fromJson(input)); + } + input.endArray(); + break; + default: + input.skipValue(); + break; + } + } + input.endObject(); + return new ProfileNode(id, callFrame, hitCount, children, dropReason, positionTicks); + } + + public int getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public CallFrame getCallFrame() { + return callFrame; + } + + public void setCallFrame(CallFrame callFrame) { + Objects.requireNonNull(callFrame, "callFrame is mandatory"); + this.callFrame = callFrame; + } + + public int getHitCount() { + return hitCount; + } + + public void setHitCount(int hitCount) { + this.hitCount = hitCount; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public String getDeoptReason() { + return deoptReason; + } + + public void setDeoptReason(String deoptReason) { + this.deoptReason = deoptReason; + } + + public List getPositionTicks() { + return positionTicks; + } + + public void setPositionTicks(List positionTicks) { + this.positionTicks = positionTicks; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptCoverage.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptCoverage.java new file mode 100644 index 0000000000000..d72d3313e5990 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptCoverage.java @@ -0,0 +1,118 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.openqa.selenium.devtools.DevToolsException; +import org.openqa.selenium.json.JsonInput; + +/** + * Coverage data for a JavaScript script + */ +public class ScriptCoverage { + + /** + * JavaScript script id. + */ + private String scriptId; + /** + * JavaScript script name or url. + */ + private String url; + /** + * Functions contained in the script that has coverage data. + */ + private List functions; + + public ScriptCoverage(String scriptId, String url, List functions) { + this.setScriptId(scriptId); + this.setUrl(url); + this.setFunctions(functions); + } + + private static ScriptCoverage parse(JsonInput input) { + String scriptId = null; + String url = null; + List functionCoverages = null; + input.beginObject(); + while (input.hasNext()) { + switch (input.nextName()) { + case "scriptId": + scriptId = input.nextString(); + break; + case "url": + url = input.nextString(); + break; + case "functions": + functionCoverages = new ArrayList<>(); + input.beginArray(); + while (input.hasNext()) { + functionCoverages.add(FunctionCoverage.fromJson(input)); + } + input.endArray(); + break; + default: + input.skipValue(); + break; + + } + } + input.endObject(); + return new ScriptCoverage(scriptId, url, functionCoverages); + } + + private static List fromJson(JsonInput input) { + List coverages = new ArrayList<>(); + while (input.hasNext()) { + coverages.add(parse(input)); + } + return coverages; + } + + public String getScriptId() { + return scriptId; + } + + public void setScriptId(String scriptId) { + Objects.requireNonNull(scriptId, "scriptId is require"); + this.scriptId = scriptId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + Objects.requireNonNull(url, "url is require"); + this.url = url; + } + + public List getFunctions() { + return functions; + } + + public void setFunctions(List functions) { + Objects.requireNonNull(functions, "functions is require"); + if (functions.isEmpty()) { + throw new DevToolsException("functions is require"); + } + this.functions = functions; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptTypeProfile.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptTypeProfile.java new file mode 100644 index 0000000000000..31e990b756bdb --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptTypeProfile.java @@ -0,0 +1,108 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.openqa.selenium.Beta; +import org.openqa.selenium.devtools.DevToolsException; +import org.openqa.selenium.json.JsonInput; + +/** + * Type profile data collected during runtime for a JavaScript script.EXPERIMENTAL + */ +@Beta +public class ScriptTypeProfile { + + /** + * JavaScript script id. + */ + private String scriptId; + /** + * JavaScript script name or url. + */ + private String url; + /** + * Type profile entries for parameters and return values of the functions in the script. + */ + private List entries; + + public ScriptTypeProfile(String scriptId, String url, + List entries) { + this.setScriptId(scriptId); + this.setUrl(url); + this.setEntries(entries); + } + + public static ScriptTypeProfile fromJson(JsonInput input) { + String scriptId = input.nextString(); + String url = null; + List entries = null; + while (input.hasNext()) { + switch (input.nextName()) { + case "url": + url = input.nextString(); + break; + case "entries": + entries = new ArrayList<>(); + input.beginArray(); + while (input.hasNext()) { + entries.add(TypeProfileEntry.fromJson(input)); + } + input.endArray(); + break; + default: + input.skipValue(); + break; + } + } + return new ScriptTypeProfile(scriptId, url, entries); + } + + public String getScriptId() { + return scriptId; + } + + public void setScriptId(String scriptId) { + Objects.requireNonNull(scriptId, "scriptId is require"); + this.scriptId = scriptId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + Objects.requireNonNull(url, "url is require"); + this.url = url; + } + + public List getEntries() { + return entries; + } + + public void setEntries( + List entries) { + Objects.requireNonNull(entries, "entries are require"); + if (entries.isEmpty()) { + throw new DevToolsException("entries are require"); + } + this.entries = entries; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeObject.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeObject.java new file mode 100644 index 0000000000000..f240f98e19702 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeObject.java @@ -0,0 +1,51 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import java.util.Objects; +import org.openqa.selenium.Beta; +import org.openqa.selenium.json.JsonInput; + +/** + * Describes a type collected during runtime.EXPERIMENTAL + */ +@Beta +public class TypeObject { + + /** + * Name of a type collected with type profiling. + */ + private String name; + + public TypeObject(String name) { + this.setName(name); + } + + public static TypeObject fromJson(JsonInput input) { + return new TypeObject(input.nextString()); + } + + public String getName() { + return name; + } + + public void setName(String name) { + Objects.requireNonNull(name, "name is require"); + this.name = name; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java new file mode 100644 index 0000000000000..db302940c7330 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java @@ -0,0 +1,90 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools.profiler.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.openqa.selenium.Beta; +import org.openqa.selenium.devtools.DevToolsException; +import org.openqa.selenium.json.JsonInput; +import org.openqa.selenium.json.JsonInputConverter; + +/** + * Source offset and types for a parameter or return value.EXPERIMENTAL + */ +@Beta +public class TypeProfileEntry { + + /** + * Source offset of the parameter or end of function for return values. + */ + private int offset; + /** + * The types for this parameter or return value. + */ + private List types; + + + public TypeProfileEntry(int offset, + List types) { + this.setOffset(offset); + this.setTypes(types); + } + + public static TypeProfileEntry fromJson(JsonInput input) { + int offset = JsonInputConverter.extractInt(input); + List types = null; + while (input.hasNext()) { + switch (input.nextName()) { + case "types": + types = new ArrayList<>(); + input.beginArray(); + while (input.hasNext()) { + types.add(TypeObject.fromJson(input)); + } + input.endArray(); + break; + default: + input.skipValue(); + break; + } + } + return new TypeProfileEntry(offset, types); + } + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public List getTypes() { + return types; + } + + public void setTypes(List types) { + Objects.requireNonNull(types, "types is require"); + if (types.isEmpty()) { + throw new DevToolsException("types is require"); + } + this.types = types; + } +} diff --git a/java/client/src/org/openqa/selenium/json/JsonInputConverter.java b/java/client/src/org/openqa/selenium/json/JsonInputConverter.java index 24f9ef55715ba..47ecb213c0445 100644 --- a/java/client/src/org/openqa/selenium/json/JsonInputConverter.java +++ b/java/client/src/org/openqa/selenium/json/JsonInputConverter.java @@ -17,15 +17,22 @@ package org.openqa.selenium.json; +import java.time.Instant; import java.util.HashMap; import java.util.Map; public class JsonInputConverter { + public static Double extractDouble(JsonInput input){ Number number = input.nextNumber(); return (null != number) ? number.doubleValue() : null; } + public static Long extractLong(JsonInput input) { + Number number = input.nextNumber(); + return (null != number) ? number.longValue() : null; + } + public static Integer extractInt(JsonInput input){ Number number = input.nextNumber(); return (null != number) ? number.intValue() : null; @@ -41,5 +48,8 @@ public static Map extractMap(JsonInput input){ return map; } - + public static Instant extractInstant(JsonInput input) { + Long instant = extractLong(input); + return (null != instant) ? Instant.ofEpochMilli(instant) : null; + } } diff --git a/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java new file mode 100644 index 0000000000000..ea863d3496eda --- /dev/null +++ b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java @@ -0,0 +1,129 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.devtools; + +import static org.openqa.selenium.devtools.profiler.Profiler.consoleProfileFinished; +import static org.openqa.selenium.devtools.profiler.Profiler.consoleProfileStarted; +import static org.openqa.selenium.devtools.profiler.Profiler.disable; +import static org.openqa.selenium.devtools.profiler.Profiler.enable; +import static org.openqa.selenium.devtools.profiler.Profiler.getBestEffortCoverage; +import static org.openqa.selenium.devtools.profiler.Profiler.setSamplingInterval; +import static org.openqa.selenium.devtools.profiler.Profiler.start; +import static org.openqa.selenium.devtools.profiler.Profiler.startPreciseCoverage; +import static org.openqa.selenium.devtools.profiler.Profiler.startTypeProfile; +import static org.openqa.selenium.devtools.profiler.Profiler.stop; +import static org.openqa.selenium.devtools.profiler.Profiler.stopTypeProfile; +import static org.openqa.selenium.devtools.profiler.Profiler.takePreciseCoverage; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertTrue; + +import java.util.List; +import java.util.Optional; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.devtools.profiler.model.Profile; +import org.openqa.selenium.devtools.profiler.model.ProfileNode; +import org.openqa.selenium.devtools.profiler.model.ScriptCoverage; +import org.testng.AssertJUnit; + + +public class ChromeDevToolsProfilerTest extends ChromeDevToolsTestBase { + + + @Before + public void preEachTest() { + devTools.send(enable()); + chromeDriver.get(appServer.whereIs("simpleTest.html")); + + } + + @After + public void postEachTest() { + devTools.send(disable()); + } + + @Test + public void setSimpleStartStopAndGetProfilerTest() { + + devTools.send(start()); + chromeDriver.navigate().refresh(); + Profile profiler = devTools.send(stop()); + validateProfile(profiler); + + + } + + private void validateProfile(Profile profiler) { + assertNotNull(profiler); + assertNotNull(profiler.getNodes()); + assertNotNull(profiler.getStartTime()); + assertNotNull(profiler.getEndTime()); + assertNotNull(profiler.getTimeDeltas()); + for (Integer integer : profiler.getTimeDeltas()) { + assertNotNull(integer); + } + for (ProfileNode n : profiler.getNodes()) { + assertNotNull(n); + assertNotNull(n.getCallFrame()); + } + } + + @Test + public void sampleGetBestEffortProfilerTest() { + devTools.send(setSamplingInterval(30)); + List bestEffort = devTools.send(getBestEffortCoverage()); + assertNotNull(bestEffort); + assertTrue(!bestEffort.isEmpty()); + } + + @Test + public void sampleSetStartPreciseCoverageTest() { + devTools.send(startPreciseCoverage(Optional.of(true), Optional.of(true))); + devTools.send(start()); + chromeDriver.navigate().refresh(); + List pc = devTools.send(takePreciseCoverage()); + assertNotNull(pc); + Profile profiler = devTools.send(stop()); + validateProfile(profiler); + } + + @Test + public void sampleSetStartTypeProfileTest() { + devTools.send(startTypeProfile()); + devTools.send(start()); + chromeDriver.navigate().refresh(); + + Profile profiler = devTools.send(stop()); + devTools.send(stopTypeProfile()); + validateProfile(profiler); + } + + @Test + public void sampleProfileEvents() { + devTools.addListener(consoleProfileStarted(), AssertJUnit::assertNotNull); + devTools.send(startTypeProfile()); + devTools.send(start()); + chromeDriver.navigate().refresh(); + devTools.addListener(consoleProfileFinished(), AssertJUnit::assertNotNull); + devTools.send(stopTypeProfile()); + Profile profiler = devTools.send(stop()); + validateProfile(profiler); + } + +} From fdbe07bfebed20a205ab3ea42add2e68640a5d12 Mon Sep 17 00:00:00 2001 From: Shay Dratler Date: Wed, 29 May 2019 22:56:57 +0300 Subject: [PATCH 2/4] After merge: + Remove JsonInputConverter with JsonInput Methods + Add nextInstant at JsonInput + Fix some Merge issues + Fix Test + Test imports --- .../profiler/model/CoverageRange.java | 7 ++- .../devtools/profiler/model/Location.java | 8 ++-- .../profiler/model/PositionTickInfo.java | 8 ++-- .../devtools/profiler/model/Profile.java | 15 +++--- .../devtools/profiler/model/ProfileNode.java | 11 ++--- .../profiler/model/TypeProfileEntry.java | 10 ++-- .../org/openqa/selenium/json/JsonInput.java | 7 +++ .../selenium/remote/http/HttpHandler.java | 6 --- .../devtools/ChromeDevToolsProfilerTest.java | 47 +++++++------------ 9 files changed, 54 insertions(+), 65 deletions(-) diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java index 563d564a80176..1fe6cf4e6eea7 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java @@ -17,7 +17,6 @@ package org.openqa.selenium.devtools.profiler.model; -import static org.openqa.selenium.json.JsonInputConverter.extractInt; import org.openqa.selenium.json.JsonInput; @@ -53,13 +52,13 @@ public static CoverageRange fronJson(JsonInput input) { while (input.hasNext()) { switch (input.nextName()) { case "startOffset": - startOffset = extractInt(input); + startOffset = input.read(Integer.class); break; case "endOffset": - endOffset = extractInt(input); + endOffset = input.read(Integer.class); break; case "count": - count = extractInt(input); + count = input.read(Integer.class); break; default: input.skipValue(); diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java index 7859a148a76e5..0c196392c65bd 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java @@ -17,9 +17,9 @@ package org.openqa.selenium.devtools.profiler.model; -import java.util.Objects; import org.openqa.selenium.json.JsonInput; -import org.openqa.selenium.json.JsonInputConverter; + +import java.util.Objects; /** * Location in the source code. @@ -52,10 +52,10 @@ public static Location fronJson(JsonInput input) { while (input.hasNext()) { switch (input.nextName()) { case "lineNumber": - lineNumber = JsonInputConverter.extractInt(input); + lineNumber = input.read(Integer.class); break; case "columnNumber": - columnNumber = JsonInputConverter.extractInt(input); + columnNumber = input.read(Integer.class); break; default: input.skipValue(); diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java index 632170362b18e..ec09bf4160c6a 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java @@ -17,9 +17,9 @@ package org.openqa.selenium.devtools.profiler.model; -import java.util.Objects; import org.openqa.selenium.json.JsonInput; -import org.openqa.selenium.json.JsonInputConverter; + +import java.util.Objects; /** * Specifies a number of samples attributed to a certain source position. @@ -41,12 +41,12 @@ public PositionTickInfo(int line, int ticks) { } public static PositionTickInfo fromJson(JsonInput input) { - int line = JsonInputConverter.extractInt(input); + int line = input.read(Integer.class); int ticks = 0; while (input.hasNext()) { switch (input.nextName()) { case "ticks": - ticks = JsonInputConverter.extractInt(input); + ticks = input.read(Integer.class); break; default: input.skipValue(); diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java index 2aa8350b31707..959e158276072 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java @@ -17,13 +17,13 @@ package org.openqa.selenium.devtools.profiler.model; +import org.openqa.selenium.devtools.DevToolsException; +import org.openqa.selenium.json.JsonInput; + import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import org.openqa.selenium.devtools.DevToolsException; -import org.openqa.selenium.json.JsonInput; -import org.openqa.selenium.json.JsonInputConverter; /** * Recorded profile. @@ -83,16 +83,16 @@ public static Profile fromJson(JsonInput input) { input.endArray(); break; case "startTime": - startTime = JsonInputConverter.extractInstant(input); + startTime = input.nextInstant(); break; case "endTime": - endTime = JsonInputConverter.extractInstant(input); + endTime = input.nextInstant(); break; case "samples": samples = new ArrayList<>(); input.beginArray(); while (input.hasNext()) { - samples.add(JsonInputConverter.extractInt(input)); + samples.add(input.read(Integer.class)); } input.endArray(); break; @@ -100,7 +100,7 @@ public static Profile fromJson(JsonInput input) { timeDeltas = new ArrayList<>(); input.beginArray(); while (input.hasNext()) { - timeDeltas.add(JsonInputConverter.extractInt(input)); + timeDeltas.add(input.read(Integer.class)); } input.endArray(); break; @@ -158,4 +158,5 @@ public List getTimeDeltas() { public void setTimeDeltas(List timeDeltas) { this.timeDeltas = timeDeltas; } + } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java index 067ebd4bc67af..5435e7aa95d8f 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java @@ -17,13 +17,12 @@ package org.openqa.selenium.devtools.profiler.model; -import static org.openqa.selenium.json.JsonInputConverter.extractInt; +import org.openqa.selenium.devtools.network.model.CallFrame; +import org.openqa.selenium.json.JsonInput; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import org.openqa.selenium.devtools.network.model.CallFrame; -import org.openqa.selenium.json.JsonInput; public class ProfileNode { @@ -83,19 +82,19 @@ public static ProfileNode fromJson(JsonInput input) { while (input.hasNext()) { switch (input.nextName()) { case "id": - id = extractInt(input); + id = input.read(Integer.class); break; case "callFrame": callFrame = CallFrame.parseCallFrame(input); break; case "hitCount": - hitCount = extractInt(input); + hitCount = input.read(Integer.class); break; case "children": children = new ArrayList<>(); input.beginArray(); while (input.hasNext()) { - children.add(extractInt(input)); + children.add(input.read(Integer.class)); } input.endArray(); break; diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java index db302940c7330..d268dc9e4125c 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java @@ -17,13 +17,13 @@ package org.openqa.selenium.devtools.profiler.model; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import org.openqa.selenium.Beta; import org.openqa.selenium.devtools.DevToolsException; import org.openqa.selenium.json.JsonInput; -import org.openqa.selenium.json.JsonInputConverter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; /** * Source offset and types for a parameter or return value.EXPERIMENTAL @@ -48,7 +48,7 @@ public TypeProfileEntry(int offset, } public static TypeProfileEntry fromJson(JsonInput input) { - int offset = JsonInputConverter.extractInt(input); + int offset = input.read(Integer.class); List types = null; while (input.hasNext()) { switch (input.nextName()) { diff --git a/java/client/src/org/openqa/selenium/json/JsonInput.java b/java/client/src/org/openqa/selenium/json/JsonInput.java index 2d4088dd7082b..869cddb46750c 100644 --- a/java/client/src/org/openqa/selenium/json/JsonInput.java +++ b/java/client/src/org/openqa/selenium/json/JsonInput.java @@ -22,6 +22,7 @@ import java.io.UncheckedIOException; import java.lang.reflect.Type; import java.math.BigDecimal; +import java.time.Instant; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; @@ -186,6 +187,11 @@ public String nextString() { return readString(); } + public Instant nextInstant() { + Long time = read(Long.class); + return (null != time) ? Instant.ofEpochSecond(time) : null; + } + public boolean hasNext() { if (stack.isEmpty()) { throw new JsonException( @@ -283,6 +289,7 @@ public T read(Type type) { return coercer.coerce(this, type, setter); } + private boolean isReadingName() { return stack.peekFirst() == Container.MAP_NAME; } diff --git a/java/client/src/org/openqa/selenium/remote/http/HttpHandler.java b/java/client/src/org/openqa/selenium/remote/http/HttpHandler.java index 2a17dbeb78b4e..4aac1e9ac9362 100644 --- a/java/client/src/org/openqa/selenium/remote/http/HttpHandler.java +++ b/java/client/src/org/openqa/selenium/remote/http/HttpHandler.java @@ -17,12 +17,6 @@ package org.openqa.selenium.remote.http; -import org.openqa.selenium.json.JsonInput; - -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; - import java.util.function.Function; @FunctionalInterface diff --git a/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java index ea863d3496eda..0264c88a6cd5d 100644 --- a/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java +++ b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java @@ -29,18 +29,17 @@ import static org.openqa.selenium.devtools.profiler.Profiler.stop; import static org.openqa.selenium.devtools.profiler.Profiler.stopTypeProfile; import static org.openqa.selenium.devtools.profiler.Profiler.takePreciseCoverage; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertTrue; -import java.util.List; -import java.util.Optional; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.devtools.profiler.model.Profile; import org.openqa.selenium.devtools.profiler.model.ProfileNode; import org.openqa.selenium.devtools.profiler.model.ScriptCoverage; -import org.testng.AssertJUnit; + +import java.util.List; +import java.util.Optional; public class ChromeDevToolsProfilerTest extends ChromeDevToolsTestBase { @@ -59,7 +58,7 @@ public void postEachTest() { } @Test - public void setSimpleStartStopAndGetProfilerTest() { + public void aSimpleStartStopAndGetProfilerTest() { devTools.send(start()); chromeDriver.navigate().refresh(); @@ -70,17 +69,17 @@ public void setSimpleStartStopAndGetProfilerTest() { } private void validateProfile(Profile profiler) { - assertNotNull(profiler); - assertNotNull(profiler.getNodes()); - assertNotNull(profiler.getStartTime()); - assertNotNull(profiler.getEndTime()); - assertNotNull(profiler.getTimeDeltas()); + Assert.assertNotNull(profiler); + Assert.assertNotNull(profiler.getNodes()); + Assert.assertNotNull(profiler.getStartTime()); + Assert.assertNotNull(profiler.getEndTime()); + Assert.assertNotNull(profiler.getTimeDeltas()); for (Integer integer : profiler.getTimeDeltas()) { - assertNotNull(integer); + Assert.assertNotNull(integer); } for (ProfileNode n : profiler.getNodes()) { - assertNotNull(n); - assertNotNull(n.getCallFrame()); + Assert.assertNotNull(n); + Assert.assertNotNull(n.getCallFrame()); } } @@ -88,8 +87,8 @@ private void validateProfile(Profile profiler) { public void sampleGetBestEffortProfilerTest() { devTools.send(setSamplingInterval(30)); List bestEffort = devTools.send(getBestEffortCoverage()); - assertNotNull(bestEffort); - assertTrue(!bestEffort.isEmpty()); + Assert.assertNotNull(bestEffort); + Assert.assertTrue(!bestEffort.isEmpty()); } @Test @@ -98,29 +97,19 @@ public void sampleSetStartPreciseCoverageTest() { devTools.send(start()); chromeDriver.navigate().refresh(); List pc = devTools.send(takePreciseCoverage()); - assertNotNull(pc); + Assert.assertNotNull(pc); Profile profiler = devTools.send(stop()); validateProfile(profiler); } - @Test - public void sampleSetStartTypeProfileTest() { - devTools.send(startTypeProfile()); - devTools.send(start()); - chromeDriver.navigate().refresh(); - - Profile profiler = devTools.send(stop()); - devTools.send(stopTypeProfile()); - validateProfile(profiler); - } @Test public void sampleProfileEvents() { - devTools.addListener(consoleProfileStarted(), AssertJUnit::assertNotNull); + devTools.addListener(consoleProfileStarted(), Assert::assertNotNull); devTools.send(startTypeProfile()); devTools.send(start()); chromeDriver.navigate().refresh(); - devTools.addListener(consoleProfileFinished(), AssertJUnit::assertNotNull); + devTools.addListener(consoleProfileFinished(), Assert::assertNotNull); devTools.send(stopTypeProfile()); Profile profiler = devTools.send(stop()); validateProfile(profiler); From 5c7efae425636ee3af84824dd5d79fa9f5a1ccdb Mon Sep 17 00:00:00 2001 From: Shay Dratler Date: Thu, 30 May 2019 16:35:33 +0300 Subject: [PATCH 3/4] adding Profile test to devTool test suit --- java/client/test/org/openqa/selenium/devtools/DevToolsTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/client/test/org/openqa/selenium/devtools/DevToolsTests.java b/java/client/test/org/openqa/selenium/devtools/DevToolsTests.java index 1a38509709095..4990002f51de6 100644 --- a/java/client/test/org/openqa/selenium/devtools/DevToolsTests.java +++ b/java/client/test/org/openqa/selenium/devtools/DevToolsTests.java @@ -22,6 +22,7 @@ @RunWith(Suite.class) @Suite.SuiteClasses({ + ChromeDevToolsProfilerTest.class, ChromeDevToolsNetworkTest.class, ChromeDevToolsPerformanceTest.class, ChromeDevToolsConsoleTest.class, From 462635dd87c0b1cd27e3a4794499d26c114ab312 Mon Sep 17 00:00:00 2001 From: Shay Dratler Date: Sat, 1 Jun 2019 22:16:42 +0300 Subject: [PATCH 4/4] Fix all comments of Code review --- .../selenium/devtools/profiler/Profiler.java | 45 +++++++------- .../model/ConsoleProfileFinished.java | 42 ++++++------- .../profiler/model/ConsoleProfileStarted.java | 38 +++++------- .../profiler/model/CoverageRange.java | 23 +++---- .../profiler/model/FunctionCoverage.java | 35 +++++------ .../devtools/profiler/model/Location.java | 26 +++----- .../profiler/model/PositionTickInfo.java | 24 ++++--- .../devtools/profiler/model/Profile.java | 62 +++++++------------ .../devtools/profiler/model/ProfileNode.java | 61 +++++------------- .../profiler/model/ScriptCoverage.java | 45 +++++++------- .../profiler/model/ScriptTypeProfile.java | 40 +++++------- .../devtools/profiler/model/TypeObject.java | 14 ++--- .../profiler/model/TypeProfileEntry.java | 21 +++---- .../devtools/ChromeDevToolsProfilerTest.java | 28 ++++----- 14 files changed, 200 insertions(+), 304 deletions(-) diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/Profiler.java b/java/client/src/org/openqa/selenium/devtools/profiler/Profiler.java index 897a028dc5180..c8b7eac62e4fb 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/Profiler.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/Profiler.java @@ -22,8 +22,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; import com.google.common.reflect.TypeToken; -import java.util.List; -import java.util.Optional; + import org.openqa.selenium.Beta; import org.openqa.selenium.devtools.Command; import org.openqa.selenium.devtools.Event; @@ -33,6 +32,9 @@ import org.openqa.selenium.devtools.profiler.model.ScriptCoverage; import org.openqa.selenium.devtools.profiler.model.ScriptTypeProfile; +import java.util.List; +import java.util.Optional; + public class Profiler { /** @@ -58,28 +60,25 @@ public static Command start() { /** * stop Profiling process - * - * @return {@link Profile} with sampled values - */ + **/ public static Command stop() { return new Command<>("Profiler.stop", ImmutableMap.of(), map("profile", Profile.class)); } /** * Collect coverage data for the current isolate. The coverage data may be incomplete due to garbage collection. - * - * @return Array {@link ScriptCoverage} - */ + **/ public static Command> getBestEffortCoverage() { return new Command<>( - "Profiler.getBestEffortCoverage", ImmutableMap.of(), map("result", new TypeToken>() { - }.getType())); + "Profiler.getBestEffortCoverage", ImmutableMap.of(), + map("result", new TypeToken>() { + }.getType())); } /** * Changes CPU profiler sampling interval. Must be called before CPU profiles recording started. * - * @param interval - New sampling interval in microseconds. + * @param interval New sampling interval in microseconds. */ public static Command setSamplingInterval(int interval) { return new Command<>("Profiler.setSamplingInterval", ImmutableMap.of("interval", interval)); @@ -89,11 +88,11 @@ public static Command setSamplingInterval(int interval) { * Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code coverage may be * incomplete. Enabling prevents running optimized code and resets execution counters. * - * @param callCount - Collect accurate call counts beyond simple 'covered' or 'not covered'. - * @param detailed - Collect block-based coverage. + * @param callCount Collect accurate call counts beyond simple 'covered' or 'not covered'. + * @param detailed Collect block-based coverage. */ public static Command startPreciseCoverage( - Optional callCount, Optional detailed) { + Optional callCount, Optional detailed) { Builder mapBuilder = ImmutableMap.builder(); callCount.ifPresent(value -> mapBuilder.put("callCount", value)); detailed.ifPresent(value -> mapBuilder.put("detailed", value)); @@ -130,24 +129,22 @@ public static Command stopTypeProfile() { */ public static Command> takePreciseCoverage() { return new Command<>( - "Profiler.takePreciseCoverage", - ImmutableMap.of(), - map("result", new TypeToken>() { - }.getType())); + "Profiler.takePreciseCoverage", + ImmutableMap.of(), + map("result", new TypeToken>() { + }.getType())); } /** * Collect type profile.EXPERIMENTAL - * - * @return - Type profile for all scripts since startTypeProfile() was turned on. */ @Beta public static Command> takeTypeProfile() { return new Command<>( - "Profiler.takeTypeProfile", - ImmutableMap.of(), - map("result", new TypeToken>() { - }.getType())); + "Profiler.takeTypeProfile", + ImmutableMap.of(), + map("result", new TypeToken>() { + }.getType())); } public static Event consoleProfileFinished() { diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileFinished.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileFinished.java index eac3eb34b7f39..94cb720e35577 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileFinished.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileFinished.java @@ -17,18 +17,19 @@ package org.openqa.selenium.devtools.profiler.model; -import java.util.Objects; import org.openqa.selenium.json.JsonInput; +import java.util.Objects; + public class ConsoleProfileFinished { - private String id; + private final String id; /** * Location of console.profileEnd(). */ - private Location location; + private final Location location; - private Profile profile; + private final Profile profile; /** * Profile title passed as an argument to console.profile(). @@ -37,14 +38,20 @@ public class ConsoleProfileFinished { */ private String title; - public ConsoleProfileFinished(String id, Location location, Profile profile, String title) { - this.setId(id); - this.setLocation(location); - this.setProfile(profile); - this.setTitle(title); + public ConsoleProfileFinished(String id, + Location location, + Profile profile, String title) { + Objects.requireNonNull(id, "id is require"); + Objects.requireNonNull(location, "location is require"); + Objects.requireNonNull(profile, "profile is require"); + + this.id = id; + this.location = location; + this.profile = profile; + this.title = title; } - public static ConsoleProfileFinished fromJson(JsonInput input) { + private static ConsoleProfileFinished fromJson(JsonInput input) { String id = input.nextString(); Location location = null; Profile profile = null; @@ -52,7 +59,7 @@ public static ConsoleProfileFinished fromJson(JsonInput input) { while (input.hasNext()) { switch (input.nextName()) { case "location": - location = Location.fronJson(input); + location = Location.fromJson(input); break; case "profile": profile = Profile.fromJson(input); @@ -72,29 +79,16 @@ public String getId() { return id; } - public void setId(String id) { - Objects.requireNonNull(id, "id is require"); - this.id = id; - } public Location getLocation() { return location; } - public void setLocation(Location location) { - Objects.requireNonNull(location, "location is require"); - this.location = location; - } public Profile getProfile() { return profile; } - public void setProfile(Profile profile) { - Objects.requireNonNull(profile, "profile is require"); - this.profile = profile; - } - public String getTitle() { return title; } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileStarted.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileStarted.java index bc9f39e61b017..0aad985d2a2ed 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileStarted.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ConsoleProfileStarted.java @@ -17,37 +17,42 @@ package org.openqa.selenium.devtools.profiler.model; -import java.util.Objects; import org.openqa.selenium.json.JsonInput; +import java.util.Objects; + public class ConsoleProfileStarted { - private String id; + private final String id; /** * Location of console.profileEnd(). */ - private Location location; + private final Location location; /** * Profile title passed as an argument to console.profile(). * *

Optional */ - private String title; + private final String title; + + public ConsoleProfileStarted(String id, + Location location, String title) { + Objects.requireNonNull(id, "id is require"); + Objects.requireNonNull(location, "location is require"); - public ConsoleProfileStarted(String id, Location location, String title) { - this.setId(id); - this.setLocation(location); - this.setTitle(title); + this.id = id; + this.location = location; + this.title = title; } - public static ConsoleProfileStarted fromJson(JsonInput input) { + private static ConsoleProfileStarted fromJson(JsonInput input) { String id = input.nextString(); Location location = null; String title = null; while (input.hasNext()) { switch (input.nextName()) { case "location": - location = Location.fronJson(input); + location = Location.fromJson(input); break; case "title": title = input.nextString(); @@ -64,25 +69,12 @@ public String getId() { return id; } - public void setId(String id) { - Objects.requireNonNull(id, "id is require"); - this.id = id; - } - public Location getLocation() { return location; } - public void setLocation(Location location) { - Objects.requireNonNull(location, "location is require"); - this.location = location; - } - public String getTitle() { return title; } - public void setTitle(String title) { - this.title = title; - } } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java index 1fe6cf4e6eea7..d75f76b46e045 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/CoverageRange.java @@ -28,23 +28,23 @@ public class CoverageRange { /** * JavaScript script source offset for the range start. */ - private int startOffset; + private final int startOffset; /** * JavaScript script source offset for the range end. */ - private int endOffset; + private final int endOffset; /** * Collected execution count of the source range. */ - private int count; + private final int count; public CoverageRange(int startOffset, int endOffset, int count) { - this.setStartOffset(startOffset); - this.setEndOffset(endOffset); - this.setCount(count); + this.startOffset = startOffset; + this.endOffset = endOffset; + this.count = count; } - public static CoverageRange fronJson(JsonInput input) { + static CoverageRange fromJson(JsonInput input) { int startOffset = 0; int endOffset = 0; int count = 0; @@ -73,23 +73,14 @@ public int getStartOffset() { return startOffset; } - public void setStartOffset(int startOffset) { - this.startOffset = startOffset; - } public int getEndOffset() { return endOffset; } - public void setEndOffset(int endOffset) { - this.endOffset = endOffset; - } public int getCount() { return count; } - public void setCount(int count) { - this.count = count; - } } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/FunctionCoverage.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/FunctionCoverage.java index ea1817c047b66..c0a8f3b2e3b3a 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/FunctionCoverage.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/FunctionCoverage.java @@ -17,31 +17,36 @@ package org.openqa.selenium.devtools.profiler.model; +import org.openqa.selenium.devtools.DevToolsException; +import org.openqa.selenium.json.JsonInput; + import java.util.ArrayList; import java.util.List; import java.util.Objects; -import org.openqa.selenium.devtools.DevToolsException; -import org.openqa.selenium.json.JsonInput; public class FunctionCoverage { /** * JavaScript function name. */ - private String functionName; + private final String functionName; /** * JavaScript function name. */ - private List ranges; + private final List ranges; /** * Whether coverage data for this function has block granularity. */ - private Boolean isBlockCoverage; + private final Boolean isBlockCoverage; + + public FunctionCoverage(String functionName, + List ranges, Boolean isBlockCoverage) { + validateRanges(ranges); + Objects.requireNonNull(functionName, "functionName is require"); - public FunctionCoverage(String functionName, List ranges, boolean isBlockCoverage) { - this.setFunctionName(functionName); - this.setRanges(ranges); - this.setBlockCoverage(isBlockCoverage); + this.functionName = functionName; + this.ranges = ranges; + this.isBlockCoverage = isBlockCoverage; } public static FunctionCoverage fromJson(JsonInput input) { @@ -59,7 +64,7 @@ public static FunctionCoverage fromJson(JsonInput input) { ranges = new ArrayList<>(); input.beginArray(); while (input.hasNext()) { - ranges.add(CoverageRange.fronJson(input)); + ranges.add(CoverageRange.fromJson(input)); } input.endArray(); break; @@ -79,28 +84,20 @@ public String getFunctionName() { return functionName; } - public void setFunctionName(String functionName) { - Objects.requireNonNull(functionName, "functionName is require"); - this.functionName = functionName; - } public List getRanges() { return ranges; } - public void setRanges(List ranges) { + public void validateRanges(List ranges) { Objects.requireNonNull(ranges, "ranges is require"); if (ranges.isEmpty()) { throw new DevToolsException("ranges is require"); } - this.ranges = ranges; } public boolean isBlockCoverage() { return isBlockCoverage; } - public void setBlockCoverage(Boolean blockCoverage) { - isBlockCoverage = blockCoverage; - } } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java index 0c196392c65bd..d35777695909c 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/Location.java @@ -29,23 +29,25 @@ public class Location { /** * Script identifier as reported in the Debugger.scriptParsed. */ - private String scriptId; + private final String scriptId; /** * Line number in the script (0-based). */ - private int lineNumber; + private final int lineNumber; /** * Column number in the script (0-based). Optional */ - private Integer columnNumber; + private final Integer columnNumber; public Location(String scriptId, int lineNumber, Integer columnNumber) { - this.setScriptId(scriptId); - this.setLineNumber(lineNumber); - this.setColumnNumber(columnNumber); + Objects.requireNonNull(scriptId, "scriptId is require"); + + this.scriptId = scriptId; + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; } - public static Location fronJson(JsonInput input) { + public static Location fromJson(JsonInput input) { String scriptId = input.nextString(); int lineNumber = -1; Integer columnNumber = null; @@ -69,24 +71,14 @@ public String getScriptId() { return scriptId; } - public void setScriptId(String scriptId) { - Objects.requireNonNull(scriptId, "scriptId is require"); - this.scriptId = scriptId; - } public int getLineNumber() { return lineNumber; } - public void setLineNumber(int lineNumber) { - this.lineNumber = lineNumber; - } public Integer getColumnNumber() { return columnNumber; } - public void setColumnNumber(Integer columnNumber) { - this.columnNumber = columnNumber; - } } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java index ec09bf4160c6a..06abe5de614b5 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/PositionTickInfo.java @@ -29,18 +29,18 @@ public class PositionTickInfo { /** * Source line number (1-based). */ - private int line; + private final int line; /** * Number of samples attributed to the source line. */ - private int ticks; + private final int ticks; public PositionTickInfo(int line, int ticks) { - this.setLine(line); - this.setTicks(ticks); + this.line = line; + this.ticks = ticks; } - public static PositionTickInfo fromJson(JsonInput input) { + static PositionTickInfo fromJson(JsonInput input) { int line = input.read(Integer.class); int ticks = 0; while (input.hasNext()) { @@ -60,22 +60,20 @@ public int getLine() { return line; } - public void setLine(int line) { - this.line = line; - } - public int getTicks() { return ticks; } - public void setTicks(int ticks) { - this.ticks = ticks; - } - @Override public boolean equals(Object obj) { Objects.requireNonNull(obj, "obj is mandatory for equals method"); + return this.getLine() == ((PositionTickInfo) obj).getLine() && this.getTicks() == ((PositionTickInfo) obj).getTicks(); } + + @Override + public int hashCode() { + return Objects.hash(getLine(), getTicks()); + } } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java index 959e158276072..0488a157815b4 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/Profile.java @@ -33,39 +33,43 @@ public class Profile { /** * The list of profile nodes. First item is the root node. */ - private List nodes; + private final List nodes; /** * Profiling start timestamp in microseconds. */ - private Instant startTime; + private final Instant startTime; /** * Profiling end timestamp in microseconds. */ - private Instant endTime; + private final Instant endTime; /** * Ids of samples top nodes. Optional */ - private List samples; + private final List samples; /** * Time intervals between adjacent samples in microseconds. The first delta is relative to the. profile startTime. * Optional */ - private List timeDeltas; - - public Profile( - List nodes, - Instant startTime, - Instant endTime, - List samples, - List timeDeltas) { - this.setNodes(nodes); - this.setStartTime(startTime); - this.setEndTime(endTime); - this.setSamples(samples); - this.setTimeDeltas(timeDeltas); + private final List timeDeltas; + + public Profile(List nodes, Instant startTime, Instant endTime, + List samples, List timeDeltas) { + validateNodes(nodes); + Objects.requireNonNull(startTime, "startTime is require for Profile object"); + Objects.requireNonNull(endTime, "endTime is require for Profile object"); + + this.nodes = nodes; + this.startTime = startTime; + this.endTime = endTime; + this.samples = samples; + this.timeDeltas = timeDeltas; } - public static Profile fromJson(JsonInput input) { + public List getNodes() { + return nodes; + } + + static Profile fromJson(JsonInput input) { List nodes = null; Instant startTime = null; Instant endTime = null; @@ -113,50 +117,30 @@ public static Profile fromJson(JsonInput input) { return new Profile(nodes, startTime, endTime, samples, timeDeltas); } - public List getNodes() { - return nodes; - } - - public void setNodes(List nodes) { + private void validateNodes(List nodes) { Objects.requireNonNull(nodes, "nodes are require for Profile object"); if (nodes.isEmpty()) { throw new DevToolsException("Nodes cannot be Empty Object"); } - this.nodes = nodes; } public Instant getStartTime() { return startTime; } - public void setStartTime(Instant startTime) { - Objects.requireNonNull(nodes, "startTime is require for Profile object"); - this.startTime = startTime; - } public Instant getEndTime() { return endTime; } - public void setEndTime(Instant endTime) { - Objects.requireNonNull(nodes, "endTime is require for Profile object"); - this.endTime = endTime; - } public List getSamples() { return samples; } - public void setSamples(List samples) { - this.samples = samples; - } - public List getTimeDeltas() { return timeDeltas; } - public void setTimeDeltas(List timeDeltas) { - this.timeDeltas = timeDeltas; - } } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java index 5435e7aa95d8f..4897b5f793df3 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ProfileNode.java @@ -29,46 +29,42 @@ public class ProfileNode { /** * Unique id of the node. */ - private int id; + private final int id; /** * Function location. */ - private CallFrame callFrame; + private final CallFrame callFrame; /** * Number of samples where this node was on top of the call stack. Optional */ - private Integer hitCount; + private final Integer hitCount; /** * Children node id Optional */ - private List children; + private final List children; /** * The reason of being not optimized. The function may be deoptimized or marked as don't optimize. Optional */ - private String deoptReason; + private final String deoptReason; /** * An array of source position ticks. Optional */ - private List positionTicks; - - public ProfileNode( - int id, - CallFrame callFrame, - Integer hitCount, - List children, - String deoptReason, - List positionTicks) { - - this.setId(id); - this.setCallFrame(callFrame); - this.setHitCount(hitCount); - this.setChildren(children); - this.setDeoptReason(deoptReason); - this.setPositionTicks(positionTicks); + private final List positionTicks; + + public ProfileNode(int id, CallFrame callFrame, Integer hitCount, + List children, String deoptReason, + List positionTicks) { + Objects.requireNonNull(callFrame, "callFrame is mandatory"); + this.id = id; + this.callFrame = callFrame; + this.hitCount = hitCount; + this.children = children; + this.deoptReason = deoptReason; + this.positionTicks = positionTicks; } public static ProfileNode fromJson(JsonInput input) { @@ -122,48 +118,25 @@ public int getId() { return id; } - public void setId(Integer id) { - this.id = id; - } public CallFrame getCallFrame() { return callFrame; } - public void setCallFrame(CallFrame callFrame) { - Objects.requireNonNull(callFrame, "callFrame is mandatory"); - this.callFrame = callFrame; - } - public int getHitCount() { return hitCount; } - public void setHitCount(int hitCount) { - this.hitCount = hitCount; - } - public List getChildren() { return children; } - public void setChildren(List children) { - this.children = children; - } - public String getDeoptReason() { return deoptReason; } - public void setDeoptReason(String deoptReason) { - this.deoptReason = deoptReason; - } - public List getPositionTicks() { return positionTicks; } - public void setPositionTicks(List positionTicks) { - this.positionTicks = positionTicks; - } } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptCoverage.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptCoverage.java index d72d3313e5990..df657179e3975 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptCoverage.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptCoverage.java @@ -17,11 +17,12 @@ package org.openqa.selenium.devtools.profiler.model; +import org.openqa.selenium.devtools.DevToolsException; +import org.openqa.selenium.json.JsonInput; + import java.util.ArrayList; import java.util.List; import java.util.Objects; -import org.openqa.selenium.devtools.DevToolsException; -import org.openqa.selenium.json.JsonInput; /** * Coverage data for a JavaScript script @@ -31,20 +32,25 @@ public class ScriptCoverage { /** * JavaScript script id. */ - private String scriptId; + private final String scriptId; /** * JavaScript script name or url. */ - private String url; + private final String url; /** * Functions contained in the script that has coverage data. */ - private List functions; + private final List functions; - public ScriptCoverage(String scriptId, String url, List functions) { - this.setScriptId(scriptId); - this.setUrl(url); - this.setFunctions(functions); + public ScriptCoverage(String scriptId, String url, + List functions) { + validateFunctionCovarage(functions); + Objects.requireNonNull(url, "url is require"); + Objects.requireNonNull(scriptId, "scriptId is require"); + + this.scriptId = scriptId; + this.url = url; + this.functions = functions; } private static ScriptCoverage parse(JsonInput input) { @@ -90,29 +96,20 @@ public String getScriptId() { return scriptId; } - public void setScriptId(String scriptId) { - Objects.requireNonNull(scriptId, "scriptId is require"); - this.scriptId = scriptId; + private void validateFunctionCovarage(List functionCoverages) { + Objects.requireNonNull(functionCoverages, "functions is require"); + if (functionCoverages.isEmpty()) { + throw new DevToolsException("functions are required"); + } } public String getUrl() { return url; } - public void setUrl(String url) { - Objects.requireNonNull(url, "url is require"); - this.url = url; - } - public List getFunctions() { return functions; } - public void setFunctions(List functions) { - Objects.requireNonNull(functions, "functions is require"); - if (functions.isEmpty()) { - throw new DevToolsException("functions is require"); - } - this.functions = functions; - } + } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptTypeProfile.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptTypeProfile.java index 31e990b756bdb..c21f73b2a414d 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptTypeProfile.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/ScriptTypeProfile.java @@ -17,13 +17,14 @@ package org.openqa.selenium.devtools.profiler.model; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import org.openqa.selenium.Beta; import org.openqa.selenium.devtools.DevToolsException; import org.openqa.selenium.json.JsonInput; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + /** * Type profile data collected during runtime for a JavaScript script.EXPERIMENTAL */ @@ -33,24 +34,28 @@ public class ScriptTypeProfile { /** * JavaScript script id. */ - private String scriptId; + private final String scriptId; /** * JavaScript script name or url. */ - private String url; + private final String url; /** * Type profile entries for parameters and return values of the functions in the script. */ - private List entries; + private final List entries; public ScriptTypeProfile(String scriptId, String url, - List entries) { - this.setScriptId(scriptId); - this.setUrl(url); - this.setEntries(entries); + List entries) { + validateEntrie(entries); + Objects.requireNonNull(url, "url is require"); + Objects.requireNonNull(scriptId, "scriptId is require"); + + this.scriptId = scriptId; + this.url = url; + this.entries = entries; } - public static ScriptTypeProfile fromJson(JsonInput input) { + private static ScriptTypeProfile fromJson(JsonInput input) { String scriptId = input.nextString(); String url = null; List entries = null; @@ -79,30 +84,19 @@ public String getScriptId() { return scriptId; } - public void setScriptId(String scriptId) { - Objects.requireNonNull(scriptId, "scriptId is require"); - this.scriptId = scriptId; - } - public String getUrl() { return url; } - public void setUrl(String url) { - Objects.requireNonNull(url, "url is require"); - this.url = url; - } - public List getEntries() { return entries; } - public void setEntries( + public void validateEntrie( List entries) { Objects.requireNonNull(entries, "entries are require"); if (entries.isEmpty()) { throw new DevToolsException("entries are require"); } - this.entries = entries; } } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeObject.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeObject.java index f240f98e19702..c39400e400fca 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeObject.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeObject.java @@ -17,10 +17,11 @@ package org.openqa.selenium.devtools.profiler.model; -import java.util.Objects; import org.openqa.selenium.Beta; import org.openqa.selenium.json.JsonInput; +import java.util.Objects; + /** * Describes a type collected during runtime.EXPERIMENTAL */ @@ -30,13 +31,14 @@ public class TypeObject { /** * Name of a type collected with type profiling. */ - private String name; + private final String name; public TypeObject(String name) { - this.setName(name); + Objects.requireNonNull(name, "name is require"); + this.name = name; } - public static TypeObject fromJson(JsonInput input) { + static TypeObject fromJson(JsonInput input) { return new TypeObject(input.nextString()); } @@ -44,8 +46,4 @@ public String getName() { return name; } - public void setName(String name) { - Objects.requireNonNull(name, "name is require"); - this.name = name; - } } diff --git a/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java b/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java index d268dc9e4125c..35268a3bc844e 100644 --- a/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java +++ b/java/client/src/org/openqa/selenium/devtools/profiler/model/TypeProfileEntry.java @@ -34,20 +34,20 @@ public class TypeProfileEntry { /** * Source offset of the parameter or end of function for return values. */ - private int offset; + private final int offset; /** * The types for this parameter or return value. */ - private List types; - + private final List types; public TypeProfileEntry(int offset, - List types) { - this.setOffset(offset); - this.setTypes(types); + List types) { + validateTypes(types); + this.offset = offset; + this.types = types; } - public static TypeProfileEntry fromJson(JsonInput input) { + static TypeProfileEntry fromJson(JsonInput input) { int offset = input.read(Integer.class); List types = null; while (input.hasNext()) { @@ -72,19 +72,14 @@ public int getOffset() { return offset; } - public void setOffset(int offset) { - this.offset = offset; - } - public List getTypes() { return types; } - public void setTypes(List types) { + public void validateTypes(List types) { Objects.requireNonNull(types, "types is require"); if (types.isEmpty()) { throw new DevToolsException("types is require"); } - this.types = types; } } diff --git a/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java index 0264c88a6cd5d..efb34e3dd576d 100644 --- a/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java +++ b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsProfilerTest.java @@ -30,9 +30,7 @@ import static org.openqa.selenium.devtools.profiler.Profiler.stopTypeProfile; import static org.openqa.selenium.devtools.profiler.Profiler.takePreciseCoverage; -import org.junit.After; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.openqa.selenium.devtools.profiler.model.Profile; import org.openqa.selenium.devtools.profiler.model.ProfileNode; @@ -45,26 +43,14 @@ public class ChromeDevToolsProfilerTest extends ChromeDevToolsTestBase { - @Before - public void preEachTest() { - devTools.send(enable()); - chromeDriver.get(appServer.whereIs("simpleTest.html")); - - } - - @After - public void postEachTest() { - devTools.send(disable()); - } @Test public void aSimpleStartStopAndGetProfilerTest() { - + devTools.send(enable()); devTools.send(start()); - chromeDriver.navigate().refresh(); Profile profiler = devTools.send(stop()); validateProfile(profiler); - + devTools.send(disable()); } @@ -85,26 +71,33 @@ private void validateProfile(Profile profiler) { @Test public void sampleGetBestEffortProfilerTest() { + devTools.send(enable()); + chromeDriver.get(appServer.whereIs("simpleTest.html")); devTools.send(setSamplingInterval(30)); List bestEffort = devTools.send(getBestEffortCoverage()); Assert.assertNotNull(bestEffort); Assert.assertTrue(!bestEffort.isEmpty()); + devTools.send(disable()); } @Test public void sampleSetStartPreciseCoverageTest() { + devTools.send(enable()); + chromeDriver.get(appServer.whereIs("simpleTest.html")); devTools.send(startPreciseCoverage(Optional.of(true), Optional.of(true))); devTools.send(start()); - chromeDriver.navigate().refresh(); List pc = devTools.send(takePreciseCoverage()); Assert.assertNotNull(pc); Profile profiler = devTools.send(stop()); validateProfile(profiler); + devTools.send(disable()); } @Test public void sampleProfileEvents() { + devTools.send(enable()); + chromeDriver.get(appServer.whereIs("simpleTest.html")); devTools.addListener(consoleProfileStarted(), Assert::assertNotNull); devTools.send(startTypeProfile()); devTools.send(start()); @@ -113,6 +106,7 @@ public void sampleProfileEvents() { devTools.send(stopTypeProfile()); Profile profiler = devTools.send(stop()); validateProfile(profiler); + devTools.send(disable()); } }