refactor: unify device analysis and performance testing logic for improved module consistency
This commit is contained in:
@@ -8,6 +8,12 @@ import 'performance_test.dart'; // Make sure this path is correct
|
|||||||
class DeviceAnalyzer {
|
class DeviceAnalyzer {
|
||||||
final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
|
final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
|
||||||
|
|
||||||
|
// نتيجة الفحص لا تتغير عمليا خلال جلسة واحدة، فنخزنها مؤقتا
|
||||||
|
// كي لا تُعاد الاختبارات الثقيلة (كتابة تخزين + حساب مكثف) كل مرة تُفتح الصفحة.
|
||||||
|
static List<Map<String, dynamic>>? _cachedResult;
|
||||||
|
static DateTime? _cachedAt;
|
||||||
|
static const Duration _cacheTtl = Duration(minutes: 30);
|
||||||
|
|
||||||
/// Reads the total RAM from the system's meminfo file.
|
/// Reads the total RAM from the system's meminfo file.
|
||||||
/// Returns the value in Megabytes (MB).
|
/// Returns the value in Megabytes (MB).
|
||||||
Future<double> _readTotalRamMB() async {
|
Future<double> _readTotalRamMB() async {
|
||||||
@@ -53,9 +59,12 @@ class DeviceAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The main analysis function that runs all checks.
|
/// The main analysis function that runs all checks.
|
||||||
Future<Map<String, dynamic>> analyzeDevice() async {
|
///
|
||||||
List<Map<String, dynamic>> details = [];
|
/// البنود من 1 إلى 7 تقيس قدرة الجهاز الثابتة (لا تتغير خلال الجلسة) وتُخزَّن
|
||||||
|
/// مؤقتا لتفادي إعادة تشغيل الاختبارات الثقيلة (كتابة تخزين + حساب مكثف) في كل
|
||||||
|
/// مرة تُفتح فيها الصفحة. أما بند ضغط الذاكرة الحالي (#8) فيُقاس دائما بشكل فوري
|
||||||
|
/// لأنه رقم متغير باستمرار ولا يصح تجميده مع بقية النتيجة.
|
||||||
|
Future<Map<String, dynamic>> analyzeDevice({bool forceRefresh = false}) async {
|
||||||
if (!Platform.isAndroid) {
|
if (!Platform.isAndroid) {
|
||||||
return {
|
return {
|
||||||
'score': 0,
|
'score': 0,
|
||||||
@@ -70,6 +79,50 @@ class DeviceAnalyzer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> staticDetails;
|
||||||
|
if (!forceRefresh &&
|
||||||
|
_cachedResult != null &&
|
||||||
|
_cachedAt != null &&
|
||||||
|
DateTime.now().difference(_cachedAt!) < _cacheTtl) {
|
||||||
|
staticDetails = _cachedResult!;
|
||||||
|
} else {
|
||||||
|
staticDetails = await _computeStaticDetails();
|
||||||
|
_cachedResult = staticDetails;
|
||||||
|
_cachedAt = DateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Memory Pressure (Max: 15 points) — يُقاس دائما بشكل فوري، لا يُخزَّن مؤقتا.
|
||||||
|
final usedPercent = await _readUsedRamPercent();
|
||||||
|
int memScore;
|
||||||
|
if (usedPercent <= 60) {
|
||||||
|
memScore = 15;
|
||||||
|
} else if (usedPercent <= 80) {
|
||||||
|
memScore = 10;
|
||||||
|
} else if (usedPercent <= 90) {
|
||||||
|
memScore = 5;
|
||||||
|
} else {
|
||||||
|
memScore = 0;
|
||||||
|
}
|
||||||
|
final memDetail = {
|
||||||
|
'label': 'استخدام الرام الحالي (${usedPercent.toStringAsFixed(0)}%)',
|
||||||
|
'status': memScore >= 10,
|
||||||
|
'achieved_score': memScore,
|
||||||
|
'max_score': 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
final details = [...staticDetails, memDetail];
|
||||||
|
final totalScore = details.fold<int>(
|
||||||
|
0, (sum, item) => sum + (item['achieved_score'] as int));
|
||||||
|
|
||||||
|
return {
|
||||||
|
'score': totalScore.clamp(0, 100),
|
||||||
|
'details': details,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Map<String, dynamic>>> _computeStaticDetails() async {
|
||||||
|
List<Map<String, dynamic>> details = [];
|
||||||
|
|
||||||
final info = await _deviceInfo.androidInfo;
|
final info = await _deviceInfo.androidInfo;
|
||||||
final data = info.data;
|
final data = info.data;
|
||||||
final features = List<String>.from(data['systemFeatures'] ?? []);
|
final features = List<String>.from(data['systemFeatures'] ?? []);
|
||||||
@@ -174,32 +227,6 @@ class DeviceAnalyzer {
|
|||||||
'max_score': 20,
|
'max_score': 20,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 8. Memory Pressure (Max: 15 points)
|
return details;
|
||||||
final usedPercent = await _readUsedRamPercent();
|
|
||||||
int memScore;
|
|
||||||
if (usedPercent <= 60) {
|
|
||||||
memScore = 15;
|
|
||||||
} else if (usedPercent <= 80) {
|
|
||||||
memScore = 10;
|
|
||||||
} else if (usedPercent <= 90) {
|
|
||||||
memScore = 5;
|
|
||||||
} else {
|
|
||||||
memScore = 0;
|
|
||||||
}
|
|
||||||
details.add({
|
|
||||||
'label': 'استخدام الرام الحالي (${usedPercent.toStringAsFixed(0)}%)',
|
|
||||||
'status': memScore >= 10,
|
|
||||||
'achieved_score': memScore,
|
|
||||||
'max_score': 15,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Calculate the final total score by summing up the achieved scores.
|
|
||||||
final totalScore = details.fold<int>(
|
|
||||||
0, (sum, item) => sum + (item['achieved_score'] as int));
|
|
||||||
|
|
||||||
return {
|
|
||||||
'score': totalScore.clamp(0, 100),
|
|
||||||
'details': details,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,59 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:isolate';
|
||||||
|
|
||||||
class PerformanceTester {
|
class PerformanceTester {
|
||||||
/// ✅ فحص سرعة الكتابة إلى التخزين (Storage Write Speed) بوحدة MB/s
|
/// ✅ فحص سرعة الكتابة إلى التخزين (Storage Write Speed) بوحدة MB/s
|
||||||
static Future<double> testStorageWriteSpeed() async {
|
/// يكرر القياس عدة مرات ويأخذ المتوسط الوسيط لتقليل تأثير الحمل العرضي على الجهاز.
|
||||||
try {
|
static Future<double> testStorageWriteSpeed({int samples = 3}) async {
|
||||||
final tempDir = Directory.systemTemp;
|
final List<double> speeds = [];
|
||||||
final testFile = File('${tempDir.path}/speed_test.txt');
|
for (int i = 0; i < samples; i++) {
|
||||||
final data = List<int>.filled(1024 * 1024 * 5, 0); // 5MB
|
try {
|
||||||
|
final tempDir = Directory.systemTemp;
|
||||||
|
final testFile = File('${tempDir.path}/speed_test_$i.txt');
|
||||||
|
final data = List<int>.filled(1024 * 1024 * 5, 0); // 5MB
|
||||||
|
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
await testFile.writeAsBytes(data, flush: true);
|
await testFile.writeAsBytes(data, flush: true);
|
||||||
stopwatch.stop();
|
stopwatch.stop();
|
||||||
|
|
||||||
await testFile.delete();
|
await testFile.delete();
|
||||||
|
|
||||||
double seconds = stopwatch.elapsedMilliseconds / 1000;
|
double seconds = stopwatch.elapsedMilliseconds / 1000;
|
||||||
if (seconds == 0) seconds = 0.001;
|
if (seconds == 0) seconds = 0.001;
|
||||||
|
|
||||||
final speed = 5 / seconds;
|
speeds.add(5 / seconds);
|
||||||
return double.parse(speed.toStringAsFixed(2));
|
} catch (e) {
|
||||||
} catch (e) {
|
print("❌ Storage write error: $e");
|
||||||
print("❌ Storage write error: $e");
|
}
|
||||||
return 0.0;
|
|
||||||
}
|
}
|
||||||
|
if (speeds.isEmpty) return 0.0;
|
||||||
|
speeds.sort();
|
||||||
|
final median = speeds[speeds.length ~/ 2];
|
||||||
|
return double.parse(median.toStringAsFixed(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// عملية حسابية مكثفة تُنفَّذ داخل isolate منفصل حتى لا تجمّد واجهة المستخدم.
|
||||||
|
static double _cpuBurn(int iterations) {
|
||||||
|
double x = 0;
|
||||||
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
x += i * 0.000001;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ✅ فحص سرعة المعالج (CPU Compute Speed) بوحدة الثواني
|
/// ✅ فحص سرعة المعالج (CPU Compute Speed) بوحدة الثواني
|
||||||
static Future<double> testCPUSpeed() async {
|
/// يعمل في isolate منفصل (لا يجمّد الواجهة) ويكرر القياس ويأخذ الوسيط.
|
||||||
|
static Future<double> testCPUSpeed({int samples = 3}) async {
|
||||||
try {
|
try {
|
||||||
final stopwatch = Stopwatch()..start();
|
final List<double> times = [];
|
||||||
double x = 0;
|
for (int i = 0; i < samples; i++) {
|
||||||
for (int i = 0; i < 100000000; i++) {
|
final stopwatch = Stopwatch()..start();
|
||||||
x += i * 0.000001;
|
await Isolate.run(() => _cpuBurn(100000000));
|
||||||
|
stopwatch.stop();
|
||||||
|
times.add(stopwatch.elapsedMilliseconds / 1000.0);
|
||||||
}
|
}
|
||||||
stopwatch.stop();
|
times.sort();
|
||||||
return stopwatch.elapsedMilliseconds / 1000.0;
|
return times[times.length ~/ 2];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("❌ CPU compute error: $e");
|
print("❌ CPU compute error: $e");
|
||||||
return 999.0;
|
return 999.0;
|
||||||
|
|||||||
Reference in New Issue
Block a user