| #!/usr/bin/env python3 |
| # Copyright 2022 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Test and benchmark the experiment module.""" |
| |
| import pathlib |
| import tempfile |
| import textwrap |
| import unittest |
| |
| from experiment import * |
| import fpsutils |
| import pandas as pd |
| import simulate_fpstudy |
| |
| |
| class Test_has_user_groups(unittest.TestCase): |
| """Test the `has_user_groups` method.""" |
| |
| BARE_DECISIONS_TABLE = pd.DataFrame( |
| columns=Experiment.DECISION_TABLE_COLS, |
| ) |
| BARE_DECISIONS_TABLE_WITH_GROUPS = pd.DataFrame( |
| columns=Experiment.DECISION_TABLE_COLS |
| + Experiment.DECISION_TABLE_GROUP_COLS, |
| ) |
| |
| def setUp(self) -> None: |
| """Run before each test.""" |
| self.exp = Experiment( |
| num_users=72, |
| num_fingers=3, |
| num_verification=40, |
| ) |
| |
| def test_without_any_tables(self): |
| self.assertFalse(self.exp.has_user_groups()) |
| |
| def test_with_far_no_user_groups(self): |
| self.exp.add_far_decisions(self.BARE_DECISIONS_TABLE.copy()) |
| self.assertFalse(self.exp.has_user_groups()) |
| |
| def test_with_far_and_user_groups(self): |
| self.exp.add_far_decisions(self.BARE_DECISIONS_TABLE_WITH_GROUPS.copy()) |
| self.assertTrue(self.exp.has_user_groups()) |
| |
| def test_with_frr_no_user_groups(self): |
| self.exp.add_frr_decisions(self.BARE_DECISIONS_TABLE.copy()) |
| self.assertFalse(self.exp.has_user_groups()) |
| |
| def test_with_frr_and_user_groups(self): |
| self.exp.add_frr_decisions(self.BARE_DECISIONS_TABLE_WITH_GROUPS.copy()) |
| self.assertTrue(self.exp.has_user_groups()) |
| |
| |
| class Test_fa_query(unittest.TestCase): |
| def setUp(self): |
| # VerifyUser VerifyFinger VerifySample EnrollUser EnrollFinger Decision |
| # This can take about 2 seconds. |
| self.far_decisions = simulate_fpstudy.GenerateFARResults( |
| num_users=72, |
| num_fingers=3, |
| num_verify_samples=40, |
| user_groups=None, |
| verbose=True, |
| ) |
| self.exp = Experiment( |
| num_users=72, |
| num_fingers=3, |
| num_verification=40, |
| far_decisions=self.far_decisions, |
| ) |
| |
| def test_benchmark_fa_query1(self): |
| print("Query 1 Timing") |
| |
| fpsutils.benchmark( |
| "self.exp.fa_query(verify_user_id=10071)", |
| globals={**globals(), **locals()}, |
| ) |
| fpsutils.benchmark( |
| "self.exp.fa_query(verify_user_id=10071, verify_finger_id=0)", |
| globals={**globals(), **locals()}, |
| ) |
| fpsutils.benchmark( |
| "self.exp.fa_query(verify_user_id=10071, verify_finger_id=0, verify_sample_index=60)", |
| globals={**globals(), **locals()}, |
| ) |
| fpsutils.benchmark( |
| "self.exp.fa_query(verify_user_id=10071, verify_finger_id=0, verify_sample_index=60, enroll_user_id=10057)", |
| globals={**globals(), **locals()}, |
| ) |
| fpsutils.benchmark( |
| "self.exp.fa_query(verify_user_id=10071, verify_finger_id=0, verify_sample_index=60, enroll_user_id=10057, enroll_finger_id=4)", |
| globals={**globals(), **locals()}, |
| ) |
| |
| def test_benchmark_fa_query2(self): |
| print("Query 2 Timing") |
| |
| fpsutils.benchmark( |
| "self.exp.fa_query2(verify_user_id=10071)", |
| globals={**globals(), **locals()}, |
| ) |
| fpsutils.benchmark( |
| "self.exp.fa_query2(verify_user_id=10071, verify_finger_id=0)", |
| globals={**globals(), **locals()}, |
| ) |
| fpsutils.benchmark( |
| "self.exp.fa_query2(verify_user_id=10071, verify_finger_id=0, verify_sample_index=60)", |
| globals={**globals(), **locals()}, |
| ) |
| fpsutils.benchmark( |
| "self.exp.fa_query2(verify_user_id=10071, verify_finger_id=0, verify_sample_index=60, enroll_user_id=10057)", |
| globals={**globals(), **locals()}, |
| ) |
| fpsutils.benchmark( |
| "self.exp.fa_query2(verify_user_id=10071, verify_finger_id=0, verify_sample_index=60, enroll_user_id=10057, enroll_finger_id=4)", |
| globals={**globals(), **locals()}, |
| ) |
| |
| |
| class Test_Experiment_CSV(unittest.TestCase): |
| """Test the CSV reading and writing capabilities of `Experiment`.""" |
| |
| CSV_FAR = textwrap.dedent( |
| """\ |
| EnrollUser,EnrollFinger,VerifyUser,VerifyFinger,VerifySample,Decision |
| 10001,1,10002,3,12,REJECT |
| 10001,1,10002,3,13,REJECT |
| """ |
| ) |
| CSV_FAR_DATAFRAME = pd.DataFrame( |
| { |
| Experiment.TableCol.Enroll_User.value: [10001, 10001], |
| Experiment.TableCol.Enroll_Finger.value: [1, 1], |
| Experiment.TableCol.Verify_User.value: [10002, 10002], |
| Experiment.TableCol.Verify_Finger.value: [3, 3], |
| Experiment.TableCol.Verify_Sample.value: [12, 13], |
| Experiment.TableCol.Decision.value: [ |
| Experiment.Decision.Reject.value, |
| Experiment.Decision.Reject.value, |
| ], |
| } |
| ) |
| |
| CSV_FRR = textwrap.dedent( |
| """\ |
| EnrollUser,EnrollFinger,VerifyUser,VerifyFinger,VerifySample,Decision |
| 10001,1,10001,1,25,ACCEPT |
| 10001,2,10001,2,25,ACCEPT |
| """ |
| ) |
| CSV_FRR_DATAFRAME = pd.DataFrame( |
| { |
| Experiment.TableCol.Enroll_User.value: [10001, 10001], |
| Experiment.TableCol.Enroll_Finger.value: [1, 2], |
| Experiment.TableCol.Verify_User.value: [10001, 10001], |
| Experiment.TableCol.Verify_Finger.value: [1, 2], |
| Experiment.TableCol.Verify_Sample.value: [25, 25], |
| Experiment.TableCol.Decision.value: [ |
| Experiment.Decision.Accept.value, |
| Experiment.Decision.Accept.value, |
| ], |
| } |
| ) |
| |
| CSV_USER_GROUP = textwrap.dedent( |
| """\ |
| User,Group |
| 10001,A |
| 10002,B |
| """ |
| ) |
| CSV_USER_GROUP_DATAFRAME = pd.DataFrame( |
| { |
| Experiment.TableCol.User.value: [10001, 10002], |
| Experiment.TableCol.Group.value: ["A", "B"], |
| } |
| ) |
| |
| def setUp(self) -> None: |
| self.temp_dir = tempfile.TemporaryDirectory() |
| self.temp_csv = pathlib.Path(self.temp_dir.name) / "test.csv" |
| |
| def tearDown(self) -> None: |
| self.temp_dir.cleanup() |
| return super().tearDown() |
| |
| def test_read_far(self): |
| exp = Experiment(num_verification=0, num_fingers=0, num_users=0) |
| |
| self.temp_csv.write_text(self.CSV_FAR) |
| exp.add_far_decisions_from_csv(self.temp_csv) |
| self.assertTrue(exp.has_far_decisions()) |
| self.assertFalse(exp.has_frr_decisions()) |
| df = exp.far_decisions() |
| self.assertTrue(df.equals(self.CSV_FAR_DATAFRAME)) |
| |
| def test_read_frr(self): |
| exp = Experiment(num_verification=0, num_fingers=0, num_users=0) |
| |
| self.temp_csv.write_text(self.CSV_FRR) |
| exp.add_frr_decisions_from_csv(self.temp_csv) |
| self.assertFalse(exp.has_far_decisions()) |
| self.assertTrue(exp.has_frr_decisions()) |
| df = exp.frr_decisions() |
| self.assertTrue(df.equals(self.CSV_FRR_DATAFRAME)) |
| |
| def test_write_far(self): |
| exp = Experiment( |
| num_verification=0, |
| num_fingers=0, |
| num_users=0, |
| far_decisions=self.CSV_FAR_DATAFRAME, |
| ) |
| self.temp_csv.unlink(missing_ok=True) |
| exp.far_decisions_to_csv(self.temp_csv) |
| self.assertEqual(self.temp_csv.read_text(), self.CSV_FAR) |
| |
| def test_write_frr(self): |
| exp = Experiment( |
| num_verification=0, |
| num_fingers=0, |
| num_users=0, |
| frr_decisions=self.CSV_FRR_DATAFRAME, |
| ) |
| self.temp_csv.unlink(missing_ok=True) |
| exp.frr_decisions_to_csv(self.temp_csv) |
| self.assertEqual(self.temp_csv.read_text(), self.CSV_FRR) |
| |
| def test_read_user_group(self): |
| exp = Experiment(num_verification=0, num_fingers=0, num_users=0) |
| |
| self.temp_csv.write_text(self.CSV_USER_GROUP) |
| exp.add_groups_from_csv(self.temp_csv) |
| self.assertTrue(exp.has_user_groups()) |
| df = exp.user_groups_table() |
| self.assertTrue(df.equals(self.CSV_USER_GROUP_DATAFRAME)) |
| |
| def test_write_user_group(self): |
| exp = Experiment(num_verification=0, num_fingers=0, num_users=0) |
| |
| exp.add_groups(self.CSV_USER_GROUP_DATAFRAME) |
| self.assertTrue(exp.has_user_groups()) |
| self.temp_csv.unlink(missing_ok=True) |
| exp.user_groups_table_to_csv(self.temp_csv) |
| self.assertEqual(self.temp_csv.read_text(), self.CSV_USER_GROUP) |
| |
| |
| class Test_Experiment_User_Groups(unittest.TestCase): |
| """Test the User Group capabilities of `Experiment`.""" |
| |
| def setUp(self) -> None: |
| self.temp_dir = tempfile.TemporaryDirectory() |
| self.temp_csv = pathlib.Path(self.temp_dir.name) / "test.csv" |
| |
| def tearDown(self) -> None: |
| self.temp_dir.cleanup() |
| return super().tearDown() |
| |
| FRR_DATAFRAME_GROUPS = pd.DataFrame( |
| { |
| Experiment.TableCol.Enroll_User.value: [10001, 10002], |
| Experiment.TableCol.Enroll_Finger.value: [1, 2], |
| Experiment.TableCol.Verify_User.value: [10001, 10002], |
| Experiment.TableCol.Verify_Finger.value: [1, 2], |
| Experiment.TableCol.Verify_Sample.value: [25, 25], |
| Experiment.TableCol.Decision.value: [ |
| Experiment.Decision.Accept.value, |
| Experiment.Decision.Accept.value, |
| ], |
| # With Enroll and Verify groups. |
| Experiment.TableCol.Enroll_Group.value: ["A", "B"], |
| Experiment.TableCol.Verify_Group.value: ["A", "B"], |
| } |
| ) |
| |
| FAR_DATAFRAME_GROUPS = pd.DataFrame( |
| { |
| Experiment.TableCol.Enroll_User.value: [10001, 10001], |
| Experiment.TableCol.Enroll_Finger.value: [1, 1], |
| Experiment.TableCol.Verify_User.value: [10002, 10002], |
| Experiment.TableCol.Verify_Finger.value: [3, 3], |
| Experiment.TableCol.Verify_Sample.value: [12, 13], |
| Experiment.TableCol.Decision.value: [ |
| Experiment.Decision.Reject.value, |
| Experiment.Decision.Reject.value, |
| ], |
| # With Enroll and Verify groups. |
| Experiment.TableCol.Enroll_Group.value: ["A", "A"], |
| Experiment.TableCol.Verify_Group.value: ["B", "B"], |
| } |
| ) |
| """This example requires the groups to be scanned from both enroll and verify.""" |
| |
| USER_GROUPS_TABLE = pd.DataFrame( |
| { |
| Experiment.TableCol.User.value: [10001, 10002], |
| Experiment.TableCol.Group.value: ["A", "B"], |
| } |
| ) |
| |
| def test_infer_groups_from_frr(self): |
| """Test whether the user_groups can be scanned from the FRR table.""" |
| exp = Experiment(num_verification=0, num_fingers=0, num_users=0) |
| |
| exp.add_frr_decisions(self.FRR_DATAFRAME_GROUPS) |
| self.assertTrue(exp.has_user_groups()) |
| user_groups = exp.user_groups_table() |
| self.assertIsNotNone(user_groups) |
| self.assertTrue(user_groups.equals(self.USER_GROUPS_TABLE)) |
| |
| def test_infer_groups_from_far(self): |
| """Test whether the user_groups can be scanned from the FAR table. |
| |
| Additionally, this checks whether groups are detected from both enroll |
| and verify columns. |
| """ |
| exp = Experiment(num_verification=0, num_fingers=0, num_users=0) |
| |
| exp.add_far_decisions(self.FAR_DATAFRAME_GROUPS) |
| self.assertTrue(exp.has_user_groups()) |
| user_groups = exp.user_groups_table() |
| self.assertIsNotNone(user_groups) |
| self.assertTrue(user_groups.equals(self.USER_GROUPS_TABLE)) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |