@@ -581,3 +581,272 @@ TEST (ColorTests, Chaining_Operations)
581581 EXPECT_EQ (modified.getGreen (), 128 );
582582 EXPECT_EQ (modified.getBlue (), 64 );
583583}
584+
585+ TEST (ColorTests, HSL_String_Parsing)
586+ {
587+ // Test basic HSL parsing (hue normalized to 0-1 range, not degrees)
588+ // Green: hue = 120/360 = 0.333...
589+ Color fromHSL = Color::fromString (" hsl(0.333, 1, 0.5)" );
590+ EXPECT_GT (fromHSL.getGreen (), 200 ); // Green dominant
591+ EXPECT_LT (fromHSL.getRed (), 50 );
592+ EXPECT_LT (fromHSL.getBlue (), 50 );
593+
594+ // Test HSL with percentage values
595+ // Blue: hue = 240/360 = 0.666...
596+ Color fromHSLPercent = Color::fromString (" hsl(0.666, 100%, 50%)" );
597+ EXPECT_GT (fromHSLPercent.getBlue (), 200 ); // Blue dominant
598+ EXPECT_LT (fromHSLPercent.getRed (), 50 );
599+ EXPECT_LT (fromHSLPercent.getGreen (), 50 );
600+
601+ // Test HSLA parsing
602+ // Red: hue = 0
603+ Color fromHSLA = Color::fromString (" hsla(0, 1, 0.5, 0.5)" );
604+ EXPECT_GT (fromHSLA.getRed (), 200 ); // Red dominant
605+ EXPECT_LT (fromHSLA.getGreen (), 50 );
606+ EXPECT_LT (fromHSLA.getBlue (), 50 );
607+ EXPECT_NEAR (fromHSLA.getAlpha (), 127 , 1 ); // Alpha ~0.5
608+
609+ // Test HSLA with percentage and decimal values mixed
610+ // Yellow: hue = 60/360 = 0.166...
611+ Color fromHSLAMixed = Color::fromString (" hsla(0.166, 100%, 50%, 0.75)" );
612+ EXPECT_GT (fromHSLAMixed.getRed (), 200 ); // Yellow (red + green)
613+ EXPECT_GT (fromHSLAMixed.getGreen (), 200 );
614+ EXPECT_LT (fromHSLAMixed.getBlue (), 50 );
615+ EXPECT_NEAR (fromHSLAMixed.getAlpha (), 191 , 1 ); // Alpha ~0.75
616+
617+ // Test HSL with spaces and commas
618+ // Cyan: hue = 180/360 = 0.5
619+ Color fromHSLSpaces = Color::fromString (" hsl( 0.5 , 1 , 0.5 )" );
620+ EXPECT_GT (fromHSLSpaces.getBlue (), 200 ); // Cyan (green + blue)
621+ EXPECT_GT (fromHSLSpaces.getGreen (), 200 );
622+ EXPECT_LT (fromHSLSpaces.getRed (), 50 );
623+
624+ // Test invalid HSL format (should return transparentBlack)
625+ Color fromInvalidHSL = Color::fromString (" hsl_invalid(0, 0, 0)" );
626+ EXPECT_EQ (fromInvalidHSL.getARGB (), Colors::transparentBlack);
627+ }
628+
629+ TEST (ColorTests, RGB_String_Parsing_EdgeCases)
630+ {
631+ // Test invalid RGB format (should return transparentBlack)
632+ Color fromInvalidRGB = Color::fromString (" rgb_invalid(255, 0, 0)" );
633+ EXPECT_EQ (fromInvalidRGB.getARGB (), Colors::transparentBlack);
634+
635+ // Test RGB with extra spaces
636+ Color fromRGBSpaces = Color::fromString (" rgb( 255 , 128 , 64 )" );
637+ EXPECT_EQ (fromRGBSpaces.getRed (), 255 );
638+ EXPECT_EQ (fromRGBSpaces.getGreen (), 128 );
639+ EXPECT_EQ (fromRGBSpaces.getBlue (), 64 );
640+
641+ // Test RGB with no spaces
642+ Color fromRGBNoSpaces = Color::fromString (" rgb(255,128,64)" );
643+ EXPECT_EQ (fromRGBNoSpaces.getRed (), 255 );
644+ EXPECT_EQ (fromRGBNoSpaces.getGreen (), 128 );
645+ EXPECT_EQ (fromRGBNoSpaces.getBlue (), 64 );
646+
647+ // Test RGBA with spaces
648+ Color fromRGBASpaces = Color::fromString (" rgba( 100 , 150 , 200 , 128 )" );
649+ EXPECT_EQ (fromRGBASpaces.getRed (), 100 );
650+ EXPECT_EQ (fromRGBASpaces.getGreen (), 150 );
651+ EXPECT_EQ (fromRGBASpaces.getBlue (), 200 );
652+ EXPECT_EQ (fromRGBASpaces.getAlpha (), 128 );
653+ }
654+
655+ TEST (ColorTests, ParseNextInt_Coverage)
656+ {
657+ // Test negative number parsing via RGB (if implementation supports it)
658+ Color fromNegative = Color::fromString (" rgb(-10, 50, 100)" );
659+ EXPECT_NO_THROW (fromNegative.getRed ()); // Should handle gracefully
660+
661+ // Test numbers with leading zeros
662+ Color fromLeadingZeros = Color::fromString (" rgb(001, 050, 100)" );
663+ EXPECT_EQ (fromLeadingZeros.getRed (), 1 );
664+ EXPECT_EQ (fromLeadingZeros.getGreen (), 50 );
665+ EXPECT_EQ (fromLeadingZeros.getBlue (), 100 );
666+
667+ // Test multiple commas and spaces
668+ Color fromMultipleDelimiters = Color::fromString (" rgb( , 10 , , 20 , 30 )" );
669+ EXPECT_NO_THROW (fromMultipleDelimiters.getRed ()); // Should handle gracefully
670+ }
671+
672+ TEST (ColorTests, ParseNextFloat_Coverage)
673+ {
674+ // Test multi-digit decimal values in HSL (now that parsing is fixed)
675+ Color fromHSLDecimal = Color::fromString (" hsl(0.333, 0.75, 0.5)" );
676+ EXPECT_NO_THROW (fromHSLDecimal.getRed ()); // Should parse decimal correctly
677+
678+ // Test values without decimals
679+ Color fromHSLNoDecimal = Color::fromString (" hsl(0, 0, 0)" );
680+ EXPECT_EQ (fromHSLNoDecimal.getRed (), 0 );
681+ EXPECT_EQ (fromHSLNoDecimal.getGreen (), 0 );
682+ EXPECT_EQ (fromHSLNoDecimal.getBlue (), 0 );
683+
684+ // Test percentage values with decimals
685+ Color fromHSLPercentDecimal = Color::fromString (" hsl(0, 50.5%, 25.25%)" );
686+ EXPECT_NO_THROW (fromHSLPercentDecimal.getRed ()); // Should handle percentage with decimals
687+
688+ // Test mixed formats (decimals and percentages)
689+ Color fromHSLMixed = Color::fromString (" hsl(0.666, 80.5%, 0.625)" );
690+ EXPECT_NO_THROW (fromHSLMixed.getRed ());
691+
692+ // Test edge case: percentage at 0%
693+ Color fromHSLZeroPercent = Color::fromString (" hsl(0, 0%, 50%)" );
694+ EXPECT_NEAR (fromHSLZeroPercent.getRed (), 127 , 2 );
695+ EXPECT_NEAR (fromHSLZeroPercent.getGreen (), 127 , 2 );
696+ EXPECT_NEAR (fromHSLZeroPercent.getBlue (), 127 , 2 );
697+
698+ // Test edge case: percentage at 100%
699+ Color fromHSLHundredPercent = Color::fromString (" hsl(0, 100%, 50%)" );
700+ EXPECT_GT (fromHSLHundredPercent.getRed (), 200 );
701+ EXPECT_LT (fromHSLHundredPercent.getGreen (), 50 );
702+ EXPECT_LT (fromHSLHundredPercent.getBlue (), 50 );
703+
704+ // Test HSLA with multi-digit float alpha
705+ Color fromHSLAFloats = Color::fromString (" hsla(0.5, 0.456, 0.789, 0.625)" );
706+ EXPECT_NO_THROW (fromHSLAFloats.getRed ());
707+ EXPECT_NEAR (fromHSLAFloats.getAlpha (), 159 , 1 ); // 0.625 * 255
708+ }
709+
710+ TEST (ColorTests, FromHSL_HueToRGB_EdgeCases)
711+ {
712+ // Test to hit line 528: t < 0.0f branch in hue2rgb lambda
713+ // This occurs when h - 1.0f/3.0f is negative (when h < 1/3)
714+ Color c1 = Color::fromHSL (0 .0f , 1 .0f , 0 .5f );
715+ EXPECT_GT (c1.getRed (), 200 ); // Red dominant
716+ EXPECT_LT (c1.getGreen (), 50 );
717+ EXPECT_LT (c1.getBlue (), 50 );
718+
719+ Color c2 = Color::fromHSL (0 .1f , 1 .0f , 0 .5f );
720+ EXPECT_NO_THROW (c2.getRed ()); // Should handle h < 1/3
721+
722+ Color c3 = Color::fromHSL (0 .2f , 1 .0f , 0 .5f );
723+ EXPECT_NO_THROW (c3.getRed ()); // Should handle h < 1/3
724+
725+ // Test to hit line 530: t > 1.0f branch in hue2rgb lambda
726+ // This occurs when h + 1.0f/3.0f is > 1.0 (when h > 2/3)
727+ Color c4 = Color::fromHSL (0 .7f , 1 .0f , 0 .5f );
728+ EXPECT_NO_THROW (c4.getRed ()); // Should handle h > 2/3
729+
730+ Color c5 = Color::fromHSL (0 .9f , 1 .0f , 0 .5f );
731+ EXPECT_NO_THROW (c5.getRed ()); // Should handle h > 2/3
732+
733+ // Test edge cases for different ranges in hue2rgb
734+ // t < 1/6 (line 531-532)
735+ Color c6 = Color::fromHSL (0 .05f , 1 .0f , 0 .5f );
736+ EXPECT_NO_THROW (c6.getRed ());
737+
738+ // 1/6 <= t < 1/2 (line 533-534)
739+ Color c7 = Color::fromHSL (0 .25f , 1 .0f , 0 .5f );
740+ EXPECT_NO_THROW (c7.getRed ());
741+
742+ // 1/2 <= t < 2/3 (line 535-536)
743+ Color c8 = Color::fromHSL (0 .5f , 1 .0f , 0 .5f );
744+ EXPECT_NO_THROW (c8.getRed ());
745+
746+ // t >= 2/3 (line 537)
747+ Color c9 = Color::fromHSL (0 .8f , 1 .0f , 0 .5f );
748+ EXPECT_NO_THROW (c9.getRed ());
749+ }
750+
751+ TEST (ColorTests, FromHSV_AllSwitchCases)
752+ {
753+ // Test all 6 cases in the switch statement (lines 623-652)
754+
755+ // Case 0: hue in [0, 1/6) - red to yellow
756+ Color case0 = Color::fromHSV (0 .0f , 1 .0f , 1 .0f );
757+ EXPECT_EQ (case0.getRed (), 255 );
758+ EXPECT_EQ (case0.getGreen (), 0 );
759+ EXPECT_EQ (case0.getBlue (), 0 );
760+
761+ // Case 1: hue in [1/6, 2/6) - yellow to green
762+ Color case1 = Color::fromHSV (1 .0f / 6 .0f + 0 .05f , 1 .0f , 1 .0f );
763+ EXPECT_GT (case1.getGreen (), 200 ); // Green becoming dominant
764+ EXPECT_LT (case1.getBlue (), 50 );
765+
766+ // Case 2: hue in [2/6, 3/6) - green to cyan
767+ Color case2 = Color::fromHSV (2 .0f / 6 .0f + 0 .05f , 1 .0f , 1 .0f );
768+ EXPECT_GT (case2.getGreen (), 200 ); // Green dominant
769+ EXPECT_LT (case2.getRed (), 50 );
770+
771+ // Case 3: hue in [3/6, 4/6) - cyan to blue
772+ Color case3 = Color::fromHSV (3 .0f / 6 .0f + 0 .05f , 1 .0f , 1 .0f );
773+ EXPECT_GT (case3.getBlue (), 200 ); // Blue becoming dominant
774+ EXPECT_LT (case3.getRed (), 50 );
775+
776+ // Case 4: hue in [4/6, 5/6) - blue to magenta
777+ Color case4 = Color::fromHSV (4 .0f / 6 .0f + 0 .05f , 1 .0f , 1 .0f );
778+ EXPECT_GT (case4.getBlue (), 200 ); // Blue dominant
779+ EXPECT_LT (case4.getGreen (), 50 );
780+
781+ // Case 5: hue in [5/6, 1.0) - magenta to red
782+ Color case5 = Color::fromHSV (5 .0f / 6 .0f + 0 .05f , 1 .0f , 1 .0f );
783+ EXPECT_GT (case5.getRed (), 200 ); // Red becoming dominant
784+ EXPECT_LT (case5.getGreen (), 50 );
785+
786+ // Test exact boundaries
787+ Color boundary0 = Color::fromHSV (0 .0f , 1 .0f , 1 .0f );
788+ EXPECT_NO_THROW (boundary0.getRed ());
789+
790+ Color boundary1 = Color::fromHSV (1 .0f / 6 .0f , 1 .0f , 1 .0f );
791+ EXPECT_NO_THROW (boundary1.getRed ());
792+
793+ Color boundary2 = Color::fromHSV (2 .0f / 6 .0f , 1 .0f , 1 .0f );
794+ EXPECT_NO_THROW (boundary2.getRed ());
795+
796+ Color boundary3 = Color::fromHSV (3 .0f / 6 .0f , 1 .0f , 1 .0f );
797+ EXPECT_NO_THROW (boundary3.getRed ());
798+
799+ Color boundary4 = Color::fromHSV (4 .0f / 6 .0f , 1 .0f , 1 .0f );
800+ EXPECT_NO_THROW (boundary4.getRed ());
801+
802+ Color boundary5 = Color::fromHSV (5 .0f / 6 .0f , 1 .0f , 1 .0f );
803+ EXPECT_NO_THROW (boundary5.getRed ());
804+ }
805+
806+ TEST (ColorTests, OverlaidWith_AlphaBlending)
807+ {
808+ // Test line 784-785: destAlpha <= 0
809+ Color transparent (0x00ff0000 ); // Fully transparent red
810+ Color opaqueSrc (0xff0000ff ); // Fully opaque blue
811+ Color result1 = transparent.overlaidWith (opaqueSrc);
812+ EXPECT_EQ (result1.getARGB (), opaqueSrc.getARGB ()); // Should return src
813+
814+ // Test line 789-790: resA <= 0
815+ Color fullyTransparent (0x00000000 );
816+ Color alsoTransparent (0x00ffffff );
817+ Color result2 = fullyTransparent.overlaidWith (alsoTransparent);
818+ EXPECT_EQ (result2.getARGB (), alsoTransparent.getARGB ()); // Should return src
819+
820+ // Test normal blending (lines 792-796)
821+ Color semiDest (0x80ff0000 ); // Semi-transparent red
822+ Color semiSrc (0x800000ff ); // Semi-transparent blue
823+ Color result3 = semiDest.overlaidWith (semiSrc);
824+ EXPECT_NE (result3.getARGB (), semiDest.getARGB ()); // Should be blended
825+ EXPECT_NE (result3.getARGB (), semiSrc.getARGB ()); // Should be blended
826+ EXPECT_GT (result3.getAlpha (), 0 ); // Should have some alpha
827+
828+ // Test with different alpha combinations
829+ Color dest1 (0xc0ff0000 ); // 75% opaque red
830+ Color src1 (0x400000ff ); // 25% opaque blue
831+ Color result4 = dest1.overlaidWith (src1);
832+ EXPECT_GT (result4.getRed (), result4.getBlue ()); // Red should dominate
833+
834+ // Test with opaque dest and semi-transparent src
835+ Color opaqueDest (0xffff0000 ); // Fully opaque red
836+ Color semiSrc2 (0x800000ff ); // Semi-transparent blue
837+ Color result5 = opaqueDest.overlaidWith (semiSrc2);
838+ EXPECT_GT (result5.getRed (), 0 ); // Should have red component
839+ EXPECT_GT (result5.getBlue (), 0 ); // Should have blue component
840+
841+ // Test with semi-transparent dest and opaque src
842+ Color semiDest2 (0x80ff0000 ); // Semi-transparent red
843+ Color opaqueSrc2 (0xff0000ff ); // Fully opaque blue
844+ Color result6 = semiDest2.overlaidWith (opaqueSrc2);
845+ EXPECT_EQ (result6.getBlue (), 255 ); // Blue should be dominant
846+
847+ // Edge case: both nearly opaque
848+ Color nearlyOpaqueDest (0xfeff0000 );
849+ Color nearlyOpaqueSrc (0xfe0000ff );
850+ Color result7 = nearlyOpaqueDest.overlaidWith (nearlyOpaqueSrc);
851+ EXPECT_NO_THROW (result7.getRed ()); // Should handle without issues
852+ }
0 commit comments