Skip to content

Commit

Permalink
feat: optionally group results (#543)
Browse files Browse the repository at this point in the history
## Proposed Changes

  - Update `TestSummaryViewController` to support grouping.

|.|.|.|
|-|-|-|
|![Simulator Screenshot - iPhone 15 Pro Max - 2023-11-30 at 14 50
48](https://github.com/ooni/probe-ios/assets/17911892/4c4cb030-5edc-400a-b2d7-075d755b804e)
| ![Simulator Screenshot - iPhone 15 Pro Max - 2023-11-30 at 14 50
52](https://github.com/ooni/probe-ios/assets/17911892/d6b29295-468d-4e29-a355-674b2b6322df)
| ![Simulator Screenshot - iPhone 15 Pro Max - 2023-11-30 at 14 10
27](https://github.com/ooni/probe-ios/assets/17911892/c72b4e10-d59d-440b-bbdb-93bcb7d9df2c)
|
| ![Simulator Screenshot - iPhone 15 Pro Max - 2023-11-30 at 14 51
06](https://github.com/ooni/probe-ios/assets/17911892/3c33595f-8ab7-4252-80b9-e985b35d6097)
| ![Simulator Screenshot - iPhone 15 Pro Max - 2023-11-30 at 14 51
08](https://github.com/ooni/probe-ios/assets/17911892/d3a22b74-6dbb-4390-8e67-483c77984259)|
.|

---------

Co-authored-by: Simone Basso <bassosimone@gmail.com>
  • Loading branch information
aanorbel and bassosimone authored Jan 22, 2024
1 parent 80aacab commit a70070a
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 64 deletions.
1 change: 1 addition & 0 deletions ooniprobe/View/TestResults/TestSummaryViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
@property (nonatomic, strong) IBOutlet UITableView *tableView;
@property (nonatomic, strong) Result* result;
@property (nonatomic, strong) SRKResultSet *measurements;
@property (nonatomic, strong) NSArray *groupedMeasurements;
@end
204 changes: 140 additions & 64 deletions ooniprobe/View/TestResults/TestSummaryViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#import "RunningTest.h"

@interface TestSummaryViewController ()

@property(nonatomic, strong) NSMutableDictionary<NSNumber *, NSNumber *> *expandedSections;
@end

@implementation TestSummaryViewController
Expand All @@ -22,12 +22,13 @@ - (void)viewDidLoad {
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reRunWebsites)];

[self reloadMeasurements];
_expandedSections = [NSMutableDictionary new];
defaultColor = [TestUtility getColorForTest:result.test_group_name];
[NavigationBarUtility setNavigationBar:self.navigationController.navigationBar color:defaultColor];
self.navigationController.navigationBar.topItem.title = @"";
}

-(void)viewWillAppear:(BOOL)animated{
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self reloadConstraints];
self.title = @"";
Expand All @@ -36,16 +37,15 @@ -(void)viewWillAppear:(BOOL)animated{
color:[TestUtility getBackgroundColorForTest:result.test_group_name]];
}

-(void)reloadConstraints{
- (void)reloadConstraints {
CGFloat tableConstraint = 0;
CGFloat uploadConstraint = 0;
if ([RunningTest currentTest].isTestRunning){
if ([RunningTest currentTest].isTestRunning) {
uploadConstraint += 64;
}
if ([result isEveryMeasurementUploaded]){
if ([result isEveryMeasurementUploaded]) {
tableConstraint = -45 + tableConstraint;
}
else {
} else {
tableConstraint = 64 + tableConstraint;
}
dispatch_async(dispatch_get_main_queue(), ^{
Expand All @@ -64,17 +64,39 @@ - (void)willMoveToParentViewController:(UIViewController *)parent {
}
}

- (void)resultUpdated:(NSNotification *)notification
{
- (void)resultUpdated:(NSNotification *)notification {
if (result.Id != ((Result *) [notification object]).Id) {
return;
}
result = [notification object];
[self reloadMeasurements];
}

-(void)reloadMeasurements{
- (void)reloadMeasurements {
self.measurements = result.measurementsSorted;
// Create a mutable dictionary to hold the grouped measurements
NSMutableDictionary *groupedMeasurements = [NSMutableDictionary new];

// Iterate over each measurement
for (Measurement *measurement in self.measurements) {
// Get the test_name of the current measurement
NSString *testName = measurement.test_name;

// If this test_name is not already a key in the dictionary, add it
// with the current measurement as the value in an array
if (!groupedMeasurements[testName]) {
groupedMeasurements[testName] = [NSMutableArray arrayWithObject:measurement];
}
// If this test_name is already a key in the dictionary, append the current
// measurement to the existing array
else {
[groupedMeasurements[testName] addObject:measurement];
}
}

// Replace the measurements array with the grouped measurements
self.groupedMeasurements = [groupedMeasurements allValues];

[self reloadConstraints];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
Expand All @@ -83,46 +105,103 @@ -(void)reloadMeasurements{

#pragma mark - Table view data source

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
return 52;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
return [self.groupedMeasurements count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.measurements count];
if (self.expandedSections[@(section)]) {
NSArray *group = self.groupedMeasurements[section];
return 1 + group.count;
}
return 1;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Measurement *current = [self.measurements objectAtIndex:indexPath.row];

NSString *cell_id;
if ([result.test_group_name isEqualToString:@"performance"])
if ([result.test_group_name isEqualToString:@"performance"]) {
cell_id = @"Cell_per";
//__deprecated
else if ([result.test_group_name isEqualToString:@"middle_boxes"] ||
[result.test_group_name isEqualToString:@"experimental"])
cell_id = @"Cell_mb";
else
//__deprecated
} else if ([result.test_group_name isEqualToString:@"middle_boxes"] ||
[result.test_group_name isEqualToString:@"experimental"]) {
cell_id = @"Cell_mb";
} else {
cell_id = @"Cell";
}
TestSummaryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cell_id forIndexPath:indexPath];
if (cell == nil) {
cell = [[TestSummaryTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cell_id];
}
[cell setResult:result andMeasurement:current];

if (indexPath.row == 0) {
Measurement *current = self.groupedMeasurements[indexPath.section][0];

[cell setResult:result andMeasurement:current];
if ([self.groupedMeasurements[indexPath.section] count] > 1) {
UIImage *image = [UIImage imageNamed:@"backArrow"];

// Define the rotation orientation
UIImageOrientation orientation = self.expandedSections[@(indexPath.section)] ? UIImageOrientationRight : UIImageOrientationLeft; // 90 degrees rotation

// Create a new image with the new orientation
UIImage *rotatedImage = [UIImage imageWithCGImage:image.CGImage scale:3.0 orientation:orientation];

// Display the rotated image
cell.accessoryView = [[UIImageView alloc] initWithImage:[rotatedImage imageWithTintColor:[UIColor colorNamed:@"color_gray4"]]];
} else {
cell.accessoryView = nil;
}
cell.backgroundColor = [UIColor colorNamed:@"color_white"];
} else {
Measurement *current = self.groupedMeasurements[indexPath.section][indexPath.row - 1];
[cell setResult:result andMeasurement:current];
cell.accessoryView = nil;
cell.backgroundColor = [UIColor colorNamed:@"color_gray0"];
}
return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
segueObj = [self.measurements objectAtIndex:indexPath.row];
[self goToDetails];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSArray *group = self.groupedMeasurements[indexPath.section];

// Check if it's a parent row
if (indexPath.row == 0) {
if (group.count == 1) {
// This is a parent with no children. Adding our click listener code.
segueObj = group[0];
[self goToDetails];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
} else {
NSNumber *sectionKey = @(indexPath.section);

// Check if the section is already expanded
if (self.expandedSections[sectionKey]) {
// If it is, remove the key to collapse it
[self.expandedSections removeObjectForKey:sectionKey];
} else {
// If it's not, add the key to expand it
self.expandedSections[sectionKey] = @YES;
}

// Reload the section to reflect the changes
[tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic];
}
} else {
// This is a child. Adding our click listener code.
segueObj = group[indexPath.row - 1];
[self goToDetails];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

}

-(void)goToDetails{
- (void)goToDetails {
if ([result.test_group_name isEqualToString:@"experimental"])
[self performSegueWithIdentifier:@"toViewLog" sender:self];
else if (segueObj.is_failed)
Expand All @@ -132,78 +211,75 @@ -(void)goToDetails{
else if ([segueObj.test_name isEqualToString:@"dash"])
[self performSegueWithIdentifier:@"toDashTestDetails" sender:self];
else if ([segueObj.test_name isEqualToString:@"whatsapp"] ||
[segueObj.test_name isEqualToString:@"telegram"] ||
[segueObj.test_name isEqualToString:@"facebook_messenger"] ||
[segueObj.test_name isEqualToString:@"signal"])
[segueObj.test_name isEqualToString:@"telegram"] ||
[segueObj.test_name isEqualToString:@"facebook_messenger"] ||
[segueObj.test_name isEqualToString:@"signal"])
[self performSegueWithIdentifier:@"toInstantMessagingTestDetails" sender:self];
else if ([segueObj.test_name isEqualToString:@"http_invalid_request_line"] ||
[segueObj.test_name isEqualToString:@"http_header_field_manipulation"])
[segueObj.test_name isEqualToString:@"http_header_field_manipulation"])
[self performSegueWithIdentifier:@"toMiddleBoxesTestDetails" sender:self];
else if ([segueObj.test_name isEqualToString:@"web_connectivity"])
[self performSegueWithIdentifier:@"toWebsitesTestDetails" sender:self];
else if ([segueObj.test_name isEqualToString:@"tor"] ||
[segueObj.test_name isEqualToString:@"psiphon"] ||
[segueObj.test_name isEqualToString:@"riseupvpn"])
[segueObj.test_name isEqualToString:@"psiphon"] ||
[segueObj.test_name isEqualToString:@"riseupvpn"])
[self performSegueWithIdentifier:@"toCircumventionTestDetails" sender:self];
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (scrollView.contentOffset.y<=0) {
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y <= 0) {
scrollView.contentOffset = CGPointZero;
}
}

-(void)reRunTests{
- (void)reRunTests {
WebsitesSuite *testSuite = [[WebsitesSuite alloc] init];
NSMutableArray *urls = [NSMutableArray new];
for (Measurement *m in self.measurements)
[urls addObject:m.url_id.url];
if ([testSuite getTestList] > 0 && [urls count] > 0)
[(WebConnectivity*)[[testSuite getTestList] objectAtIndex:0] setInputs:urls];
[[RunningTest currentTest] setAndRun:[NSMutableArray arrayWithObject:testSuite] inView: self];
[(WebConnectivity *) [[testSuite getTestList] objectAtIndex:0] setInputs:urls];
[[RunningTest currentTest] setAndRun:[NSMutableArray arrayWithObject:testSuite] inView:self];
[self reloadMeasurements];
}

-(void)reRunWebsites{
UIAlertAction* okButton = [UIAlertAction
actionWithTitle:NSLocalizedString(@"Modal.ReRun.Websites.Run", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if ([[ReachabilityManager sharedManager].reachability currentReachabilityStatus] != NotReachable)
[self reRunTests];
}];
- (void)reRunWebsites {
UIAlertAction *okButton = [UIAlertAction
actionWithTitle:NSLocalizedString(@"Modal.ReRun.Websites.Run", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
if ([[ReachabilityManager sharedManager].reachability currentReachabilityStatus] != NotReachable)
[self reRunTests];
}];
NSString *title = NSLocalizedFormatString(@"Modal.ReRun.Websites.Title",
[NSString stringWithFormat:@"%ld", self.measurements.count]);
[NSString stringWithFormat:@"%ld", self.measurements.count]);
[MessageUtility alertWithTitle:title
message:nil
okButton:okButton
inView:self];

}

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"header"]){
HeaderSwipeViewController *vc = (HeaderSwipeViewController *)segue.destinationViewController;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"header"]) {
HeaderSwipeViewController *vc = (HeaderSwipeViewController *) segue.destinationViewController;
[vc setResult:result];
}
else if ([[segue identifier] isEqualToString:@"toWebsitesTestDetails"] ||
[[segue identifier] isEqualToString:@"toMiddleBoxesTestDetails"] ||
[[segue identifier] isEqualToString:@"toInstantMessagingTestDetails"] ||
[[segue identifier] isEqualToString:@"toNdtTestDetails"] ||
[[segue identifier] isEqualToString:@"toDashTestDetails"] ||
[[segue identifier] isEqualToString:@"toFailedTestDetails"] ||
[[segue identifier] isEqualToString:@"toCircumventionTestDetails"]){
TestDetailsViewController *vc = (TestDetailsViewController *)segue.destinationViewController;
} else if ([[segue identifier] isEqualToString:@"toWebsitesTestDetails"] ||
[[segue identifier] isEqualToString:@"toMiddleBoxesTestDetails"] ||
[[segue identifier] isEqualToString:@"toInstantMessagingTestDetails"] ||
[[segue identifier] isEqualToString:@"toNdtTestDetails"] ||
[[segue identifier] isEqualToString:@"toDashTestDetails"] ||
[[segue identifier] isEqualToString:@"toFailedTestDetails"] ||
[[segue identifier] isEqualToString:@"toCircumventionTestDetails"]) {
TestDetailsViewController *vc = (TestDetailsViewController *) segue.destinationViewController;
[vc setResult:result];
[vc setMeasurement:segueObj];
}
else if ([[segue identifier] isEqualToString:@"footer_upload"]){
UploadFooterViewController *vc = (UploadFooterViewController * )segue.destinationViewController;
} else if ([[segue identifier] isEqualToString:@"footer_upload"]) {
UploadFooterViewController *vc = (UploadFooterViewController *) segue.destinationViewController;
[vc setResult:result];
[vc setUpload_all:true];
}
else if ([[segue identifier] isEqualToString:@"toViewLog"]){
LogViewController *vc = (LogViewController *)segue.destinationViewController;
} else if ([[segue identifier] isEqualToString:@"toViewLog"]) {
LogViewController *vc = (LogViewController *) segue.destinationViewController;
[vc setType:@"json"];
[vc setMeasurement:segueObj];
}
Expand Down

0 comments on commit a70070a

Please # to comment.