@@ -4,7 +4,7 @@ use crate::error::PythonError;
44use crate :: policy:: Python3StudentFilePolicy ;
55use crate :: python_test_result:: PythonTestResult ;
66use lazy_static:: lazy_static;
7- use std:: collections:: HashMap ;
7+ use std:: collections:: { HashMap , HashSet } ;
88use std:: env;
99use std:: io:: BufReader ;
1010use std:: path:: { Path , PathBuf } ;
@@ -176,17 +176,20 @@ impl Python3Plugin {
176176 let test_results: Vec < PythonTestResult > =
177177 serde_json:: from_reader ( BufReader :: new ( results_file) )
178178 . map_err ( |e| PythonError :: Deserialize ( test_results_json. to_path_buf ( ) , e) ) ?;
179- let test_results: Vec < TestResult > = test_results
180- . into_iter ( )
181- . map ( PythonTestResult :: into_test_result)
182- . collect ( ) ;
183179
184180 let mut status = RunStatus :: Passed ;
181+ let mut failed_points = HashSet :: new ( ) ;
185182 for result in & test_results {
186- if !result. successful {
183+ if !result. passed {
187184 status = RunStatus :: TestsFailed ;
185+ failed_points. extend ( result. points . iter ( ) . cloned ( ) ) ;
188186 }
189187 }
188+
189+ let test_results: Vec < TestResult > = test_results
190+ . into_iter ( )
191+ . map ( |r| r. into_test_result ( & failed_points) )
192+ . collect ( ) ;
190193 Ok ( RunResult :: new ( status, test_results, logs) )
191194 }
192195}
@@ -253,7 +256,21 @@ impl LanguagePlugin for Python3Plugin {
253256 if test_results_json. exists ( ) {
254257 file_util:: remove_file ( & test_results_json) ?;
255258 }
256- Ok ( parse_res?)
259+
260+ let mut run_result = parse_res?;
261+
262+ // remove points associated with any failed tests
263+ let mut failed_points = HashSet :: new ( ) ;
264+ for test_result in & run_result. test_results {
265+ if !test_result. successful {
266+ failed_points. extend ( test_result. points . iter ( ) . cloned ( ) ) ;
267+ }
268+ }
269+ for test_result in & mut run_result. test_results {
270+ test_result. points . retain ( |p| !failed_points. contains ( p) ) ;
271+ }
272+
273+ Ok ( run_result)
257274 }
258275 Err ( PythonError :: Tmc ( TmcError :: Command ( CommandError :: TimeOut {
259276 stdout,
@@ -708,4 +725,42 @@ class TestErroring(unittest.TestCase):
708725 let res = Python3Plugin :: find_project_dir_in_zip ( & mut zip) ;
709726 assert ! ( res. is_err( ) ) ;
710727 }
728+
729+ #[ test]
730+ fn doesnt_give_points_unless_all_relevant_exercises_pass ( ) {
731+ init ( ) ;
732+
733+ let temp_dir = temp_with_tmc ( ) ;
734+ file_to ( & temp_dir, "test/__init__.py" , "" ) ;
735+ file_to (
736+ & temp_dir,
737+ "test/test_file.py" ,
738+ r#"
739+ import unittest
740+ from tmc import points
741+
742+ @points('1')
743+ class TestClass(unittest.TestCase):
744+ @points('1.1', '1.2')
745+ def test_func1(self):
746+ self.assertTrue(False)
747+
748+ @points('1.1', '1.3')
749+ def test_func2(self):
750+ self.assertTrue(True)
751+ "# ,
752+ ) ;
753+
754+ let plugin = Python3Plugin :: new ( ) ;
755+ let results = plugin. run_tests ( temp_dir. path ( ) , & mut vec ! [ ] ) . unwrap ( ) ;
756+ assert_eq ! ( results. status, RunStatus :: TestsFailed ) ;
757+ let mut got_point = false ;
758+ for test in results. test_results {
759+ got_point = got_point || test. points . contains ( & "1.3" . to_string ( ) ) ;
760+ assert ! ( !test. points. contains( & "1" . to_string( ) ) ) ;
761+ assert ! ( !test. points. contains( & "1.1" . to_string( ) ) ) ;
762+ assert ! ( !test. points. contains( & "1.2" . to_string( ) ) ) ;
763+ }
764+ assert ! ( got_point) ;
765+ }
711766}
0 commit comments