Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Test Suite

on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]

jobs:
test:
name: Run Tests
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: subosito/flutter-action@v2
with:
channel: 'stable'
cache: true

- name: Install dependencies
run: flutter pub get

- name: Run code generation
run: flutter packages pub run build_runner build --delete-conflicting-outputs

- name: Verify formatting
run: dart format --output=none --set-exit-if-changed lib/ test/

- name: Analyze code
run: flutter analyze --no-fatal-infos

- name: Run tests
run: flutter test --coverage

- name: Check test coverage
run: |
# Parse coverage and report
LINES=$(grep -oP '(?<=LF:)\d+' coverage/lcov.info | awk '{s+=$1} END {print s}')
HITS=$(grep -oP '(?<=LH:)\d+' coverage/lcov.info | awk '{s+=$1} END {print s}')
COVERAGE=$(echo "scale=1; $HITS * 100 / $LINES" | bc)
echo "## Coverage Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Coverage**: $COVERAGE% ($HITS / $LINES lines)" >> $GITHUB_STEP_SUMMARY

- name: Upload coverage artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: coverage/lcov.info
retention-days: 5
22 changes: 21 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,24 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
.fvm/flutter_sdk
.fvm/flutter_sdk

# Coverage
coverage/

# Test output and debug files
*.txt
*_output*.log
*_failures*.log

# Python (mock backend)
__pycache__/
*.py[cod]
*$py.class
.Python
*.egg-info/
venv/
.env

# Android
**/android/local.properties
86 changes: 86 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
.PHONY: help test test-watch coverage gen clean analyze format quick-check install run run-mock build

help: ## Show this help message
@echo 'Usage: make [target]'
@echo ''
@echo 'Available targets:'
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST)

# Development
install: ## Install dependencies
flutter pub get
flutter packages pub run build_runner build --delete-conflicting-outputs

run: ## Run app in Chrome
flutter run -d chrome --web-port=8080

run-mock: ## Run app with mock backend
@echo "Starting app in Mock Mode..."
flutter run -d chrome --web-port=8080 --dart-define=MOCK_MODE=true

mock-server: ## Run the mock backend server
python3 tools/mock_backend/mock_backend.py

install-mock-deps: ## Install dependencies for mock backend
pip3 install aiohttp aiohttp-cors websockets

# Testing
test: ## Run all tests
flutter test

test-watch: ## Run tests in watch mode
flutter test --watch

coverage: ## Generate coverage report and open in browser
flutter test --coverage
@if command -v genhtml > /dev/null; then \
genhtml coverage/lcov.info -o coverage/html -q; \
open coverage/html/index.html || xdg-open coverage/html/index.html; \
else \
echo "genhtml not found. Install lcov: brew install lcov (macOS) or apt-get install lcov (Linux)"; \
fi

# Code Generation
gen: ## Run build_runner code generation
flutter packages pub run build_runner build --delete-conflicting-outputs

gen-watch: ## Watch for changes and regenerate code
flutter packages pub run build_runner watch --delete-conflicting-outputs

# Code Quality
analyze: ## Run static analysis
flutter analyze

format: ## Format code
dart format lib/ test/

format-check: ## Check code formatting
dart format --set-exit-if-changed lib/ test/

# Combined Tasks
quick-check: format analyze test ## Run format, analyze, and tests

pre-commit: format-check analyze test ## Pre-commit checks (fails on format issues)

# Cleanup
clean: ## Clean build artifacts
flutter clean
rm -rf coverage/

clean-all: clean ## Clean everything including dependencies
rm -rf .dart_tool/
rm -rf build/
rm -f pubspec.lock

reset: clean-all install ## Full reset: clean and reinstall

# Build
build: ## Build web app for production
flutter build web

build-profile: ## Build web app in profile mode
flutter build web --profile

# Other
doctor: ## Check Flutter installation
flutter doctor -v
48 changes: 39 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
# Tesla Android

[![Tests](https://img.shields.io/badge/tests-340%20passing-success)](test/README.md)
[![Coverage](https://img.shields.io/badge/coverage-80.1%25-success)](coverage/html/index.html)
[![Flutter](https://img.shields.io/badge/flutter-%3E%3D3.35.0-blue)](https://flutter.dev)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

Flutter app for Tesla Android.

Please refer to https://teslaandroid.com for release notes, hardware requirements and the install guide.
Please refer to [teslaandroid.com](https://teslaandroid.com) for release notes, hardware requirements, and the install guide.

## Getting Started
## Quick Start

```
flutter pub get
flutter packages pub run build_runner build --delete-conflicting-outputs
flutter build web
```bash
# Install app dependencies
make install

# Install mock backend dependencies
make install-mock-deps

# Start mock server (in a separate terminal)
make mock-server

# Run app (mock mode)
make run-mock

# Run tests
make test

# Generate coverage
make coverage
```

In order to build this project for debugging make sure to disable cors in Chrome and connect to Tesla Android Wi-Fi network
See [`Makefile`](Makefile) for all available commands.

#### Please consider supporting the project:
## Development

[Donations](https://teslaandroid.com/donations)
**Test guide**: [`test/README.md`](test/README.md)

**Mock backend**: [`docs/MOCK_BACKEND.md`](docs/MOCK_BACKEND.md)

## Hardware Debugging

When connecting to real Tesla Android hardware:
1. Connect to Tesla Android Wi-Fi network
2. Disable CORS in Chrome

## Please Consider Supporting the Project

[Donations](https://teslaandroid.com/donations)
58 changes: 58 additions & 0 deletions analyze_coverage.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// ignore_for_file: avoid_print
import 'dart:io';

void main() {
final file = File('coverage/lcov.info');
if (!file.existsSync()) {
print('coverage/lcov.info not found');
return;
}

final lines = file.readAsLinesSync();
final Map<String, List<int>> coverage = {};
String? currentFile;

for (final line in lines) {
if (line.startsWith('SF:')) {
currentFile = line.substring(3);
if (currentFile.endsWith('.g.dart')) {
currentFile = null;
continue;
}
coverage[currentFile] = [0, 0]; // [hit, total]
} else if (line.startsWith('DA:') && currentFile != null) {
final parts = line.substring(3).split(',');
final hits = int.parse(parts[1]);
coverage[currentFile]![1]++; // total lines
if (hits > 0) {
coverage[currentFile]![0]++; // hit lines
}
}
}

int totalHits = 0;
int totalLines = 0;

print('Coverage Report:');
print('----------------');

final sortedFiles = coverage.keys.toList()..sort();

for (final file in sortedFiles) {
final stats = coverage[file]!;
final hits = stats[0];
final total = stats[1];
totalHits += hits;
totalLines += total;
final percent = total > 0 ? (hits / total * 100).toStringAsFixed(1) : '0.0';

// Filter out files that are 100% covered to focus on gaps
if (hits < total) {
print('$percent% ($hits/$total) - $file');
}
}

print('----------------');
final totalPercent = totalLines > 0 ? (totalHits / totalLines * 100).toStringAsFixed(1) : '0.0';
print('Total Coverage: $totalPercent% ($totalHits/$totalLines)');
}
Loading