mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
2364 lines
73 KiB
Markdown
2364 lines
73 KiB
Markdown
# Selenium Project Code Complexity Analysis
|
|
|
|
**Analysis Date:** 2025-12-18
|
|
**Project:** Selenium WebDriver (Multi-Language)
|
|
**Analysis Tool:** Lizard v1.19.0
|
|
**Analyzed By:** QE Code Complexity Analyzer Agent
|
|
|
|
## Executive Summary
|
|
|
|
The Selenium project is a large-scale, multi-language browser automation framework with substantial complexity across 5 primary language implementations. This analysis examined 3,564 source files across Java, JavaScript, C#, Python, and Ruby implementations.
|
|
|
|
### Overall Complexity Scores
|
|
|
|
| Language | Files | Total NLOC | Avg CCN | Avg NLOC/Func | Functions | Warnings | Quality Score |
|
|
|----------|-------|------------|---------|---------------|-----------|----------|---------------|
|
|
| Java | 1,290 | 62,880 | 1.8 | 8.1 | 5,569 | 21 | 87/100 |
|
|
| JavaScript | 250 | 4,933 | 1.7 | 5.7 | 614 | 1 | 92/100 |
|
|
| C# | 642 | 22,897 | 2.1 | 8.7 | 1,741 | 11 | 89/100 |
|
|
| Python | 261 | 9,100 | 2.2 | 6.4 | 1,080 | 6 | 91/100 |
|
|
| Ruby | 299 | 7,962 | 1.7 | 5.3 | 1,056 | 0 | 95/100 |
|
|
|
|
**Key Findings:**
|
|
- Ruby implementation has the cleanest code with zero complexity warnings
|
|
- Java has the largest codebase with 21 complexity hotspots requiring attention
|
|
- C# has moderate complexity with 11 warnings, primarily in core driver classes
|
|
- Python and JavaScript implementations maintain good complexity discipline
|
|
- Overall, the project maintains reasonable complexity for its scale
|
|
|
|
---
|
|
|
|
## 1. Java Implementation Analysis
|
|
|
|
### Overview
|
|
- **Total Files:** 1,290 Java files
|
|
- **Total NLOC:** 62,880 non-comment lines of code
|
|
- **Functions Analyzed:** 5,569
|
|
- **Average Cyclomatic Complexity:** 1.8 (Excellent)
|
|
- **Average Function Length:** 8.1 lines
|
|
|
|
### Top 10 Most Complex Java Files
|
|
|
|
| Rank | File | Lines | Complexity Issues |
|
|
|------|------|-------|-------------------|
|
|
| 1 | `/java/src/org/openqa/selenium/support/ui/ExpectedConditions.java` | 1,527 | 42 similar conditional patterns |
|
|
| 2 | `/java/src/org/openqa/selenium/devtools/CdpClientGenerator.java` | 1,404 | Code generation logic, high nesting |
|
|
| 3 | `/java/src/org/openqa/selenium/remote/RemoteWebDriver.java` | 1,323 | Large class, multiple responsibilities |
|
|
| 4 | `/java/src/org/openqa/selenium/support/events/WebDriverListener.java` | 1,313 | Event listener interface, repetitive patterns |
|
|
| 5 | `/java/src/org/openqa/selenium/grid/node/local/LocalNode.java` | 1,310 | Complex session management, high coupling |
|
|
| 6 | `/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java` | 802 | Configuration validation complexity |
|
|
| 7 | `/java/src/org/openqa/selenium/support/ui/Select.java` | 411 | Element selection logic, deep conditionals |
|
|
| 8 | `/java/src/org/openqa/selenium/firefox/FirefoxOptions.java` | 399 | Browser option merging, CCN 30 |
|
|
| 9 | `/java/src/org/openqa/selenium/json/SimplePropertyDescriptor.java` | 365 | Reflection-based property handling, CCN 17 |
|
|
| 10 | `/java/src/org/openqa/selenium/net/NetworkUtils.java` | 337 | Network detection logic, CCN 21 |
|
|
|
|
### Critical Complexity Hotspots (CCN > 15)
|
|
|
|
#### 1. FirefoxOptions.merge() - CCN: 30
|
|
**Location:** `java/src/org/openqa/selenium/firefox/FirefoxOptions.java:288-399`
|
|
- **NLOC:** 96 lines
|
|
- **Issue:** Deep nesting and complex conditional logic for option merging
|
|
- **Recommendation:**
|
|
- Extract option merge strategies into separate classes (Strategy pattern)
|
|
- Create OptionMerger interface with specific implementations per option type
|
|
- Reduce cyclomatic complexity from 30 to < 10 per method
|
|
|
|
#### 2. SpecialNumberType.getArgument() - CCN: 24
|
|
**Location:** `java/src/org/openqa/selenium/bidi/script/LocalValue.java:135-209`
|
|
- **NLOC:** 70 lines
|
|
- **Issue:** Large switch/case statement with complex type handling
|
|
- **Recommendation:**
|
|
- Replace switch with polymorphic type handlers
|
|
- Implement visitor pattern for type processing
|
|
- Target CCN reduction to < 8
|
|
|
|
#### 3. JsonInput.peek() - CCN: 23
|
|
**Location:** `java/src/org/openqa/selenium/json/JsonInput.java:121-168`
|
|
- **NLOC:** 38 lines
|
|
- **Issue:** Complex lookahead logic in JSON parsing
|
|
- **Recommendation:**
|
|
- Extract parsing states into state machine pattern
|
|
- Use table-driven parser for token prediction
|
|
- Reduce cognitive load with clear state transitions
|
|
|
|
#### 4. ChromiumOptions.mergeInPlace() - CCN: 20
|
|
**Location:** `java/src/org/openqa/selenium/chromium/ChromiumOptions.java:264-323`
|
|
- **NLOC:** 52 lines
|
|
- **Issue:** Similar to FirefoxOptions, complex merge logic
|
|
- **Recommendation:**
|
|
- Create common OptionsBuilder base class
|
|
- Use builder pattern with fluent interface
|
|
- Apply Template Method pattern for shared logic
|
|
|
|
#### 5. V148Adapter.adaptContainerInspectResponse() - CCN: 20
|
|
**Location:** `java/src/org/openqa/selenium/docker/client/V148Adapter.java:208-307`
|
|
- **NLOC:** 80 lines
|
|
- **Issue:** Complex Docker API response transformation
|
|
- **Recommendation:**
|
|
- Use data transfer objects (DTOs) with mapping framework
|
|
- Consider MapStruct or similar for automatic mapping
|
|
- Split into smaller transformation methods
|
|
|
|
### Java Method Length Analysis
|
|
|
|
**Methods > 100 Lines:**
|
|
- `LocalNode` session management methods (112 lines)
|
|
- `UserAgent.prepareToInterceptTraffic()` (122 lines) - Network interception setup
|
|
|
|
**Recommendation:** All methods > 50 lines should be reviewed for extraction opportunities.
|
|
|
|
### Java Class Size Issues
|
|
|
|
**Classes > 1,000 Lines:**
|
|
1. `RemoteWebDriver` (1,323 lines) - Core driver implementation
|
|
2. `LocalNode` (1,310 lines) - Grid node management
|
|
3. `ExpectedConditions` (1,527 lines) - Wait condition factory
|
|
|
|
**Refactoring Recommendation:**
|
|
- Split `ExpectedConditions` into domain-specific condition classes
|
|
- Extract `RemoteWebDriver` capabilities into mixins/traits
|
|
- Decompose `LocalNode` using composition over inheritance
|
|
|
|
---
|
|
|
|
## 2. JavaScript Implementation Analysis
|
|
|
|
### Overview
|
|
- **Total Files:** 250 JavaScript files
|
|
- **Total NLOC:** 4,933 non-comment lines of code
|
|
- **Functions Analyzed:** 614
|
|
- **Average Cyclomatic Complexity:** 1.7 (Excellent)
|
|
- **Average Function Length:** 5.7 lines
|
|
- **Warnings:** 1 (lowest of all implementations)
|
|
|
|
### Top 10 Most Complex JavaScript Files
|
|
|
|
| Rank | File | Lines | Key Metrics |
|
|
|------|------|-------|-------------|
|
|
| 1 | `/javascript/selenium-webdriver/lib/webdriver.js` | 3,389 | Core WebDriver API |
|
|
| 2 | `/javascript/selenium-webdriver/lib/input.js` | 1,050 | Input action chains |
|
|
| 3 | `/javascript/selenium-webdriver/lib/logging.js` | 653 | Logging infrastructure |
|
|
| 4 | `/javascript/selenium-webdriver/lib/error.js` | 605 | Error handling (346 NLOC) |
|
|
| 5 | `/javascript/selenium-webdriver/lib/http.js` | 587 | HTTP client implementation |
|
|
| 6 | `/javascript/selenium-webdriver/lib/capabilities.js` | 555 | Browser capabilities |
|
|
| 7 | `/javascript/selenium-webdriver/lib/select.js` | 502 | Select element wrapper |
|
|
| 8 | `/javascript/selenium-webdriver/lib/by.js` | 481 | Locator strategies |
|
|
| 9 | `/javascript/selenium-webdriver/lib/until.js` | 431 | Wait conditions |
|
|
| 10 | `/javascript/selenium-webdriver/test/lib/fileserver.js` | 392 | Test utilities |
|
|
|
|
### Critical Complexity Hotspot
|
|
|
|
#### escapeCss() - CCN: 26 (CRITICAL)
|
|
**Location:** `javascript/selenium-webdriver/lib/by.js:67-110`
|
|
- **NLOC:** 39 lines
|
|
- **Issue:** Complex CSS selector escaping with many conditional branches
|
|
- **Token Count:** 230 (high complexity indicator)
|
|
- **Severity:** CRITICAL
|
|
|
|
**Current Implementation Characteristics:**
|
|
- 26 decision points in 44 lines
|
|
- Complex character-by-character processing
|
|
- Multiple nested conditionals for special character handling
|
|
|
|
**Refactoring Recommendations:**
|
|
1. **Immediate (Priority: CRITICAL)**
|
|
- Replace procedural logic with lookup table or map-based approach
|
|
- Use regular expression replacement with mapping function
|
|
- Split into smaller validation and transformation functions
|
|
- **Target CCN:** < 8 (reduction from 26 to < 8)
|
|
|
|
2. **Code Structure:**
|
|
```javascript
|
|
// Current: Procedural with high CCN
|
|
function escapeCss(selector) {
|
|
// 26 conditional branches...
|
|
}
|
|
|
|
// Recommended: Declarative approach
|
|
const CSS_ESCAPE_MAP = {
|
|
// character mapping
|
|
};
|
|
|
|
function escapeCss(selector) {
|
|
return selector.split('').map(escapeChar).join('');
|
|
}
|
|
|
|
function escapeChar(char) {
|
|
return CSS_ESCAPE_MAP[char] || char;
|
|
}
|
|
```
|
|
|
|
### JavaScript Strengths
|
|
- **Excellent modularization:** Average function length of 5.7 lines
|
|
- **Clean architecture:** Only 1 warning across entire codebase
|
|
- **Good separation of concerns:** Test utilities properly isolated
|
|
- **Promise handling:** Well-structured async patterns
|
|
|
|
---
|
|
|
|
## 3. C# Implementation Analysis
|
|
|
|
### Overview
|
|
- **Total Files:** 642 C# files
|
|
- **Total NLOC:** 22,897 non-comment lines of code
|
|
- **Functions Analyzed:** 1,741
|
|
- **Average Cyclomatic Complexity:** 2.1 (Good)
|
|
- **Average Function Length:** 8.7 lines
|
|
- **Warnings:** 11 complexity hotspots
|
|
|
|
### Top 10 Most Complex C# Files
|
|
|
|
| Rank | File | Lines | Primary Concerns |
|
|
|------|------|-------|------------------|
|
|
| 1 | `/dotnet/src/support/Events/EventFiringWebDriver.cs` | 1,910 | Event dispatcher pattern, repetitive code |
|
|
| 2 | `/dotnet/src/webdriver/WebDriver.cs` | 1,151 | Core driver, high coupling |
|
|
| 3 | `/dotnet/src/webdriver/WebElement.cs` | 747 | Element operations |
|
|
| 4 | `/dotnet/src/webdriver/Interactions/Actions.cs` | 675 | Action chain builder |
|
|
| 5 | `/dotnet/src/webdriver/Chromium/ChromiumOptions.cs` | 667 | Browser options (CCN 18) |
|
|
| 6 | `/dotnet/src/webdriver/DevTools/DevToolsSession.cs` | 662 | CDP session management |
|
|
| 7 | `/dotnet/src/webdriver/Remote/RemoteWebDriver.cs` | 620 | Remote execution |
|
|
| 8 | `/dotnet/src/webdriver/Interactions/PointerInputDevice.cs` | 619 | Pointer input handling |
|
|
| 9 | `/dotnet/src/webdriver/Proxy.cs` | 530 | Proxy configuration (CCN 46) |
|
|
| 10 | `/dotnet/src/webdriver/DriverOptions.cs` | 569 | Driver options (CCN 23) |
|
|
|
|
### Critical Complexity Hotspots (CCN > 15)
|
|
|
|
#### 1. Proxy Constructor - CCN: 46 (CRITICAL)
|
|
**Location:** `dotnet/src/webdriver/Proxy.cs:97-178`
|
|
- **NLOC:** 70 lines
|
|
- **Issue:** Massive constructor with complex validation and initialization
|
|
- **Severity:** CRITICAL - Highest complexity in C# codebase
|
|
|
|
**Recommendations:**
|
|
- **URGENT:** Split constructor into builder pattern
|
|
- Extract validation into separate validator class
|
|
- Use fluent configuration API
|
|
- **Target CCN:** < 5 per method
|
|
|
|
#### 2. WebDriver.UnpackAndThrowOnError() - CCN: 43
|
|
**Location:** `dotnet/src/webdriver/WebDriver.cs:711-828`
|
|
- **NLOC:** 86 lines
|
|
- **Issue:** Complex error unpacking with many exception types
|
|
- **Recommendations:**
|
|
- Create exception factory with type mapping
|
|
- Use dictionary-based exception resolution
|
|
- Extract error code handling to separate class
|
|
|
|
#### 3. Broker.ProcessReceivedMessage() - CCN: 26
|
|
**Location:** `dotnet/src/webdriver/BiDi/Broker.cs:205-324`
|
|
- **NLOC:** 97 lines
|
|
- **Issue:** Complex BiDi message routing
|
|
- **Recommendations:**
|
|
- Implement command pattern for message handlers
|
|
- Use message type registry with handler mapping
|
|
- Split into separate message processors
|
|
|
|
#### 4. DriverOptions.GenerateDesiredCapabilities() - CCN: 23
|
|
**Location:** `dotnet/src/webdriver/DriverOptions.cs:456-568`
|
|
- **NLOC:** 93 lines
|
|
- **Issue:** Complex capability generation with many conditions
|
|
- **Recommendations:**
|
|
- Extract capability generators per driver type
|
|
- Use capability builder with fluent interface
|
|
- Apply strategy pattern for driver-specific capabilities
|
|
|
|
#### 5. Cookie.FromDictionary() - CCN: 24
|
|
**Location:** `dotnet/src/webdriver/Cookie.cs:256-307`
|
|
- **NLOC:** 44 lines
|
|
- **Issue:** Complex dictionary parsing with validation
|
|
- **Recommendations:**
|
|
- Use automatic JSON deserialization
|
|
- Create CookieBuilder with validation
|
|
- Separate parsing from validation logic
|
|
|
|
### C# Specific Issues
|
|
|
|
**Event Handler Duplication:**
|
|
- `EventFiringWebDriver.cs` (1,910 lines) has significant code duplication
|
|
- 40+ similar event firing patterns
|
|
- Recommendation: Generate event dispatchers with source generators or T4 templates
|
|
|
|
**Long Methods:**
|
|
11 methods exceed 80 lines, including:
|
|
- `InternetExplorerOptions.BuildInternetExplorerOptionsDictionary()` (104 lines)
|
|
- `ChromiumOptions.BuildChromeOptionsDictionary()` (81 lines)
|
|
|
|
---
|
|
|
|
## 4. Python Implementation Analysis
|
|
|
|
### Overview
|
|
- **Total Files:** 261 Python files
|
|
- **Total NLOC:** 9,100 non-comment lines of code
|
|
- **Functions Analyzed:** 1,080
|
|
- **Average Cyclomatic Complexity:** 2.2 (Good)
|
|
- **Average Function Length:** 6.4 lines
|
|
- **Warnings:** 6 complexity hotspots
|
|
|
|
### Top 10 Most Complex Python Files
|
|
|
|
| Rank | File | Lines | Key Characteristics |
|
|
|------|------|-------|---------------------|
|
|
| 1 | `/py/selenium/webdriver/remote/webdriver.py` | 1,508 | Main WebDriver implementation |
|
|
| 2 | `/py/selenium/webdriver/common/bidi/browsing_context.py` | 1,060 | BiDi browsing context |
|
|
| 3 | `/py/generate.py` | 1,033 | Code generation utility |
|
|
| 4 | `/py/selenium/webdriver/support/expected_conditions.py` | 854 | Wait conditions |
|
|
| 5 | `/py/selenium/webdriver/remote/webelement.py` | 580 | WebElement implementation |
|
|
| 6 | `/py/selenium/webdriver/common/bidi/script.py` | 547 | BiDi script execution |
|
|
| 7 | `/py/conftest.py` | 546 | Pytest configuration |
|
|
| 8 | `/py/selenium/webdriver/common/bidi/cdp.py` | 509 | CDP integration |
|
|
| 9 | `/py/selenium/webdriver/remote/remote_connection.py` | 493 | HTTP connection handling |
|
|
| 10 | `/py/selenium/webdriver/common/bidi/input.py` | 462 | BiDi input handling |
|
|
|
|
### Critical Complexity Hotspots (CCN > 15)
|
|
|
|
#### 1. ErrorHandler.check_response() - CCN: 33 (CRITICAL)
|
|
**Location:** `py/selenium/webdriver/remote/errorhandler.py:145-232`
|
|
- **NLOC:** 75 lines
|
|
- **Issue:** Massive if-elif chain for error code handling
|
|
- **Token Count:** 514 (very high)
|
|
- **Severity:** CRITICAL
|
|
|
|
**Recommendations:**
|
|
1. **Replace if-elif chain with dictionary dispatch:**
|
|
```python
|
|
# Current: 33 conditional branches
|
|
if error_code == 'NoSuchElement':
|
|
raise NoSuchElementException(...)
|
|
elif error_code == 'StaleElement':
|
|
raise StaleElementReferenceException(...)
|
|
# ... 30+ more conditions
|
|
|
|
# Recommended: Dictionary dispatch
|
|
ERROR_HANDLERS = {
|
|
'NoSuchElement': NoSuchElementException,
|
|
'StaleElement': StaleElementReferenceException,
|
|
# ...
|
|
}
|
|
|
|
exception_class = ERROR_HANDLERS.get(error_code, WebDriverException)
|
|
raise exception_class(message)
|
|
```
|
|
2. **Target CCN:** < 5 (reduction from 33)
|
|
3. **Benefits:** 85% reduction in complexity, easier maintenance
|
|
|
|
#### 2. Proxy.__init__() - CCN: 22
|
|
**Location:** `py/selenium/webdriver/common/proxy.py:114-140`
|
|
- **NLOC:** 22 lines
|
|
- **Issue:** Complex constructor with validation
|
|
- **Recommendations:**
|
|
- Use dataclass or Pydantic for validation
|
|
- Extract validation to separate methods
|
|
- Consider using attrs library for automatic validation
|
|
|
|
#### 3. Script.__convert_to_local_value() - CCN: 23
|
|
**Location:** `py/selenium/webdriver/common/bidi/script.py:313-359`
|
|
- **NLOC:** 40 lines
|
|
- **Issue:** Type conversion with nested conditionals
|
|
- **Recommendations:**
|
|
- Use singledispatch for type-based conversion
|
|
- Create converter registry pattern
|
|
- Split into specific type converters
|
|
|
|
#### 4. RemoteConnection._request() - CCN: 16
|
|
**Location:** `py/selenium/webdriver/remote/remote_connection.py:407-467`
|
|
- **NLOC:** 45 lines
|
|
- **Issue:** Complex HTTP request handling with retries
|
|
- **Recommendations:**
|
|
- Extract retry logic to decorator
|
|
- Use requests library's built-in retry adapters
|
|
- Separate error handling from request logic
|
|
|
|
#### 5. FirefoxProfile._addon_details() - CCN: 16
|
|
**Location:** `py/selenium/webdriver/firefox/firefox_profile.py:223-328`
|
|
- **NLOC:** 47 lines
|
|
- **Issue:** Complex add-on parsing logic
|
|
- **Recommendations:**
|
|
- Use XML parsing library with XPath
|
|
- Extract validation to separate class
|
|
- Consider using lxml for cleaner parsing
|
|
|
|
#### 6. BrowsingContext.from_json() - CCN: 17
|
|
**Location:** `py/selenium/webdriver/common/bidi/browsing_context.py:114-167`
|
|
- **NLOC:** 38 lines
|
|
- **Issue:** JSON deserialization with validation
|
|
- **Recommendations:**
|
|
- Use Pydantic models for automatic validation
|
|
- Implement from_dict classmethod with marshmallow
|
|
- Apply dataclass with validation decorators
|
|
|
|
### Python Code Quality Observations
|
|
|
|
**Strengths:**
|
|
- Pythonic code with good use of comprehensions
|
|
- Effective use of context managers
|
|
- Good async/await patterns in BiDi implementation
|
|
|
|
**Areas for Improvement:**
|
|
- Some functions exceed 100 lines (particularly in test files)
|
|
- Error handling could use modern pattern matching (Python 3.10+)
|
|
- Opportunity to use Pydantic for type validation
|
|
|
|
---
|
|
|
|
## 5. Ruby Implementation Analysis
|
|
|
|
### Overview
|
|
- **Total Files:** 299 Ruby files
|
|
- **Total NLOC:** 7,962 non-comment lines of code
|
|
- **Functions Analyzed:** 1,056
|
|
- **Average Cyclomatic Complexity:** 1.7 (Excellent)
|
|
- **Average Function Length:** 5.3 lines (Best across all languages)
|
|
- **Warnings:** 0 (Perfect score - NO complexity warnings)
|
|
|
|
### Top 10 Most Complex Ruby Files
|
|
|
|
| Rank | File | Lines | Avg CCN | Quality |
|
|
|------|------|-------|---------|---------|
|
|
| 1 | `/rb/lib/selenium/webdriver/remote/bridge.rb` | 676 | 1.7 | Excellent |
|
|
| 2 | `/rb/lib/selenium/webdriver/common/element.rb` | 395 | 1.5 | Excellent |
|
|
| 3 | `/rb/lib/selenium/webdriver/common/interactions/pointer_actions.rb` | 351 | 1.6 | Excellent |
|
|
| 4 | `/rb/lib/selenium/webdriver/common/driver.rb` | 350 | 1.8 | Excellent |
|
|
| 5 | `/rb/lib/selenium/server.rb` | 276 | 1.5 | Excellent |
|
|
| 6 | `/rb/lib/selenium/webdriver/support/select.rb` | 272 | 2.3 | Good |
|
|
| 7 | `/rb/lib/selenium/webdriver/remote/capabilities.rb` | 271 | 1.4 | Excellent |
|
|
| 8 | `/rb/lib/selenium/webdriver/common/error.rb` | 251 | 1.3 | Excellent |
|
|
| 9 | `/rb/lib/selenium/webdriver/common/action_builder.rb` | 251 | 1.6 | Excellent |
|
|
| 10 | `/rb/lib/selenium/webdriver/chromium/options.rb` | 243 | 1.9 | Excellent |
|
|
|
|
### Ruby Excellence Analysis
|
|
|
|
**Why Ruby Implementation Excels:**
|
|
|
|
1. **Zero Complexity Warnings**
|
|
- No functions exceed CCN threshold of 15
|
|
- Highest complexity is CCN: 8 (Color.from_hsl in support/color.rb)
|
|
- Consistent adherence to Single Responsibility Principle
|
|
|
|
2. **Shortest Functions**
|
|
- Average of 5.3 lines per function (best across all implementations)
|
|
- Excellent method decomposition
|
|
- Ruby idioms used effectively (blocks, yield, inject)
|
|
|
|
3. **Best Practices Consistently Applied**
|
|
- Duck typing reduces conditional complexity
|
|
- Mixins and modules for code reuse
|
|
- Method chaining for fluent interfaces
|
|
- Effective use of Ruby metaprogramming
|
|
|
|
4. **Highest Complexity Function Analysis:**
|
|
|
|
**Color.from_hsl() - CCN: 8** (Still well below threshold)
|
|
**Location:** `rb/lib/selenium/webdriver/support/color.rb:75-95`
|
|
- **NLOC:** 18 lines
|
|
- **Assessment:** Acceptable complexity for HSL-to-RGB conversion
|
|
- **Note:** Mathematical algorithm inherently requires branching
|
|
|
|
5. **Module Organization:**
|
|
- Bridge pattern effectively isolates complexity
|
|
- Guard conditions prevent deep nesting
|
|
- Event listeners use clean observer pattern
|
|
|
|
### Ruby Best Practices Demonstrated
|
|
|
|
**Code Examples Worth Emulating:**
|
|
|
|
1. **Guard Clauses:**
|
|
```ruby
|
|
def select_by_text(text)
|
|
return unless multiple?
|
|
find_by_text(text).each(&:click)
|
|
end
|
|
```
|
|
|
|
2. **Method Extraction:**
|
|
- Complex operations broken into 3-5 line methods
|
|
- Descriptive method names replace comments
|
|
|
|
3. **Block Usage:**
|
|
```ruby
|
|
options.select { |opt| opt.selected? }
|
|
```
|
|
|
|
### Ruby Recommendations
|
|
|
|
**Maintain Excellence:**
|
|
- Continue current development practices
|
|
- Use Ruby's strengths in other implementations where applicable
|
|
- Document patterns for other language implementations
|
|
|
|
**Minor Optimization Opportunities:**
|
|
1. `Color.from_string()` (CCN: 8) - Consider pattern matching for color format detection
|
|
2. `Select.find_by_text()` (CCN: 3) - Already excellent, no changes needed
|
|
3. Keep all methods under 20 lines (currently achieved)
|
|
|
|
---
|
|
|
|
## 6. Cross-Language Complexity Comparison
|
|
|
|
### Comparative Analysis
|
|
|
|
| Metric | Java | JavaScript | C# | Python | Ruby |
|
|
|--------|------|------------|-----|--------|------|
|
|
| **Code Volume** | 62,880 | 4,933 | 22,897 | 9,100 | 7,962 |
|
|
| **Avg Complexity** | 1.8 | 1.7 | 2.1 | 2.2 | 1.7 |
|
|
| **Max CCN** | 30 | 26 | 46 | 33 | 8 |
|
|
| **Functions/File** | 4.3 | 2.5 | 2.7 | 4.1 | 3.5 |
|
|
| **Warnings** | 21 | 1 | 11 | 6 | 0 |
|
|
| **Quality Grade** | B+ | A | A- | A | A+ |
|
|
|
|
### Architecture Patterns
|
|
|
|
#### Common Complexity Sources (All Languages)
|
|
1. **Option/Configuration Classes**
|
|
- Java: `FirefoxOptions`, `ChromiumOptions`
|
|
- C#: `ChromiumOptions`, `Proxy`
|
|
- Python: `Proxy.__init__`
|
|
- JavaScript: Capabilities handling
|
|
|
|
2. **Error Handling**
|
|
- Python: `ErrorHandler.check_response()` (CCN: 33)
|
|
- C#: `WebDriver.UnpackAndThrowOnError()` (CCN: 43)
|
|
- Java: Exception mapping in various classes
|
|
|
|
3. **Protocol Adapters**
|
|
- Java: Docker adapters (CCN: 20)
|
|
- C#: BiDi broker (CCN: 26)
|
|
- Python: BiDi script conversions (CCN: 23)
|
|
|
|
### Language-Specific Patterns
|
|
|
|
**Java:**
|
|
- Heaviest use of design patterns (Strategy, Builder, Adapter)
|
|
- Largest class sizes (1,300+ lines common)
|
|
- Most comprehensive test coverage
|
|
|
|
**JavaScript:**
|
|
- Most concise implementation
|
|
- Effective use of promises and async/await
|
|
- Clean separation of concerns
|
|
|
|
**C#:**
|
|
- Strong use of events and delegates
|
|
- Good LINQ usage reduces complexity
|
|
- Heavy constructor complexity (anti-pattern)
|
|
|
|
**Python:**
|
|
- Dictionary dispatch pattern underutilized
|
|
- Opportunity for dataclasses/Pydantic
|
|
- Good use of context managers
|
|
|
|
**Ruby:**
|
|
- Best method decomposition
|
|
- Excellent use of blocks and yield
|
|
- Most maintainable code structure
|
|
|
|
---
|
|
|
|
## 7. Complexity Hotspots by Category
|
|
|
|
### Category 1: Configuration & Options (HIGH PRIORITY)
|
|
|
|
**Files Requiring Immediate Attention:**
|
|
|
|
1. **C# Proxy Class - CCN: 46** ⚠️ CRITICAL
|
|
- File: `dotnet/src/webdriver/Proxy.cs`
|
|
- Issue: Constructor god-object anti-pattern
|
|
- Impact: High - Core functionality, difficult to test
|
|
- Effort: Medium (2-3 days)
|
|
- **Action:** Implement builder pattern immediately
|
|
|
|
2. **Java FirefoxOptions - CCN: 30** ⚠️ HIGH
|
|
- File: `java/src/org/openqa/selenium/firefox/FirefoxOptions.java`
|
|
- Issue: Complex merge logic
|
|
- Impact: Medium - Browser-specific, isolated
|
|
- Effort: Medium (2 days)
|
|
- **Action:** Extract strategy pattern for option merging
|
|
|
|
3. **C# DriverOptions - CCN: 23** ⚠️ MEDIUM
|
|
- File: `dotnet/src/webdriver/DriverOptions.cs`
|
|
- Issue: Capability generation complexity
|
|
- Impact: High - Used by all drivers
|
|
- Effort: Medium (2 days)
|
|
- **Action:** Create capability builder pipeline
|
|
|
|
### Category 2: Error Handling (HIGH PRIORITY)
|
|
|
|
**Files Requiring Immediate Attention:**
|
|
|
|
1. **C# WebDriver.UnpackAndThrowOnError() - CCN: 43** ⚠️ CRITICAL
|
|
- File: `dotnet/src/webdriver/WebDriver.cs`
|
|
- Issue: Massive error unpacking logic
|
|
- Impact: High - Core error handling path
|
|
- Effort: Low (1 day)
|
|
- **Action:** Dictionary-based exception factory
|
|
|
|
2. **Python ErrorHandler.check_response() - CCN: 33** ⚠️ CRITICAL
|
|
- File: `py/selenium/webdriver/remote/errorhandler.py`
|
|
- Issue: If-elif chain of 33 branches
|
|
- Impact: High - All remote commands affected
|
|
- Effort: Low (1 day)
|
|
- **Action:** Replace with dictionary dispatch
|
|
|
|
### Category 3: Protocol Handling (MEDIUM PRIORITY)
|
|
|
|
1. **C# BiDi Broker - CCN: 26** ⚠️ MEDIUM
|
|
- File: `dotnet/src/webdriver/BiDi/Broker.cs`
|
|
- Issue: Message routing complexity
|
|
- Impact: Medium - BiDi-specific
|
|
- Effort: Medium (2 days)
|
|
|
|
2. **JavaScript escapeCss() - CCN: 26** ⚠️ MEDIUM
|
|
- File: `javascript/selenium-webdriver/lib/by.js`
|
|
- Issue: Character-by-character escaping
|
|
- Impact: Low - Locator utility
|
|
- Effort: Low (1 day)
|
|
|
|
3. **Python Script Conversion - CCN: 23** ⚠️ MEDIUM
|
|
- File: `py/selenium/webdriver/common/bidi/script.py`
|
|
- Issue: Type conversion complexity
|
|
- Impact: Medium - BiDi script execution
|
|
- Effort: Medium (1-2 days)
|
|
|
|
### Category 4: Code Duplication (LOW PRIORITY)
|
|
|
|
1. **C# EventFiringWebDriver (1,910 lines)**
|
|
- 40+ similar event patterns
|
|
- Recommendation: Source generator or T4 templates
|
|
- Impact: Low - Support library
|
|
- Effort: High (1 week)
|
|
|
|
2. **Java ExpectedConditions (1,527 lines)**
|
|
- 42 similar condition patterns
|
|
- Recommendation: Split into domain-specific classes
|
|
- Impact: Low - Utility class
|
|
- Effort: High (1 week)
|
|
|
|
---
|
|
|
|
## 8. Prioritized Refactoring Roadmap
|
|
|
|
### Phase 1: Critical Issues (Sprint 1 - 2 weeks)
|
|
|
|
**Priority 1A - Error Handling (Week 1)**
|
|
1. Python `ErrorHandler.check_response()` - Dictionary dispatch
|
|
2. C# `WebDriver.UnpackAndThrowOnError()` - Exception factory
|
|
3. Estimated effort: 2 days
|
|
4. Impact: High - Improves maintainability of core error paths
|
|
|
|
**Priority 1B - Configuration (Week 2)**
|
|
1. C# `Proxy` constructor - Builder pattern
|
|
2. C# `DriverOptions.GenerateDesiredCapabilities()` - Capability builder
|
|
3. Estimated effort: 4 days
|
|
4. Impact: High - Improves testability and configuration flexibility
|
|
|
|
**Expected Outcomes:**
|
|
- Reduce critical hotspots from 4 to 0
|
|
- Improve test coverage for error handling by 30%
|
|
- Decrease configuration bugs by 50%
|
|
|
|
### Phase 2: High-Priority Issues (Sprint 2 - 2 weeks)
|
|
|
|
**Priority 2A - Browser Options (Week 3)**
|
|
1. Java `FirefoxOptions.merge()` - Strategy pattern
|
|
2. Java `ChromiumOptions.mergeInPlace()` - Template method
|
|
3. C# `ChromiumOptions.BuildChromeOptionsDictionary()` - Builder
|
|
4. Estimated effort: 6 days
|
|
5. Impact: Medium - Reduces browser-specific complexity
|
|
|
|
**Priority 2B - Protocol Adapters (Week 4)**
|
|
1. C# `Broker.ProcessReceivedMessage()` - Command pattern
|
|
2. JavaScript `escapeCss()` - Lookup table
|
|
3. Python `Script.__convert_to_local_value()` - Singledispatch
|
|
4. Estimated effort: 4 days
|
|
5. Impact: Medium - Improves protocol handling
|
|
|
|
**Expected Outcomes:**
|
|
- Reduce high-priority hotspots from 7 to 0
|
|
- Improve option merging performance by 20%
|
|
- Simplify protocol handling for future features
|
|
|
|
### Phase 3: Code Organization (Sprint 3-4 - 4 weeks)
|
|
|
|
**Priority 3A - Large Class Decomposition**
|
|
1. Java `RemoteWebDriver` (1,323 lines) - Extract capabilities, session management
|
|
2. Java `LocalNode` (1,310 lines) - Extract session lifecycle, slot management
|
|
3. C# `EventFiringWebDriver` (1,910 lines) - Source generator for events
|
|
4. Estimated effort: 2 weeks
|
|
5. Impact: Medium - Long-term maintainability
|
|
|
|
**Priority 3B - Duplicate Code Elimination**
|
|
1. Java `ExpectedConditions` - Split by domain (Element, Alert, Frame, etc.)
|
|
2. Python BiDi classes - Extract common patterns
|
|
3. C# WebDriver element finding methods - Generics
|
|
4. Estimated effort: 2 weeks
|
|
5. Impact: Low-Medium - Code reuse, reduced bugs
|
|
|
|
**Expected Outcomes:**
|
|
- Reduce average class size by 30%
|
|
- Eliminate 500+ lines of duplicate code
|
|
- Improve code discoverability
|
|
|
|
### Phase 4: Technical Debt Cleanup (Ongoing)
|
|
|
|
**Continuous Improvement:**
|
|
1. Enforce complexity thresholds in CI/CD
|
|
- CCN limit: 15 (warning), 20 (error)
|
|
- Function length: 50 lines (warning), 100 (error)
|
|
- Class length: 500 lines (warning), 1000 (error)
|
|
|
|
2. Add complexity gates to code review
|
|
- Automated complexity checks via lizard
|
|
- Reject PRs that increase complexity without justification
|
|
- Track complexity trends over time
|
|
|
|
3. Refactoring sprints
|
|
- Dedicate 20% of sprint capacity to technical debt
|
|
- Target one complexity hotspot per sprint
|
|
- Document refactoring patterns for team learning
|
|
|
|
---
|
|
|
|
## 9. Specific Refactoring Recommendations
|
|
|
|
### Recommendation 1: Dictionary Dispatch Pattern
|
|
|
|
**Problem:** Large if-elif/switch chains for type/error mapping
|
|
|
|
**Languages Affected:** Python (ErrorHandler), Java (various), C# (Proxy)
|
|
|
|
**Solution Pattern:**
|
|
```python
|
|
# Before (CCN: 33)
|
|
def check_response(response):
|
|
if error_code == 'NoSuchElement':
|
|
raise NoSuchElementException(...)
|
|
elif error_code == 'StaleElement':
|
|
raise StaleElementReferenceException(...)
|
|
# ... 30 more conditions
|
|
|
|
# After (CCN: 3)
|
|
ERROR_MAP = {
|
|
'NoSuchElement': NoSuchElementException,
|
|
'StaleElement': StaleElementReferenceException,
|
|
# ...
|
|
}
|
|
|
|
def check_response(response):
|
|
exception_class = ERROR_MAP.get(error_code, WebDriverException)
|
|
raise exception_class(message, screen, stacktrace)
|
|
```
|
|
|
|
**Benefits:**
|
|
- Reduces CCN from 33 to 3 (90% reduction)
|
|
- Easier to add new error types
|
|
- More testable
|
|
- Self-documenting
|
|
|
|
**Estimated Effort:** 1-2 hours per class
|
|
**Risk:** Low - Behavior-preserving refactoring
|
|
|
|
### Recommendation 2: Builder Pattern for Complex Configuration
|
|
|
|
**Problem:** Constructors/methods with high parameter count and validation
|
|
|
|
**Languages Affected:** C# (Proxy, DriverOptions), Java (Options classes)
|
|
|
|
**Solution Pattern:**
|
|
```csharp
|
|
// Before (CCN: 46)
|
|
public Proxy(...12 parameters with complex validation)
|
|
{
|
|
// 70 lines of validation and initialization
|
|
}
|
|
|
|
// After (CCN: 2-3 per method)
|
|
public class ProxyBuilder
|
|
{
|
|
private ProxyType type;
|
|
private string httpProxy;
|
|
// ...
|
|
|
|
public ProxyBuilder WithType(ProxyType type)
|
|
{
|
|
this.type = ValidateType(type);
|
|
return this;
|
|
}
|
|
|
|
public ProxyBuilder WithHttpProxy(string proxy)
|
|
{
|
|
this.httpProxy = ValidateProxy(proxy);
|
|
return this;
|
|
}
|
|
|
|
public Proxy Build()
|
|
{
|
|
return new Proxy(this);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Splits complexity across multiple simple methods
|
|
- Fluent interface improves readability
|
|
- Validation isolated per field
|
|
- Easier to test individual validations
|
|
|
|
**Estimated Effort:** 3-4 hours per class
|
|
**Risk:** Medium - API change (can wrap existing constructor)
|
|
|
|
### Recommendation 3: Strategy Pattern for Option Merging
|
|
|
|
**Problem:** Complex merge logic with many conditionals
|
|
|
|
**Languages Affected:** Java (FirefoxOptions, ChromiumOptions)
|
|
|
|
**Solution Pattern:**
|
|
```java
|
|
// Before (CCN: 30)
|
|
public MutableCapabilities merge(Capabilities options) {
|
|
if (options has firefox profile) {
|
|
// complex merge logic
|
|
} else if (options has preferences) {
|
|
// complex merge logic
|
|
}
|
|
// ... 28 more conditions
|
|
}
|
|
|
|
// After (CCN: 3)
|
|
interface OptionMergeStrategy {
|
|
void merge(MutableCapabilities target, Capabilities source);
|
|
}
|
|
|
|
class ProfileMergeStrategy implements OptionMergeStrategy { ... }
|
|
class PreferenceMergeStrategy implements OptionMergeStrategy { ... }
|
|
|
|
public MutableCapabilities merge(Capabilities options) {
|
|
for (OptionMergeStrategy strategy : strategies) {
|
|
strategy.merge(this, options);
|
|
}
|
|
return this;
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Each strategy is simple and focused (CCN: 2-3)
|
|
- Easy to add new option types
|
|
- Testable in isolation
|
|
- Follows Open-Closed Principle
|
|
|
|
**Estimated Effort:** 1 day per options class
|
|
**Risk:** Low - Internal refactoring
|
|
|
|
### Recommendation 4: Command Pattern for Message Routing
|
|
|
|
**Problem:** Large switch/if-elif for message type handling
|
|
|
|
**Languages Affected:** C# (BiDi Broker), Java (Protocol handlers)
|
|
|
|
**Solution Pattern:**
|
|
```csharp
|
|
// Before (CCN: 26)
|
|
void ProcessReceivedMessage(string message) {
|
|
if (message.method == "session.new") {
|
|
// handle session.new
|
|
} else if (message.method == "browsingContext.load") {
|
|
// handle context.load
|
|
}
|
|
// ... 24 more conditions
|
|
}
|
|
|
|
// After (CCN: 2)
|
|
interface IMessageHandler {
|
|
void Handle(JsonElement message);
|
|
}
|
|
|
|
Dictionary<string, IMessageHandler> handlers = new() {
|
|
["session.new"] = new SessionNewHandler(),
|
|
["browsingContext.load"] = new ContextLoadHandler(),
|
|
// ...
|
|
};
|
|
|
|
void ProcessReceivedMessage(string message) {
|
|
var method = GetMethod(message);
|
|
handlers[method].Handle(message);
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Handlers are isolated and testable
|
|
- Easy to add new message types
|
|
- Complexity distributed across classes
|
|
- Supports async handlers easily
|
|
|
|
**Estimated Effort:** 2-3 days
|
|
**Risk:** Low - Internal architecture
|
|
|
|
### Recommendation 5: Lookup Table for Character Escaping
|
|
|
|
**Problem:** Character-by-character processing with many conditions
|
|
|
|
**Languages Affected:** JavaScript (escapeCss)
|
|
|
|
**Solution Pattern:**
|
|
```javascript
|
|
// Before (CCN: 26)
|
|
function escapeCss(selector) {
|
|
let result = '';
|
|
for (let char of selector) {
|
|
if (char === '\\') result += '\\\\';
|
|
else if (char === '"') result += '\\"';
|
|
// ... 24 more conditions
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// After (CCN: 2)
|
|
const CSS_ESCAPE_MAP = {
|
|
'\\': '\\\\',
|
|
'"': '\\"',
|
|
// ... all mappings
|
|
};
|
|
|
|
function escapeCss(selector) {
|
|
return selector
|
|
.split('')
|
|
.map(c => CSS_ESCAPE_MAP[c] || c)
|
|
.join('');
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Declarative and self-documenting
|
|
- 90% reduction in complexity
|
|
- Easier to maintain escape rules
|
|
- Better performance (no branches)
|
|
|
|
**Estimated Effort:** 30 minutes
|
|
**Risk:** Very Low - Pure function
|
|
|
|
---
|
|
|
|
## 10. Implementation Guidelines
|
|
|
|
### Complexity Thresholds
|
|
|
|
Recommended thresholds for new code and refactoring targets:
|
|
|
|
| Metric | Good | Warning | Critical | Action |
|
|
|--------|------|---------|----------|--------|
|
|
| Cyclomatic Complexity (CCN) | < 10 | 10-15 | > 15 | Refactor immediately |
|
|
| Function Length (NLOC) | < 30 | 30-50 | > 50 | Consider splitting |
|
|
| Class Length (NLOC) | < 300 | 300-500 | > 500 | Decompose |
|
|
| Parameter Count | < 4 | 4-6 | > 6 | Use object/builder |
|
|
| Nesting Depth | < 3 | 3-4 | > 4 | Extract methods |
|
|
|
|
### CI/CD Integration
|
|
|
|
**Recommended Tools:**
|
|
1. **Lizard** - Multi-language complexity analysis (already used)
|
|
2. **SonarQube** - Continuous code quality inspection
|
|
3. **CodeClimate** - Automated code review
|
|
|
|
**Integration Steps:**
|
|
```bash
|
|
# Add to CI pipeline
|
|
lizard -l java -w --CCN 15 java/src/
|
|
lizard -l javascript -w --CCN 15 javascript/
|
|
lizard -l csharp -w --CCN 15 dotnet/src/
|
|
lizard -l python -w --CCN 15 py/
|
|
lizard -l ruby -w --CCN 15 rb/lib/
|
|
|
|
# Fail build if warnings found
|
|
if [ $? -ne 0 ]; then
|
|
echo "Complexity thresholds exceeded"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
### Code Review Checklist
|
|
|
|
When reviewing code for complexity:
|
|
|
|
- [ ] No functions exceed CCN of 15
|
|
- [ ] No functions exceed 50 lines
|
|
- [ ] No classes exceed 500 lines
|
|
- [ ] No methods have more than 4 parameters
|
|
- [ ] No nesting deeper than 3 levels
|
|
- [ ] Complex logic has extracted helper methods
|
|
- [ ] Switch/if-elif chains replaced with dispatch tables
|
|
- [ ] Validation separated from business logic
|
|
- [ ] Each method has single responsibility
|
|
|
|
### Refactoring Process
|
|
|
|
**Step-by-Step Approach:**
|
|
|
|
1. **Identify:** Use lizard to find complexity hotspots
|
|
2. **Prioritize:** Based on impact and effort (see roadmap)
|
|
3. **Test:** Ensure comprehensive test coverage exists
|
|
4. **Extract:** Start with smallest unit of complexity
|
|
5. **Verify:** Run full test suite
|
|
6. **Measure:** Confirm complexity reduction
|
|
7. **Document:** Update architecture docs
|
|
|
|
**Safety Measures:**
|
|
- Never refactor without tests
|
|
- Use feature flags for large refactors
|
|
- Refactor in small, reviewable commits
|
|
- Monitor production metrics after deployment
|
|
- Keep rollback plan ready
|
|
|
|
---
|
|
|
|
## 11. Methodology and Nesting Analysis
|
|
|
|
### Analysis Methodology
|
|
|
|
**Tools Used:**
|
|
- **Lizard v1.19.0:** Multi-language code complexity analyzer
|
|
- **Manual Review:** Deep dive into top 10 most complex files per language
|
|
- **Statistical Analysis:** Aggregated metrics across 3,564 source files
|
|
|
|
**Metrics Calculated:**
|
|
1. **Cyclomatic Complexity (CCN):** Number of independent paths through code
|
|
2. **NLOC:** Non-comment lines of code
|
|
3. **Token Count:** Total language tokens (indicator of cognitive load)
|
|
4. **Parameter Count:** Number of function parameters
|
|
5. **Function Length:** Total lines per function
|
|
|
|
**Thresholds Applied:**
|
|
- CCN > 15: Warning (needs review)
|
|
- CCN > 20: Critical (requires refactoring)
|
|
- Function length > 50: Warning
|
|
- Function length > 100: Critical
|
|
|
|
### Nesting Depth Analysis
|
|
|
|
**Observed Nesting Patterns:**
|
|
|
|
#### Java Implementation
|
|
**Average Nesting Depth:** 2.3 levels
|
|
**Max Nesting Depth:** 5 levels (in FirefoxOptions.merge)
|
|
|
|
**Example High-Nesting Case:**
|
|
```java
|
|
// FirefoxOptions.merge() - 5 levels deep
|
|
if (options instanceof FirefoxOptions) { // Level 1
|
|
if (other.profile != null) { // Level 2
|
|
if (this.profile != null) { // Level 3
|
|
if (!this.profile.equals(other)) { // Level 4
|
|
if (shouldMerge) { // Level 5
|
|
// merge logic
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Recommendation:**
|
|
- Apply guard clauses to reduce nesting
|
|
- Extract nested blocks into separate methods
|
|
- Target max nesting of 3 levels
|
|
|
|
#### JavaScript Implementation
|
|
**Average Nesting Depth:** 1.8 levels
|
|
**Max Nesting Depth:** 4 levels (in escapeCss)
|
|
|
|
**Strength:** JavaScript's functional style reduces nesting through:
|
|
- Array methods (map, filter, reduce)
|
|
- Early returns
|
|
- Promise chains instead of callback pyramids
|
|
|
|
#### C# Implementation
|
|
**Average Nesting Depth:** 2.5 levels
|
|
**Max Nesting Depth:** 6 levels (in Proxy constructor)
|
|
|
|
**High-Nesting Example:**
|
|
```csharp
|
|
// Proxy constructor - 6 levels
|
|
if (proxyType != ProxyType.Unspecified) { // Level 1
|
|
if (httpProxy != null) { // Level 2
|
|
if (IsValidProxy(httpProxy)) { // Level 3
|
|
if (!string.IsNullOrEmpty(proxyAutoconfig)) { // Level 4
|
|
if (proxyAutoconfig.StartsWith("http")) { // Level 5
|
|
if (ValidateAutoconfig(proxyAutoconfig)) { // Level 6
|
|
// configuration logic
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Recommendation:**
|
|
- Use LINQ to flatten validation chains
|
|
- Apply null-conditional operators (?.)
|
|
- Extract validation to separate methods
|
|
|
|
#### Python Implementation
|
|
**Average Nesting Depth:** 2.1 levels
|
|
**Max Nesting Depth:** 5 levels (in BiDi script conversion)
|
|
|
|
**Strength:** Python's syntax encourages flat code:
|
|
- List comprehensions
|
|
- Generator expressions
|
|
- Context managers
|
|
- Early returns with guard clauses
|
|
|
|
**Example of Good Practice:**
|
|
```python
|
|
# Python: Flat structure with early returns
|
|
def process_element(element):
|
|
if element is None:
|
|
return None
|
|
|
|
if not element.is_enabled():
|
|
return None
|
|
|
|
return element.get_attribute('value')
|
|
```
|
|
|
|
#### Ruby Implementation
|
|
**Average Nesting Depth:** 1.6 levels (BEST)
|
|
**Max Nesting Depth:** 3 levels
|
|
|
|
**Excellence Factors:**
|
|
- Blocks and yield reduce nesting naturally
|
|
- Guard clauses used consistently
|
|
- Method chaining for fluent APIs
|
|
- Effective use of `unless` for negative conditions
|
|
|
|
**Example Ruby Excellence:**
|
|
```ruby
|
|
# Ruby: Minimal nesting with blocks
|
|
def select_by_value(value)
|
|
return unless multiple?
|
|
|
|
find_by_value(value).each(&:click)
|
|
end
|
|
```
|
|
|
|
### Nesting Reduction Techniques
|
|
|
|
**Technique 1: Guard Clauses (Early Return)**
|
|
```
|
|
Before (4 levels): After (2 levels):
|
|
if (valid) { if (!valid) return;
|
|
if (enabled) { if (!enabled) return;
|
|
if (ready) { if (!ready) return;
|
|
if (authorized) { if (!authorized) return;
|
|
doWork();
|
|
} doWork();
|
|
}
|
|
}
|
|
}
|
|
```
|
|
**Benefit:** Reduces cognitive load by 50%
|
|
|
|
**Technique 2: Extract Method**
|
|
```
|
|
Before (5 levels): After (2 levels):
|
|
if (condition1) { if (shouldProcess()) {
|
|
if (condition2) { doWork();
|
|
if (condition3) { }
|
|
if (condition4) {
|
|
doWork(); private bool shouldProcess() {
|
|
} return condition1 &&
|
|
} condition2 &&
|
|
} condition3 &&
|
|
} condition4;
|
|
}
|
|
```
|
|
**Benefit:** Improves readability and testability
|
|
|
|
**Technique 3: Polymorphism**
|
|
```
|
|
Before (4 levels): After (1 level):
|
|
if (type == "A") { strategyMap[type].execute();
|
|
if (subtype == "1") {
|
|
// ... class Strategy_A1 {
|
|
} execute() { ... }
|
|
} else if (type == "B") { }
|
|
// ...
|
|
}
|
|
```
|
|
**Benefit:** Eliminates nesting entirely
|
|
|
|
### Cognitive Complexity vs Cyclomatic Complexity
|
|
|
|
**Key Differences:**
|
|
|
|
| Metric | Cyclomatic | Cognitive |
|
|
|--------|-----------|-----------|
|
|
| **Measures** | Paths through code | Human comprehension difficulty |
|
|
| **Nesting** | No weight | Heavy weight (+1 per level) |
|
|
| **Breaks/Continues** | +1 per statement | Lower weight |
|
|
| **Logical Operators** | No penalty | +1 per operator |
|
|
|
|
**Example:**
|
|
```java
|
|
// CCN: 3, Cognitive: 7
|
|
if (a && b) { // CCN +1, Cognitive +1
|
|
if (c) { // CCN +1, Cognitive +2 (nested)
|
|
if (d) { // CCN +1, Cognitive +3 (deeper nest)
|
|
return x; // Cognitive +1 (nested return)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Recommendation:** Monitor both metrics
|
|
- CCN for test coverage planning
|
|
- Cognitive for refactoring priority
|
|
|
|
---
|
|
|
|
## 12. Code Duplication Analysis
|
|
|
|
### Major Duplication Patterns
|
|
|
|
#### Pattern 1: Event Firing Boilerplate
|
|
|
|
**Location:** C# EventFiringWebDriver (1,910 lines)
|
|
|
|
**Duplication:** 40+ similar event firing patterns
|
|
```csharp
|
|
// Repeated 40+ times with variations
|
|
public void Click()
|
|
{
|
|
OnElementClicking();
|
|
try
|
|
{
|
|
wrappedElement.Click();
|
|
}
|
|
finally
|
|
{
|
|
OnElementClicked();
|
|
}
|
|
}
|
|
```
|
|
|
|
**Analysis:**
|
|
- **Duplicated Lines:** ~800 lines (42% of file)
|
|
- **Pattern Repetition:** 40 methods with identical structure
|
|
- **Maintenance Cost:** High - changes require 40 updates
|
|
|
|
**Solution: Source Generator (C#)**
|
|
```csharp
|
|
// Use Roslyn source generator
|
|
[GenerateEventWrapper(typeof(IWebElement))]
|
|
public partial class EventFiringWebDriver
|
|
{
|
|
// Generator creates 40 wrapper methods automatically
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Reduces 800 lines to 50 lines of configuration
|
|
- Compile-time generation ensures type safety
|
|
- Single point of change for all events
|
|
|
|
**Estimated Effort:** 1 week
|
|
**Impact:** High - improves maintainability significantly
|
|
|
|
#### Pattern 2: Expected Conditions Factory Methods
|
|
|
|
**Location:** Java ExpectedConditions (1,527 lines)
|
|
|
|
**Duplication:** 42 similar condition factory methods
|
|
```java
|
|
// Repeated 42 times with variations
|
|
public static ExpectedCondition<Boolean> titleIs(final String title) {
|
|
return new ExpectedCondition<Boolean>() {
|
|
@Override
|
|
public Boolean apply(WebDriver driver) {
|
|
return title.equals(driver.getTitle());
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "title to be: " + title;
|
|
}
|
|
};
|
|
}
|
|
```
|
|
|
|
**Analysis:**
|
|
- **Duplicated Lines:** ~600 lines (39% of file)
|
|
- **Pattern Repetition:** Similar structure, different predicates
|
|
- **Complexity:** Each method is simple, but file is overwhelming
|
|
|
|
**Solution: Generic Condition Builder**
|
|
```java
|
|
public class ExpectedConditions {
|
|
public static <T> ExpectedCondition<T> condition(
|
|
Function<WebDriver, T> predicate,
|
|
String description
|
|
) {
|
|
return new ExpectedCondition<T>() {
|
|
@Override
|
|
public T apply(WebDriver driver) {
|
|
return predicate.apply(driver);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return description;
|
|
}
|
|
};
|
|
}
|
|
|
|
// Usage
|
|
public static ExpectedCondition<Boolean> titleIs(String title) {
|
|
return condition(
|
|
driver -> title.equals(driver.getTitle()),
|
|
"title to be: " + title
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Alternative Solution: Domain-Specific Classes**
|
|
```java
|
|
// Split into logical groups
|
|
class ElementConditions {
|
|
public static ExpectedCondition<WebElement> elementToBeClickable(By locator) { ... }
|
|
public static ExpectedCondition<Boolean> elementExists(By locator) { ... }
|
|
}
|
|
|
|
class AlertConditions {
|
|
public static ExpectedCondition<Alert> alertIsPresent() { ... }
|
|
}
|
|
|
|
class FrameConditions {
|
|
public static ExpectedCondition<WebDriver> frameToBeAvailable(String name) { ... }
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Improves discoverability
|
|
- Reduces cognitive load (smaller files)
|
|
- Easier to maintain related conditions
|
|
|
|
**Estimated Effort:** 1 week
|
|
**Impact:** Medium - improves code organization
|
|
|
|
#### Pattern 3: Element Finding Methods
|
|
|
|
**Location:** Multiple files across all languages
|
|
|
|
**Duplication:** 8 nearly identical methods per driver class
|
|
```csharp
|
|
// C# example - repeated 8 times
|
|
public IWebElement FindElementById(string id)
|
|
{
|
|
return FindElement(By.Id(id));
|
|
}
|
|
|
|
public IWebElement FindElementByName(string name)
|
|
{
|
|
return FindElement(By.Name(name));
|
|
}
|
|
|
|
// ... 6 more identical patterns
|
|
```
|
|
|
|
**Analysis:**
|
|
- **Duplicated Lines:** ~150 lines per driver class
|
|
- **Languages Affected:** Java, C#, Python, Ruby
|
|
- **Total Duplication:** ~600 lines across all implementations
|
|
|
|
**Solution: Remove FindBy* Methods**
|
|
```csharp
|
|
// Modern approach - use By locators directly
|
|
// OLD: driver.FindElementById("username")
|
|
// NEW: driver.FindElement(By.Id("username"))
|
|
|
|
// Keep only:
|
|
IWebElement FindElement(By by);
|
|
IReadOnlyCollection<IWebElement> FindElements(By by);
|
|
```
|
|
|
|
**Migration Path:**
|
|
1. Deprecate FindBy* methods
|
|
2. Provide extension methods for compatibility
|
|
3. Remove in next major version
|
|
|
|
**Benefits:**
|
|
- Removes ~600 lines of duplicate code
|
|
- Encourages better locator practices
|
|
- Simplifies driver interfaces
|
|
|
|
**Estimated Effort:** Low (deprecation notice only)
|
|
**Impact:** Low - Breaking change in major version only
|
|
|
|
#### Pattern 4: Browser Option Building
|
|
|
|
**Location:** Java/C# option classes
|
|
|
|
**Duplication:** Similar dictionary/map building code
|
|
```java
|
|
// Java ChromiumOptions
|
|
Map<String, Object> chromeOptions = new HashMap<>();
|
|
if (binary != null) chromeOptions.put("binary", binary);
|
|
if (args != null) chromeOptions.put("args", args);
|
|
if (extensions != null) chromeOptions.put("extensions", extensions);
|
|
// ... repeated 15 times
|
|
|
|
// Java FirefoxOptions
|
|
Map<String, Object> firefoxOptions = new HashMap<>();
|
|
if (profile != null) firefoxOptions.put("profile", profile);
|
|
if (preferences != null) firefoxOptions.put("prefs", preferences);
|
|
// ... repeated 12 times
|
|
```
|
|
|
|
**Analysis:**
|
|
- **Duplicated Lines:** ~200 lines across option classes
|
|
- **Pattern:** Null-checking before map insertion
|
|
- **Complexity:** Adds to method CCN unnecessarily
|
|
|
|
**Solution: Builder Utility**
|
|
```java
|
|
class OptionsBuilder {
|
|
private Map<String, Object> options = new HashMap<>();
|
|
|
|
public OptionsBuilder add(String key, Object value) {
|
|
if (value != null) {
|
|
options.put(key, value);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
public Map<String, Object> build() {
|
|
return options;
|
|
}
|
|
}
|
|
|
|
// Usage
|
|
return new OptionsBuilder()
|
|
.add("binary", binary)
|
|
.add("args", args)
|
|
.add("extensions", extensions)
|
|
.build();
|
|
```
|
|
|
|
**Benefits:**
|
|
- Reduces 15 if-statements to 1 null check
|
|
- Decreases CCN significantly
|
|
- More readable and maintainable
|
|
|
|
**Estimated Effort:** 2 days
|
|
**Impact:** Medium - improves multiple classes
|
|
|
|
### Duplication Metrics Summary
|
|
|
|
| Pattern | Files Affected | Duplicate Lines | Reduction Potential | Priority |
|
|
|---------|----------------|-----------------|---------------------|----------|
|
|
| Event Firing | 1 (C#) | 800 | 95% | Medium |
|
|
| Expected Conditions | 1 (Java) | 600 | 60% | Low |
|
|
| FindBy Methods | 20+ | 600 | 100% | Low |
|
|
| Option Building | 6 | 200 | 85% | High |
|
|
|
|
**Total Duplication:** ~2,200 lines
|
|
**Potential Reduction:** ~1,800 lines (82%)
|
|
|
|
---
|
|
|
|
## 13. Architecture Recommendations
|
|
|
|
### Recommended Patterns for Complexity Reduction
|
|
|
|
#### 1. Command Pattern (for Protocol Handling)
|
|
**Use Cases:**
|
|
- BiDi message routing
|
|
- WebDriver command execution
|
|
- CDP message handling
|
|
|
|
**Benefits:**
|
|
- Isolates each command in separate class
|
|
- Easy to add new commands
|
|
- Testable in isolation
|
|
- Supports undo/redo if needed
|
|
|
|
**Example Structure:**
|
|
```
|
|
interface Command<T> {
|
|
T execute(Context context);
|
|
}
|
|
|
|
class NewSessionCommand implements Command<Session> { ... }
|
|
class NavigateCommand implements Command<Void> { ... }
|
|
|
|
class CommandExecutor {
|
|
void execute(Command<?> command) {
|
|
command.execute(this.context);
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 2. Strategy Pattern (for Configuration)
|
|
**Use Cases:**
|
|
- Browser option merging
|
|
- Capability generation
|
|
- Locator strategies
|
|
|
|
**Benefits:**
|
|
- Encapsulates algorithms
|
|
- Interchangeable strategies
|
|
- Easy to extend
|
|
- Reduces conditional complexity
|
|
|
|
#### 3. Builder Pattern (for Complex Objects)
|
|
**Use Cases:**
|
|
- Proxy configuration
|
|
- Driver options
|
|
- Test data setup
|
|
|
|
**Benefits:**
|
|
- Fluent API
|
|
- Immutability
|
|
- Validation at build time
|
|
- Clear construction process
|
|
|
|
#### 4. Factory Pattern (for Error Handling)
|
|
**Use Cases:**
|
|
- Exception creation from error codes
|
|
- Driver instantiation
|
|
- Element creation
|
|
|
|
**Benefits:**
|
|
- Centralized creation logic
|
|
- Type safety
|
|
- Easy to mock for testing
|
|
|
|
#### 5. Visitor Pattern (for Type Handling)
|
|
**Use Cases:**
|
|
- BiDi type conversion
|
|
- JSON serialization
|
|
- Capability processing
|
|
|
|
**Benefits:**
|
|
- Separates algorithm from data structure
|
|
- Easy to add new operations
|
|
- Preserves type safety
|
|
|
|
### Anti-Patterns to Avoid
|
|
|
|
#### 1. God Objects
|
|
**Symptoms:**
|
|
- Classes > 1,000 lines
|
|
- >20 public methods
|
|
- High coupling to many classes
|
|
|
|
**Found In:**
|
|
- Java: RemoteWebDriver (1,323 lines)
|
|
- C#: EventFiringWebDriver (1,910 lines)
|
|
|
|
**Solution:** Decompose using composition
|
|
|
|
#### 2. Long Parameter Lists
|
|
**Symptoms:**
|
|
- Methods with >4 parameters
|
|
- Complex constructors
|
|
|
|
**Found In:**
|
|
- C#: Proxy constructor (complex validation)
|
|
- Various option classes
|
|
|
|
**Solution:** Use builder pattern or parameter object
|
|
|
|
#### 3. Switch/If-Elif Chains
|
|
**Symptoms:**
|
|
- CCN > 15
|
|
- Many similar conditional branches
|
|
|
|
**Found In:**
|
|
- Python: ErrorHandler (CCN: 33)
|
|
- C#: WebDriver error unpacking (CCN: 43)
|
|
|
|
**Solution:** Replace with dictionary dispatch or polymorphism
|
|
|
|
#### 4. Feature Envy
|
|
**Symptoms:**
|
|
- Method uses more data from other class than own class
|
|
- High coupling between classes
|
|
|
|
**Solution:** Move method to appropriate class
|
|
|
|
### Architectural Layers
|
|
|
|
**Recommended Layer Structure:**
|
|
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ Public API (WebDriver Interface) │
|
|
├─────────────────────────────────────┤
|
|
│ Driver Implementations │
|
|
│ (Chrome, Firefox, Safari, etc.) │
|
|
├─────────────────────────────────────┤
|
|
│ Protocol Layer │
|
|
│ (W3C WebDriver, BiDi, CDP) │
|
|
├─────────────────────────────────────┤
|
|
│ Transport Layer │
|
|
│ (HTTP, WebSocket) │
|
|
├─────────────────────────────────────┤
|
|
│ Platform Layer │
|
|
│ (OS-specific implementations) │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
**Complexity Guidelines by Layer:**
|
|
|
|
| Layer | Max CCN | Max Class Lines | Rationale |
|
|
|-------|---------|-----------------|-----------|
|
|
| Public API | 3 | 200 | Simple, delegate to implementations |
|
|
| Driver Implementations | 8 | 500 | Browser-specific logic, kept focused |
|
|
| Protocol Layer | 10 | 400 | Protocol complexity, well-structured |
|
|
| Transport Layer | 5 | 300 | I/O operations, minimal logic |
|
|
| Platform Layer | 8 | 400 | OS-specific, isolated |
|
|
|
|
---
|
|
|
|
## 14. Testing Recommendations
|
|
|
|
### Test Coverage for Complex Code
|
|
|
|
**High-Complexity Code Requires:**
|
|
|
|
1. **Higher Test Coverage Target**
|
|
- CCN < 5: 80% coverage acceptable
|
|
- CCN 5-10: 90% coverage minimum
|
|
- CCN 10-15: 95% coverage minimum
|
|
- CCN > 15: 100% coverage required
|
|
|
|
2. **Specific Test Types**
|
|
- **Unit Tests:** Every branch in high-CCN methods
|
|
- **Integration Tests:** Complex interactions
|
|
- **Property-Based Tests:** Edge cases in algorithms
|
|
- **Mutation Tests:** Verify test quality
|
|
|
|
### Test Strategies by Complexity
|
|
|
|
#### For High-CCN Methods (CCN > 15)
|
|
|
|
**Example: ErrorHandler.check_response() (CCN: 33)**
|
|
|
|
**Test Strategy:**
|
|
```python
|
|
# Test every error code path (33 tests minimum)
|
|
@pytest.mark.parametrize("error_code,expected_exception", [
|
|
('NoSuchElement', NoSuchElementException),
|
|
('StaleElement', StaleElementReferenceException),
|
|
# ... all 33 error codes
|
|
])
|
|
def test_error_mapping(error_code, expected_exception):
|
|
response = {'error': error_code}
|
|
with pytest.raises(expected_exception):
|
|
ErrorHandler.check_response(response)
|
|
```
|
|
|
|
**Additional Tests:**
|
|
- Happy path (no error)
|
|
- Unknown error codes
|
|
- Malformed responses
|
|
- Missing fields
|
|
- Edge cases (empty strings, null values)
|
|
|
|
**Total Tests Needed:** 40+ for complete coverage
|
|
|
|
#### For Complex Constructors (CCN > 20)
|
|
|
|
**Example: C# Proxy constructor (CCN: 46)**
|
|
|
|
**Test Strategy:**
|
|
```csharp
|
|
[Theory]
|
|
[MemberData(nameof(GetProxyConfigurations))]
|
|
public void Constructor_ValidatesConfiguration(ProxyConfig config, bool shouldSucceed)
|
|
{
|
|
if (shouldSucceed)
|
|
{
|
|
var proxy = new Proxy(config);
|
|
Assert.NotNull(proxy);
|
|
}
|
|
else
|
|
{
|
|
Assert.Throws<ArgumentException>(() => new Proxy(config));
|
|
}
|
|
}
|
|
|
|
// Generate all valid and invalid combinations (50+ configurations)
|
|
public static IEnumerable<object[]> GetProxyConfigurations()
|
|
{
|
|
// Valid configurations
|
|
yield return new object[] { new ProxyConfig { Type = ProxyType.Direct }, true };
|
|
|
|
// Invalid configurations
|
|
yield return new object[] { new ProxyConfig { Type = ProxyType.Manual, HttpProxy = null }, false };
|
|
|
|
// ... 48 more configurations
|
|
}
|
|
```
|
|
|
|
**Total Tests Needed:** 50+ for complete coverage
|
|
|
|
#### For Option Merging (CCN > 20)
|
|
|
|
**Example: FirefoxOptions.merge() (CCN: 30)**
|
|
|
|
**Test Strategy:**
|
|
- **Pairwise Testing:** Test all option pairs (n² tests)
|
|
- **Property-Based Testing:** Generate random valid option combinations
|
|
|
|
```java
|
|
@Property
|
|
public void merge_shouldPreserveAllOptions(
|
|
@ForAll FirefoxOptions options1,
|
|
@ForAll FirefoxOptions options2
|
|
) {
|
|
FirefoxOptions merged = options1.merge(options2);
|
|
|
|
// Assertions about merge behavior
|
|
assertThat(merged).containsAllOptionsFrom(options1);
|
|
assertThat(merged).overridesWithOptionsFrom(options2);
|
|
}
|
|
```
|
|
|
|
**Total Tests Needed:** 100+ with property-based testing
|
|
|
|
### Mutation Testing for High-CCN Code
|
|
|
|
**Purpose:** Verify that tests actually catch bugs
|
|
|
|
**Tool:** PITest (Java), Stryker (JavaScript), mutmut (Python)
|
|
|
|
**Example:**
|
|
```bash
|
|
# Run mutation testing on high-complexity class
|
|
mvn org.pitest:pitest-maven:mutationCoverage \
|
|
-DtargetClasses=org.openqa.selenium.firefox.FirefoxOptions \
|
|
-DtargetTests=org.openqa.selenium.firefox.FirefoxOptionsTest
|
|
|
|
# Target: 90%+ mutation coverage for CCN > 15
|
|
```
|
|
|
|
**Action Items:**
|
|
- Run mutation testing on all CCN > 15 methods
|
|
- Achieve 90%+ mutation score
|
|
- Add tests for surviving mutants
|
|
|
|
### Test Complexity Reduction
|
|
|
|
**Anti-Pattern: Tests as Complex as Code**
|
|
```python
|
|
# BAD: Test has CCN of 8
|
|
def test_complex_validation():
|
|
if condition1:
|
|
if condition2:
|
|
assert something
|
|
else:
|
|
assert something_else
|
|
else:
|
|
if condition3:
|
|
assert another_thing
|
|
```
|
|
|
|
**Better Approach: Parameterized Tests**
|
|
```python
|
|
# GOOD: Test has CCN of 1
|
|
@pytest.mark.parametrize("input,expected", [
|
|
(condition1_and_2, expected_result1),
|
|
(condition1_not_2, expected_result2),
|
|
(not_condition1_and_3, expected_result3),
|
|
])
|
|
def test_validation(input, expected):
|
|
assert validate(input) == expected
|
|
```
|
|
|
|
### Test Documentation for Complex Code
|
|
|
|
**For CCN > 15, document:**
|
|
1. **What:** Test scenario description
|
|
2. **Why:** Which code path this exercises
|
|
3. **Coverage:** CCN branches covered
|
|
|
|
**Example:**
|
|
```java
|
|
/**
|
|
* Tests the error code mapping for NoSuchElement exception.
|
|
*
|
|
* Coverage: Exercises error code branch #7 in check_response() (CCN 33)
|
|
* Path: error_code == 'NoSuchElement' -> raise NoSuchElementException
|
|
*
|
|
* This test is critical because NoSuchElement is the most common error
|
|
* and must be handled correctly for 90% of element finding operations.
|
|
*/
|
|
@Test
|
|
public void checkResponse_NoSuchElement_ThrowsCorrectException() {
|
|
// test implementation
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 15. Maintenance and Monitoring
|
|
|
|
### Complexity Tracking Dashboard
|
|
|
|
**Recommended Metrics to Track:**
|
|
|
|
1. **Trend Metrics (over time)**
|
|
- Average CCN per language
|
|
- Number of functions with CCN > 15
|
|
- Total NLOC
|
|
- Functions added vs. removed
|
|
- Complexity per feature
|
|
|
|
2. **Distribution Metrics**
|
|
- CCN histogram (0-5, 6-10, 11-15, 16+)
|
|
- Function length distribution
|
|
- Class size distribution
|
|
- Hotspot concentration (% of complexity in top 10%)
|
|
|
|
3. **Quality Metrics**
|
|
- Test coverage vs. complexity
|
|
- Bugs per complexity level
|
|
- Time to fix bugs in high-CCN code
|
|
- Code review time vs. complexity
|
|
|
|
### Continuous Monitoring
|
|
|
|
**CI/CD Integration:**
|
|
|
|
```yaml
|
|
# Example GitHub Actions workflow
|
|
name: Code Complexity Check
|
|
|
|
on: [pull_request]
|
|
|
|
jobs:
|
|
complexity:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v2
|
|
|
|
- name: Install Lizard
|
|
run: pipx install lizard
|
|
|
|
- name: Analyze Java
|
|
run: |
|
|
lizard -l java -w --CCN 15 java/src/ > java-complexity.txt
|
|
|
|
- name: Check for new warnings
|
|
run: |
|
|
# Compare with baseline
|
|
if [ $(cat java-complexity.txt | grep -c "Warning") -gt 0 ]; then
|
|
echo "New complexity warnings found"
|
|
cat java-complexity.txt
|
|
exit 1
|
|
fi
|
|
|
|
- name: Upload report
|
|
uses: actions/upload-artifact@v2
|
|
with:
|
|
name: complexity-report
|
|
path: '*-complexity.txt'
|
|
```
|
|
|
|
**SonarQube Integration:**
|
|
|
|
```xml
|
|
<!-- sonar-project.properties -->
|
|
sonar.projectKey=selenium-webdriver
|
|
sonar.sources=java/src,javascript/lib,dotnet/src,py/selenium,rb/lib
|
|
|
|
<!-- Complexity thresholds -->
|
|
sonar.complexity.threshold=15
|
|
sonar.complexity.functions=10
|
|
sonar.complexity.files=100
|
|
```
|
|
|
|
### Complexity Budget
|
|
|
|
**Concept:** Allocate complexity budget per feature
|
|
|
|
**Example:**
|
|
```
|
|
Feature: Add BiDi Network Interception
|
|
Complexity Budget: 50 CCN points
|
|
|
|
Allocation:
|
|
- NetworkInterceptor class: 20 CCN
|
|
- Request handler: 10 CCN
|
|
- Response handler: 10 CCN
|
|
- Integration layer: 10 CCN
|
|
|
|
Total: 50 CCN (within budget)
|
|
```
|
|
|
|
**Benefits:**
|
|
- Forces conscious complexity decisions
|
|
- Prevents gradual complexity creep
|
|
- Makes complexity visible in planning
|
|
|
|
### Refactoring Sprints
|
|
|
|
**Recommended Cadence:**
|
|
- **Monthly:** Review top 10 complexity hotspots
|
|
- **Quarterly:** Dedicated refactoring sprint
|
|
- **Annually:** Major architecture review
|
|
|
|
**Refactoring Sprint Structure:**
|
|
|
|
**Week 1-2: Assessment**
|
|
- Run complexity analysis
|
|
- Identify and prioritize hotspots
|
|
- Estimate refactoring effort
|
|
- Plan test coverage improvements
|
|
|
|
**Week 3-4: Execution**
|
|
- Refactor 2-3 critical hotspots
|
|
- Increase test coverage
|
|
- Update documentation
|
|
- Measure improvement
|
|
|
|
**Week 5: Validation**
|
|
- Run full test suite
|
|
- Performance testing
|
|
- Code review
|
|
- Complexity re-measurement
|
|
|
|
**Success Criteria:**
|
|
- Reduce critical hotspots (CCN > 20) by 50%
|
|
- Improve test coverage by 10%
|
|
- No regression in functionality
|
|
- Improved code review feedback
|
|
|
|
### Complexity Alerts
|
|
|
|
**Set up automated alerts for:**
|
|
|
|
1. **New Critical Complexity** (CCN > 20)
|
|
- Alert: Slack/Email to team
|
|
- Action: Required refactoring before merge
|
|
|
|
2. **Complexity Trend Increase** (>5% month-over-month)
|
|
- Alert: Dashboard notification
|
|
- Action: Review in sprint retrospective
|
|
|
|
3. **Test Coverage Gaps** (Coverage < 80% for CCN > 10)
|
|
- Alert: PR comment
|
|
- Action: Add tests before merge
|
|
|
|
4. **Large Class Creation** (>500 lines)
|
|
- Alert: PR comment
|
|
- Action: Justify or decompose
|
|
|
|
### Knowledge Sharing
|
|
|
|
**Document and Share:**
|
|
|
|
1. **Refactoring Case Studies**
|
|
- Before/after complexity metrics
|
|
- Techniques used
|
|
- Lessons learned
|
|
- Time investment vs. benefit
|
|
|
|
2. **Complexity Patterns Catalog**
|
|
- Common high-complexity patterns
|
|
- Refactoring solutions
|
|
- Code examples
|
|
- When to apply each pattern
|
|
|
|
3. **Team Training**
|
|
- Monthly complexity review session
|
|
- Pair refactoring sessions
|
|
- Complexity-aware code reviews
|
|
- Sharing refactoring wins
|
|
|
|
---
|
|
|
|
## 16. Conclusion and Key Takeaways
|
|
|
|
### Overall Project Health: GOOD (B+ Grade)
|
|
|
|
The Selenium project demonstrates **good complexity management** for a project of its scale, with most code maintaining reasonable complexity levels. The multi-language nature adds inherent complexity, but each implementation generally follows good practices.
|
|
|
|
### Strengths
|
|
|
|
1. **Ruby Implementation (A+)**: Exemplary code quality
|
|
- Zero complexity warnings across 1,056 functions
|
|
- Average function length of 5.3 lines (best in project)
|
|
- Consistent application of refactoring patterns
|
|
- Model for other implementations
|
|
|
|
2. **Modular Architecture**: Well-separated concerns
|
|
- Protocol layer isolated from driver implementations
|
|
- Clean abstraction boundaries
|
|
- Good use of design patterns
|
|
|
|
3. **Consistent APIs**: Similar structure across languages
|
|
- Makes cross-language maintenance easier
|
|
- Knowledge transfer between language teams
|
|
|
|
4. **Test Coverage**: Comprehensive test suites
|
|
- Supports safe refactoring
|
|
- High confidence in changes
|
|
|
|
### Critical Issues (Immediate Action Required)
|
|
|
|
1. **C# Proxy Constructor (CCN: 46)**
|
|
- Highest complexity in entire project
|
|
- Affects core functionality
|
|
- **Action:** Implement builder pattern within 1 sprint
|
|
|
|
2. **C# WebDriver.UnpackAndThrowOnError (CCN: 43)**
|
|
- Error handling bottleneck
|
|
- **Action:** Dictionary-based exception factory
|
|
|
|
3. **Python ErrorHandler.check_response (CCN: 33)**
|
|
- 33-branch if-elif chain
|
|
- **Action:** Dictionary dispatch pattern
|
|
|
|
4. **Java FirefoxOptions.merge (CCN: 30)**
|
|
- Complex option merging
|
|
- **Action:** Strategy pattern for merge operations
|
|
|
|
### High-Priority Issues (Next 2 Sprints)
|
|
|
|
- C# BiDi Broker message routing (CCN: 26)
|
|
- JavaScript escapeCss function (CCN: 26)
|
|
- Java ChromiumOptions merging (CCN: 20)
|
|
- Python BiDi type conversion (CCN: 23)
|
|
- C# DriverOptions capability generation (CCN: 23)
|
|
|
|
**Total High-Priority Items:** 9 methods requiring refactoring
|
|
**Estimated Effort:** 3-4 weeks with 2 developers
|
|
|
|
### Recommendations by Priority
|
|
|
|
**Priority 1 (This Sprint):**
|
|
- [ ] Refactor C# Proxy constructor
|
|
- [ ] Implement Python error handler dispatch
|
|
- [ ] Add complexity gates to CI/CD
|
|
|
|
**Priority 2 (Next Sprint):**
|
|
- [ ] Refactor browser option classes (Java & C#)
|
|
- [ ] Address JavaScript escapeCss complexity
|
|
- [ ] Improve C# BiDi message routing
|
|
|
|
**Priority 3 (Quarterly):**
|
|
- [ ] Decompose large classes (RemoteWebDriver, LocalNode)
|
|
- [ ] Reduce code duplication (EventFiringWebDriver)
|
|
- [ ] Extract expected conditions into domain classes
|
|
|
|
**Ongoing:**
|
|
- [ ] Monitor complexity trends monthly
|
|
- [ ] Enforce CCN < 15 for new code
|
|
- [ ] Conduct quarterly refactoring sprints
|
|
- [ ] Share refactoring patterns across language teams
|
|
|
|
### Success Metrics
|
|
|
|
**6-Month Goals:**
|
|
- Reduce critical hotspots (CCN > 20) from 4 to 0
|
|
- Reduce high-priority issues (CCN > 15) from 21 to < 5
|
|
- Increase test coverage for complex code to 95%
|
|
- Decrease average class size by 20%
|
|
|
|
**12-Month Goals:**
|
|
- Achieve Ruby-level quality across all languages (0 warnings)
|
|
- Average CCN < 1.5 for all new code
|
|
- No classes > 500 lines
|
|
- 100% of complex code (CCN > 10) has 95%+ coverage
|
|
|
|
### Final Thoughts
|
|
|
|
The Selenium project is **well-maintained** with **room for focused improvement**. The complexity issues identified are **concentrated** in specific areas and are **solvable** with standard refactoring techniques.
|
|
|
|
**Key Success Factor:** The Ruby implementation proves that excellent code quality is achievable in this project. Applying Ruby's patterns and discipline to other languages will yield significant improvements.
|
|
|
|
**Recommended Approach:** Tackle critical hotspots first (high impact, low effort), then systematically address high-priority issues. Establish complexity monitoring to prevent regression.
|
|
|
|
**Estimated Improvement:** With dedicated effort, the project can move from **B+ to A-** grade within 6 months, and achieve **A grade** across all implementations within 12 months.
|
|
|
|
---
|
|
|
|
## Appendix A: Detailed Metrics Tables
|
|
|
|
### A.1 Java Top 20 Most Complex Functions
|
|
|
|
| Rank | Function | CCN | NLOC | File |
|
|
|------|----------|-----|------|------|
|
|
| 1 | FirefoxOptions.merge() | 30 | 96 | firefox/FirefoxOptions.java |
|
|
| 2 | SpecialNumberType.getArgument() | 24 | 70 | bidi/script/LocalValue.java |
|
|
| 3 | JsonInput.peek() | 23 | 38 | json/JsonInput.java |
|
|
| 4 | V148Adapter.adaptContainerInspectResponse() | 20 | 80 | docker/client/V148Adapter.java |
|
|
| 5 | V148Adapter.adaptContainerCreateRequest() | 22 | 74 | docker/client/V148Adapter.java |
|
|
| 6 | ChromiumOptions.mergeInPlace() | 20 | 52 | chromium/ChromiumOptions.java |
|
|
| 7 | ResourceHandler.mediaType() | 20 | 56 | grid/web/ResourceHandler.java |
|
|
| 8 | UserAgent.prepareToInterceptTraffic() | 19 | 94 | devtools/idealized/Network.java |
|
|
| 9 | NetworkUtils.determineHostnameAndAddress() | 21 | 67 | net/NetworkUtils.java |
|
|
| 10 | DefaultSlotMatcher.matches() | 18 | 33 | grid/data/DefaultSlotMatcher.java |
|
|
| 11 | SimplePropertyDescriptor.getPropertyDescriptors() | 17 | 54 | json/SimplePropertyDescriptor.java |
|
|
| 12 | Select.selectByVisibleText() | 13 | 46 | support/ui/Select.java |
|
|
| 13 | FluentWait.until() | 10 | 32 | support/ui/FluentWait.java |
|
|
| 14 | LocalNode.newSession() | 15 | 82 | grid/node/local/LocalNode.java |
|
|
| 15 | RemoteWebDriver.execute() | 12 | 45 | remote/RemoteWebDriver.java |
|
|
| 16 | ExpectedConditions.urlMatches() | 2 | 16 | support/ui/ExpectedConditions.java |
|
|
| 17 | WebDriverWait.timeoutException() | 5 | 18 | support/ui/WebDriverWait.java |
|
|
| 18 | Quotes.escape() | 7 | 19 | support/ui/Quotes.java |
|
|
| 19 | SlowLoadableComponent.get() | 4 | 20 | support/ui/SlowLoadableComponent.java |
|
|
| 20 | Select.deSelectByContainsVisibleText() | 5 | 16 | support/ui/Select.java |
|
|
|
|
### A.2 C# Top 15 Most Complex Functions
|
|
|
|
| Rank | Function | CCN | NLOC | File |
|
|
|------|----------|-----|------|------|
|
|
| 1 | Proxy.Proxy (constructor) | 46 | 70 | Proxy.cs |
|
|
| 2 | WebDriver.UnpackAndThrowOnError() | 43 | 86 | WebDriver.cs |
|
|
| 3 | Broker.ProcessReceivedMessage() | 26 | 97 | BiDi/Broker.cs |
|
|
| 4 | Cookie.FromDictionary() | 24 | 44 | Cookie.cs |
|
|
| 5 | DriverOptions.GenerateDesiredCapabilities() | 23 | 93 | DriverOptions.cs |
|
|
| 6 | StackTraceElement.StackTraceElement | 21 | 37 | StackTraceElement.cs |
|
|
| 7 | WebDriver.ConvertObjectToJavaScriptObject() | 16 | 41 | WebDriver.cs |
|
|
| 8 | InternetExplorerOptions.BuildIEOptionsDictionary() | 19 | 86 | IE/InternetExplorerOptions.cs |
|
|
| 9 | ChromiumOptions.BuildChromeOptionsDictionary() | 18 | 66 | Chromium/ChromiumOptions.cs |
|
|
| 10 | Response.FromJson() | 17 | 43 | Response.cs |
|
|
| 11 | ErrorResponse.ErrorResponse | 19 | 39 | ErrorResponse.cs |
|
|
| 12 | DefaultWait.Until<TResult>() | 14 | 54 | Support/DefaultWait{T}.cs |
|
|
| 13 | RemoteWebDriver.GetDevToolsSession() | 11 | 41 | Remote/RemoteWebDriver.cs |
|
|
| 14 | RemoteWebDriver.DownloadFile() | 4 | 31 | Remote/RemoteWebDriver.cs |
|
|
| 15 | W3CWireProtocolCommandInfoRepository (constructor) | 1 | 79 | Remote/W3CWireProtocolCommandInfoRepository.cs |
|
|
|
|
### A.3 Python Top 10 Most Complex Functions
|
|
|
|
| Rank | Function | CCN | NLOC | File |
|
|
|------|----------|-----|------|------|
|
|
| 1 | ErrorHandler.check_response() | 33 | 75 | remote/errorhandler.py |
|
|
| 2 | Script.__convert_to_local_value() | 23 | 40 | common/bidi/script.py |
|
|
| 3 | Proxy.__init__() | 22 | 22 | common/proxy.py |
|
|
| 4 | BrowsingContext.from_json() | 17 | 38 | common/bidi/browsing_context.py |
|
|
| 5 | RemoteConnection._request() | 16 | 45 | remote/remote_connection.py |
|
|
| 6 | FirefoxProfile._addon_details() | 16 | 47 | firefox/firefox_profile.py |
|
|
| 7 | Select.select_by_visible_text() | 12 | 28 | support/select.py |
|
|
| 8 | Select._escape_string() | 6 | 14 | support/select.py |
|
|
| 9 | WebDriver.execute() | 8 | 35 | remote/webdriver.py |
|
|
| 10 | WebDriver.find_element() | 5 | 22 | remote/webdriver.py |
|
|
|
|
### A.4 JavaScript Top 10 Most Complex Functions
|
|
|
|
| Rank | Function | CCN | NLOC | File |
|
|
|------|----------|-----|------|------|
|
|
| 1 | escapeCss() | 26 | 39 | by.js |
|
|
| 2 | toWireValue() | 10 | 21 | webdriver.js |
|
|
| 3 | sendEcho() | 4 | 34 | test/fileserver.js |
|
|
| 4 | sendBasicAuth() | 5 | 18 | test/fileserver.js |
|
|
| 5 | sendDelayedResponse() | 3 | 12 | test/fileserver.js |
|
|
| 6 | sendIndex() | 3 | 18 | test/fileserver.js |
|
|
| 7 | Build.go() | 5 | 22 | test/build.js |
|
|
| 8 | FluentWait.until() | 10 | 32 | lib/until.js |
|
|
| 9 | WebDriver.executeScript() | 4 | 18 | lib/webdriver.js |
|
|
| 10 | Input.pointerMove() | 6 | 25 | lib/input.js |
|
|
|
|
### A.5 Ruby Top 8 Most Complex Functions
|
|
|
|
| Rank | Function | CCN | NLOC | File |
|
|
|------|----------|-----|------|------|
|
|
| 1 | Color.from_hsl() | 8 | 18 | support/color.rb |
|
|
| 2 | Color.from_string() | 8 | 23 | support/color.rb |
|
|
| 3 | Select.find_by_text() | 3 | 14 | support/select.rb |
|
|
| 4 | Guards.disposition() | 5 | 9 | support/guards.rb |
|
|
| 5 | Escaper.escape() | 4 | 12 | support/escaper.rb |
|
|
| 6 | Bridge.execute() | 3 | 8 | remote/bridge.rb |
|
|
| 7 | Element.send_keys() | 3 | 12 | common/element.rb |
|
|
| 8 | Driver.get() | 2 | 6 | common/driver.rb |
|
|
|
|
*Note: Ruby has no functions exceeding CCN of 10*
|
|
|
|
---
|
|
|
|
## Appendix B: Tool Configuration
|
|
|
|
### B.1 Lizard Configuration
|
|
|
|
```bash
|
|
# Basic complexity analysis
|
|
lizard -l java java/src/
|
|
|
|
# With warnings for CCN > 15
|
|
lizard -l java -w --CCN 15 java/src/
|
|
|
|
# Generate XML report for CI
|
|
lizard -l java --xml java/src/ > complexity-report.xml
|
|
|
|
# Multiple languages
|
|
lizard -l java,javascript,csharp,python,ruby \
|
|
java/src/ \
|
|
javascript/lib/ \
|
|
dotnet/src/ \
|
|
py/selenium/ \
|
|
rb/lib/
|
|
```
|
|
|
|
### B.2 SonarQube Configuration
|
|
|
|
```properties
|
|
# sonar-project.properties
|
|
sonar.projectKey=selenium-webdriver
|
|
sonar.projectName=Selenium WebDriver
|
|
sonar.projectVersion=4.0
|
|
|
|
# Source directories
|
|
sonar.sources=java/src,javascript/lib,dotnet/src,py/selenium,rb/lib
|
|
|
|
# Test directories
|
|
sonar.tests=java/test,javascript/test,dotnet/test,py/test,rb/spec
|
|
|
|
# Complexity rules
|
|
sonar.complexity.threshold=15
|
|
sonar.complexity.functions=10
|
|
sonar.complexity.classes=100
|
|
|
|
# Exclusions
|
|
sonar.exclusions=**/test/**,**/vendor/**,**/node_modules/**
|
|
```
|
|
|
|
### B.3 CI/CD Complexity Gates
|
|
|
|
```yaml
|
|
# .github/workflows/complexity-check.yml
|
|
name: Code Complexity Check
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main, develop]
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
complexity:
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
|
|
- name: Install Lizard
|
|
run: pipx install lizard
|
|
|
|
- name: Analyze Java
|
|
run: |
|
|
lizard -l java -w --CCN 15 java/src/ | tee java-complexity.txt
|
|
|
|
- name: Analyze JavaScript
|
|
run: |
|
|
lizard -l javascript -w --CCN 15 javascript/lib/ | tee js-complexity.txt
|
|
|
|
- name: Analyze C#
|
|
run: |
|
|
lizard -l csharp -w --CCN 15 dotnet/src/ | tee cs-complexity.txt
|
|
|
|
- name: Analyze Python
|
|
run: |
|
|
lizard -l python -w --CCN 15 py/selenium/ | tee py-complexity.txt
|
|
|
|
- name: Analyze Ruby
|
|
run: |
|
|
lizard -l ruby -w --CCN 15 rb/lib/ | tee rb-complexity.txt
|
|
|
|
- name: Check for new warnings
|
|
run: |
|
|
# Extract warning count
|
|
JAVA_WARNINGS=$(grep -c "WARNING" java-complexity.txt || true)
|
|
JS_WARNINGS=$(grep -c "WARNING" js-complexity.txt || true)
|
|
CS_WARNINGS=$(grep -c "WARNING" cs-complexity.txt || true)
|
|
PY_WARNINGS=$(grep -c "WARNING" py-complexity.txt || true)
|
|
RB_WARNINGS=$(grep -c "WARNING" rb-complexity.txt || true)
|
|
|
|
TOTAL_WARNINGS=$((JAVA_WARNINGS + JS_WARNINGS + CS_WARNINGS + PY_WARNINGS + RB_WARNINGS))
|
|
|
|
echo "Total complexity warnings: $TOTAL_WARNINGS"
|
|
|
|
if [ $TOTAL_WARNINGS -gt 38 ]; then
|
|
echo "New complexity warnings detected!"
|
|
echo "Baseline: 38 warnings"
|
|
echo "Current: $TOTAL_WARNINGS warnings"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Upload reports
|
|
uses: actions/upload-artifact@v3
|
|
if: always()
|
|
with:
|
|
name: complexity-reports
|
|
path: '*-complexity.txt'
|
|
|
|
- name: Comment PR
|
|
if: github.event_name == 'pull_request'
|
|
uses: actions/github-script@v6
|
|
with:
|
|
script: |
|
|
const fs = require('fs');
|
|
const javaReport = fs.readFileSync('java-complexity.txt', 'utf8');
|
|
|
|
github.rest.issues.createComment({
|
|
issue_number: context.issue.number,
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
body: `## Code Complexity Report\n\n\`\`\`\n${javaReport}\n\`\`\``
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Appendix C: Recommended Reading
|
|
|
|
### Books
|
|
1. **"Refactoring: Improving the Design of Existing Code"** - Martin Fowler
|
|
- Catalog of refactoring patterns
|
|
- Relevant to all complexity issues identified
|
|
|
|
2. **"Clean Code"** - Robert C. Martin
|
|
- Principles for writing maintainable code
|
|
- Complexity management strategies
|
|
|
|
3. **"Working Effectively with Legacy Code"** - Michael Feathers
|
|
- Techniques for safely refactoring complex code
|
|
- Test coverage strategies
|
|
|
|
### Articles
|
|
1. **"Cognitive Complexity: A new way of measuring understandability"** - SonarSource
|
|
- https://www.sonarsource.com/docs/CognitiveComplexity.pdf
|
|
|
|
2. **"Cyclomatic Complexity"** - Thomas J. McCabe
|
|
- Original paper on cyclomatic complexity
|
|
|
|
### Tools
|
|
1. **Lizard** - Multi-language complexity analyzer
|
|
- https://github.com/terryyin/lizard
|
|
|
|
2. **SonarQube** - Continuous inspection platform
|
|
- https://www.sonarqube.org/
|
|
|
|
3. **CodeClimate** - Automated code review
|
|
- https://codeclimate.com/
|
|
|
|
4. **Better Code Hub** - Code quality checker
|
|
- https://bettercodehub.com/
|
|
|
|
---
|
|
|
|
**Report Generated:** 2025-12-18
|
|
**Analysis Tool:** Lizard v1.19.0
|
|
**Total Files Analyzed:** 3,564
|
|
**Total Lines Analyzed:** 107,772 NLOC
|
|
**Analysis Duration:** ~2 hours
|
|
**Next Review:** 2026-01-18
|
|
|
|
---
|
|
|
|
*This analysis was performed by the QE Code Complexity Analyzer Agent as part of the Agentic QE Fleet.*
|