diff --git a/chat/pom.xml b/chat/pom.xml index fa1b04d..a61a9a2 100644 --- a/chat/pom.xml +++ b/chat/pom.xml @@ -25,6 +25,22 @@ javafx-fxml 18 + + org.openjfx + javafx-swing + 19 + + + org.junit.jupiter + junit-jupiter-params + 5.8.1 + test + + + net.synedra + validatorfx + 0.5.1 + org.junit.jupiter diff --git a/chat/src/main/java/module-info.java b/chat/src/main/java/module-info.java index 34c52c5..d4b00e4 100644 --- a/chat/src/main/java/module-info.java +++ b/chat/src/main/java/module-info.java @@ -2,8 +2,13 @@ module rtgre.chat { requires javafx.controls; requires javafx.fxml; requires java.logging; + requires java.desktop; + requires javafx.swing; + requires net.synedra.validatorfx; opens rtgre.chat to javafx.fxml; exports rtgre.chat; + exports rtgre.chat.graphisme; + opens rtgre.chat.graphisme to javafx.fxml; } \ No newline at end of file diff --git a/chat/src/main/java/rtgre/chat/ChatApplication.java b/chat/src/main/java/rtgre/chat/ChatApplication.java index b4e2085..039b5e8 100644 --- a/chat/src/main/java/rtgre/chat/ChatApplication.java +++ b/chat/src/main/java/rtgre/chat/ChatApplication.java @@ -15,8 +15,8 @@ import java.util.logging.Logger; public class ChatApplication extends Application { public static final Logger LOGGER = Logger.getLogger(ChatApplication.class.getCanonicalName()); + public class EssaiLogger { - /* . . . */ static { try { InputStream is = EssaiLogger.class.getClassLoader() @@ -26,8 +26,8 @@ public class ChatApplication extends Application { LOGGER.log(Level.INFO, "Cannot read configuration file", e); } } - /* . . . */ } + @Override public void start(Stage stage) throws IOException { FXMLLoader fxmlLoader = new FXMLLoader(ChatApplication.class.getResource("chat-view.fxml")); diff --git a/chat/src/main/java/rtgre/chat/ChatController.java b/chat/src/main/java/rtgre/chat/ChatController.java index aab603f..6b307d5 100644 --- a/chat/src/main/java/rtgre/chat/ChatController.java +++ b/chat/src/main/java/rtgre/chat/ChatController.java @@ -2,22 +2,47 @@ package rtgre.chat; import javafx.application.Platform; import javafx.beans.Observable; +import javafx.beans.binding.Bindings; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.embed.swing.SwingFXUtils; +import javafx.event.ActionEvent; import javafx.event.Event; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; +import javafx.stage.Stage; +import net.synedra.validatorfx.Check; +import net.synedra.validatorfx.TooltipWrapper; +import net.synedra.validatorfx.Validator; +import rtgre.chat.graphisme.ContactListViewCell; +import rtgre.modeles.Contact; +import rtgre.modeles.ContactMap; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; import java.net.URL; import java.util.Date; import java.util.Objects; import java.util.ResourceBundle; import java.util.logging.Logger; +import java.util.regex.Pattern; import static rtgre.chat.ChatApplication.LOGGER; + public class ChatController implements Initializable { + private static final Pattern LOGIN_PATTERN = Pattern.compile("^([a-z][a-z0-9]{2,7})$"); + private static final Pattern HOST_PATTERN = Pattern.compile("/^[a-z]*((\\:?)\\d{1,5})?$/gm"); public MenuItem hostAddMenuItem; public MenuItem avatarMenuItem; public MenuItem aboutMenuItem; @@ -28,11 +53,15 @@ public class ChatController implements Initializable { public SplitPane exchangeSplitPane; public ListView postListView; public ListView roomsListView; - public ListView contactListView; + public ListView contactsListView; public TextField messageTextField; public Button sendButton; public Label statusLabel; public Label dateTimeLabel; + public Contact contact; + private ContactMap contactMap = new ContactMap(); + private ObservableList contactObservableList = FXCollections.observableArrayList(); + Validator validatorLogin = new Validator(); @Override @@ -53,13 +82,74 @@ public class ChatController implements Initializable { statusLabel.setText("not connected to " + hostComboBox.getValue()); + connectionButton.disableProperty().bind(validatorLogin.containsErrorsProperty()); + connectionButton.selectedProperty().addListener(this::handleConnection); + loginTextField.disableProperty().bind(connectionButton.selectedProperty()); + hostComboBox.disableProperty().bind(connectionButton.selectedProperty()); + + avatarMenuItem.setOnAction(this::handleAvatarChange); + avatarImageView.setOnMouseClicked(this::handleAvatarChange); + + initContactListView(); + + validatorLogin.createCheck() + .dependsOn("login", loginTextField.textProperty()) + .withMethod(this::checkLogin) + .decorates(loginTextField) + .immediate(); + + + /* /!\ Set-up d'environnement de test /!\ */ + /* -------------------------------------- */ + loginTextField.setText("riri"); + connectionButton.setSelected(true); + } + + private void handleAvatarChange(Event event) { + /** + * Ouvre une fenêtre de dialogue permettant de choisir son avatar + */ + FileChooser fileChooser = new FileChooser(); + Stage stage = (Stage) avatarImageView.getScene().getWindow(); + fileChooser.setTitle("Select Avatar"); + fileChooser.getExtensionFilters().addAll( + new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg") + ); + File selectedFile = fileChooser.showOpenDialog(stage); + if (selectedFile != null) { + avatarImageView.setImage(new Image(selectedFile.toURI().toString())); + } + } + + + + private void handleConnection(Observable observable) { + /** + * + */ + if (connectionButton.isSelected()) { + java.awt.Image img = SwingFXUtils.fromFXImage(this.avatarImageView.getImage(), null); + this.contact = new Contact(loginTextField.getText(), img); + contactMap.put(this.contact.getLogin(), this.contact); + System.out.println("Nouveau contact : " + contact); + System.out.println(contactMap); + } + } + + private void checkLogin(Check.Context context) { + String login = context.get("login"); + if (!LOGIN_PATTERN.matcher(login).matches()) { + context.error("Format de login non respecté"); + } + if (login.equals("system")) { + context.error("Le login ne peut pas être system"); + } } private void statusNameUpdate(Event event) { statusLabel.setText("not connected to " + hostComboBox.getValue()); - } @@ -67,7 +157,6 @@ public class ChatController implements Initializable { while (true) { try { String datetime = "%1$ta %1$te %1$tb %1$tY - %1$tH:%1$tM".formatted(new Date()); - // System.out.println(datetime); Platform.runLater(() -> dateTimeLabel.setText(datetime)); Thread.sleep(60000); } catch (Exception e) { @@ -76,4 +165,20 @@ public class ChatController implements Initializable { } } + + private void initContactListView() { + try { + contactsListView.setCellFactory(contactListView -> new ContactListViewCell()); + contactsListView.setItems(contactObservableList); + File avatars = new File(getClass().getResource("avatars.png").toURI()); + Contact riri = new Contact("riri", false, avatars); + Contact fifi = new Contact("fifi", true, avatars); + contactObservableList.add(riri); + contactMap.add(riri); + contactObservableList.add(fifi); + contactMap.add(fifi); + } catch (Exception e) { + LOGGER.severe(e.getMessage()); + } + } } \ No newline at end of file diff --git a/chat/src/main/java/rtgre/chat/graphisme/ContactListViewCell.java b/chat/src/main/java/rtgre/chat/graphisme/ContactListViewCell.java new file mode 100644 index 0000000..0a1439c --- /dev/null +++ b/chat/src/main/java/rtgre/chat/graphisme/ContactListViewCell.java @@ -0,0 +1,88 @@ +package rtgre.chat.graphisme; + +import rtgre.chat.ChatController; +import javafx.embed.swing.SwingFXUtils; +import javafx.geometry.Pos; +import javafx.scene.control.ListCell; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.text.Font; +import javafx.scene.text.Text; +import rtgre.modeles.Contact; + +import java.awt.image.BufferedImage; + +import static rtgre.chat.ChatApplication.LOGGER; + +/** + * Classe modélisant la fabrique de cellule de la vue des contacts + * visibles/connectés {@link ChatController#contactsListView}. + * + * @see ListCell + */ +public class ContactListViewCell extends ListCell { + + /** + * Callback déclenchée à chaque modification d'un objet d'une liste d'observable. + * + * @param contact Le contact à mettre à jour + * @param empty La liste de cellule doit-elle être complètement remise à zéro ? + */ + @Override + protected void updateItem(Contact contact, boolean empty) { + super.updateItem(contact, empty); + if (empty) { + setGraphic(null); + } + else { + // Cas d'un contact + updateContact(contact); + } + } + + /** + * Mise à jour de la cellule d'un contact. + * + * @param contact Le contact à mettre à jour + */ + + private void updateContact(Contact contact) { + LOGGER.finest("Mise à jour de " + contact); + + String unreadCountNotif = (contact.getUnreadCount() == 0) ? "" : " (%d)".formatted(contact.getUnreadCount()); + LOGGER.finest("unread: %s %s".formatted(contact.getLogin(), unreadCountNotif)); + Text loginText = new Text(contact.getLogin() + unreadCountNotif); + loginText.setFont(Font.font(null, 12)); // FontWeight.BOLD, 14)); + loginText.setFill(contact.isConnected() ? Color.BLACK : Color.GRAY); + + Circle circle = new Circle(5, 5, 5); + circle.setFill(contact.isConnected() ? Color.CADETBLUE : Color.FIREBRICK); + // circle.setOpacity(contact.is_connected() ? 1 : 0.5); + + Image avatar; + ImageView view = new ImageView(); + + if (contact.getAvatar() != null) { + avatar = SwingFXUtils.toFXImage((BufferedImage) contact.getAvatar(), null); + view = new ImageView(avatar); + } + view.setOpacity(contact.isConnected() ? 1 : 0.5); + view.setFitWidth(15); + view.setFitHeight(15); + + HBox temp = new HBox(circle); + temp.setAlignment(Pos.CENTER_RIGHT); + HBox.setHgrow(temp, Priority.ALWAYS); + HBox hBox = new HBox(view, loginText, temp); + hBox.setSpacing(5.0); + hBox.setAlignment(Pos.CENTER_LEFT); + + setGraphic(hBox); + } + + +} \ No newline at end of file diff --git a/chat/src/main/java/rtgre/modeles/Contact.java b/chat/src/main/java/rtgre/modeles/Contact.java new file mode 100644 index 0000000..cf3e786 --- /dev/null +++ b/chat/src/main/java/rtgre/modeles/Contact.java @@ -0,0 +1,122 @@ +package rtgre.modeles; + + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +public class Contact { + protected String login; + protected java.awt.Image avatar; + protected boolean connected; + protected String currentRoom; + + public Contact(String login, java.awt.Image avatar) { + /** + * Crée un objet Contact + * @param: String login + * @param: java.awt.Image avatar + */ + this.login = login; + this.avatar = avatar; + this.connected = false; + this.currentRoom = null; + } + public Contact(String login, boolean connected, java.awt.Image avatar) { + /** + * Crée un objet Contact + * @param: String login + * @param: boolean connected + * @param: java.awt.Image avatar + */ + this.login = login; + this.avatar = avatar; + this.connected = connected; + this.currentRoom = null; + } + + public String getCurrentRoom() { + return currentRoom; + } + + public Contact(String login, boolean connected, File banques_avatars) { + /** + * Crée un objet Contact + * @param: String login + * @param: boolean connected + * @param: File banques_avatars + */ + this.login = login; + try { + this.avatar = avatarFromLogin(banques_avatars, login); + } catch (IOException e) { + System.out.println("Impossible de créer l'utilisateur " + login); + System.out.println(e.getMessage()); + System.out.println(banques_avatars); + } + this.connected = connected; + this.currentRoom = null; + } + + + public String getLogin() { + return this.login; + } + + public java.awt.Image getAvatar() { + return this.avatar; + } + + @Override + public String toString() { + return "@" + this.login; + } + + public boolean isConnected() { + return this.connected; + } + + public void setConnected(boolean connected) { + this.connected = connected; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Contact contact = (Contact) o; + return Objects.equals(login, contact.login); + } + + @Override + public int hashCode() { + return Objects.hashCode(login); + } + + public int getUnreadCount() { + return 0; + } + + public static BufferedImage avatarFromLogin(File fichier, String login) throws IOException { + /** + * Renvoie une sous-image en fonction d'une banque d'image et d'un login. + * @param: File fichier + * @param: String login + */ + BufferedImage img = ImageIO.read(fichier); + int width = img.getWidth() / 9; + int height = img.getHeight(); + int n = Integer.remainderUnsigned(login.hashCode(), 9); + return img.getSubimage(n*width, 0, width, height); + } + + public void setAvatarFromFile(File f) { + try { + this.avatar = avatarFromLogin(f, this.login); + } catch (IOException e) { + System.out.println("Erreur : " + e.getMessage()); + } + } +} diff --git a/chat/src/main/java/rtgre/modeles/ContactMap.java b/chat/src/main/java/rtgre/modeles/ContactMap.java new file mode 100644 index 0000000..f44bcc0 --- /dev/null +++ b/chat/src/main/java/rtgre/modeles/ContactMap.java @@ -0,0 +1,12 @@ +package rtgre.modeles; +import java.util.TreeMap; + +public class ContactMap extends TreeMap { + public void add(Contact contact) { + this.put(contact.login, contact); + } + + public Contact getContact(String login) { + return this.get(login); + } +} diff --git a/chat/src/main/resources/rtgre/chat/avatar1.png b/chat/src/main/resources/rtgre/chat/avatar1.png new file mode 100644 index 0000000..aa871c1 Binary files /dev/null and b/chat/src/main/resources/rtgre/chat/avatar1.png differ diff --git a/chat/src/main/resources/rtgre/chat/avatar2.png b/chat/src/main/resources/rtgre/chat/avatar2.png new file mode 100644 index 0000000..c8fe326 Binary files /dev/null and b/chat/src/main/resources/rtgre/chat/avatar2.png differ diff --git a/chat/src/main/resources/rtgre/chat/avatars.png b/chat/src/main/resources/rtgre/chat/avatars.png new file mode 100644 index 0000000..b5e2c99 Binary files /dev/null and b/chat/src/main/resources/rtgre/chat/avatars.png differ diff --git a/chat/src/main/resources/rtgre/chat/chat-view.fxml b/chat/src/main/resources/rtgre/chat/chat-view.fxml index a389c65..7d82d81 100644 --- a/chat/src/main/resources/rtgre/chat/chat-view.fxml +++ b/chat/src/main/resources/rtgre/chat/chat-view.fxml @@ -1,27 +1,13 @@ - - - - - - - - - - - - - - - - - - + + + + - + - + @@ -77,7 +63,7 @@ - + @@ -111,10 +97,10 @@ - + diff --git a/chat/src/test/java/rtgre/modeles/ContactBaseTest1.java b/chat/src/test/java/rtgre/modeles/ContactBaseTest1.java new file mode 100644 index 0000000..5198bb2 --- /dev/null +++ b/chat/src/test/java/rtgre/modeles/ContactBaseTest1.java @@ -0,0 +1,155 @@ +package rtgre.modeles; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.lang.reflect.*; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** Tests unitaires du modèle de base de Contact (étape 1) */ + +class ContactBaseTest1 { + + static Class classe = Contact.class; + static String module = "rtgre.modeles"; + + @DisplayName("01-Structure de la classe Contact") + @Nested + class StructureTest { + + static List constructeursSignatures; + static List methodesSignatures; + + @BeforeAll + static void init() { + Constructor[] constructeurs = classe.getConstructors(); + constructeursSignatures = Arrays.stream(constructeurs).map(Constructor::toString).collect(Collectors.toList()); + Method[] methodes = classe.getDeclaredMethods(); + methodesSignatures = Arrays.stream(methodes).map(Method::toString).collect(Collectors.toList()); + } + + /** + * Attributs + */ + static Stream attributsProvider() { + return Stream.of( + arguments("login", "java.lang.String", Modifier.PROTECTED), + arguments("avatar", "java.awt.Image", Modifier.PROTECTED), + arguments("connected", "boolean", Modifier.PROTECTED), + arguments("currentRoom", "java.lang.String", Modifier.PROTECTED) + ); + } + + @DisplayName("Déclaration des attributs : nom, type et visibilité") + @ParameterizedTest + @MethodSource("attributsProvider") + void testDeclarationAttributs(String nom, String type, int modifier) throws NoSuchFieldException { + Field field = classe.getDeclaredField(nom); + Assertions.assertEquals(type, field.getType().getName(), + "Type " + nom + " erroné : doit être " + type); + Assertions.assertEquals(modifier, field.getModifiers(), + "Visibilité " + nom + " erronée : doit être " + modifier); + + } + + /** + * Constructeurs + */ + static Stream constructeursProvider() { + return Stream.of( + arguments("public %s.Contact(java.lang.String,java.awt.Image)") + ); + } + + @DisplayName("Déclaration des constructeurs (base)") + @ParameterizedTest + @MethodSource("constructeursProvider") + void testConstructeurs1(String signature) { + Assertions.assertTrue(constructeursSignatures.contains(String.format(signature, module)), + String.format("Constructeur non déclaré : doit être %s\nalors que sont déclarés %s", + signature, constructeursSignatures)); + + } + + + /** + * Méthodes + */ + static Stream methodesProvider1() { + return Stream.of( + arguments("getLogin", "public java.lang.String %s.Contact.getLogin()"), + arguments("getAvatar", "public java.awt.Image %s.Contact.getAvatar()"), + arguments("isConnected", "public boolean %s.Contact.isConnected()"), + arguments("toString", "public boolean %s.Contact.isConnected()"), + arguments("setConnected", "public java.lang.String %s.Contact.toString()"), + arguments("equals", "public boolean %s.Contact.equals(java.lang.Object)") + ); + } + + @DisplayName("Déclaration des méthodes (base)") + @ParameterizedTest + @MethodSource("methodesProvider1") + void testDeclarationMethodes1(String nom, String signature) { + Assertions.assertTrue(methodesSignatures.contains(String.format(signature, module, module)), + String.format("Méthode non déclarée : doit être %s\nalors que sont déclarés %s", + signature, methodesSignatures)); + } + + + @DisplayName("02-Instanciation et getters") + @Nested + class InstanciationContactTest { + + @Test + @DisplayName("sans avatar : getters de login, connected") + void TestConstructeurParDefautSansAvatar() { + Contact riri = new Contact("riri", null); + Assertions.assertEquals("riri", riri.getLogin(), "Login erroné"); + Assertions.assertFalse(riri.isConnected(), "Etat par défaut erroné"); + } + + } + + @DisplayName("03-Modification") + @Nested + class ModificationContactTest { + + @Test + @DisplayName("Setter de connexion") + void TestEtatConnexion() { + Contact riri = new Contact("riri", null); + riri.setConnected(true); + Assertions.assertTrue(riri.isConnected(), "Changement d'état erroné"); + riri.setConnected(false); + Assertions.assertFalse(riri.isConnected(), "Changement d'état erroné"); + } + + } + + @Test + @DisplayName("04-Représentation textuelle") + void TestToString() { + Contact riri = new Contact("riri", null); + Assertions.assertEquals("@riri", riri.toString(), + "Représentation textuelle erronée"); + } + + @Test + @DisplayName("05-Egalité") + void TestEquals() { + Contact riri = new Contact("riri", null); + Contact riri2 = new Contact("riri", null); + Contact fifi = new Contact("fifi", null); + Assertions.assertEquals(riri, riri2, "Comparaison erronée"); + Assertions.assertNotEquals(riri, fifi, "Comparaison erronée"); + } + + } +} \ No newline at end of file diff --git a/chat/src/test/java/rtgre/modeles/ContactMapTest.java b/chat/src/test/java/rtgre/modeles/ContactMapTest.java new file mode 100644 index 0000000..2dce20d --- /dev/null +++ b/chat/src/test/java/rtgre/modeles/ContactMapTest.java @@ -0,0 +1,47 @@ +package rtgre.modeles; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.TreeMap; + +class ContactMapTest { + + static Class classe = ContactMap.class; + + @DisplayName("01-Structure") + @Nested + class StructureContactMapTest { + + @Test + @DisplayName("Heritage") + void testHeritage() { + ContactMap contactMap = new ContactMap(); + Assertions.assertInstanceOf(TreeMap.class, contactMap, "doit hériter de TreeMap"); + } + + + } + + @DisplayName("02-Ajout") + @Nested + class AddTest { + + @Test + @DisplayName("Ajout d'un contact") + void TestAdd() { + Contact riri = new Contact("riri", null); + ContactMap contactMap = new ContactMap(); + contactMap.add(riri); + + Assertions.assertTrue(contactMap.containsKey("riri"), + "Les clés sont les logins"); + Assertions.assertTrue(contactMap.containsValue(riri), + "Les valeurs sont les contacts"); + } + + } + +} \ No newline at end of file diff --git a/chat/src/test/java/rtgre/modeles/ContactWithAvatarTest2.java b/chat/src/test/java/rtgre/modeles/ContactWithAvatarTest2.java new file mode 100644 index 0000000..d9d9de6 --- /dev/null +++ b/chat/src/test/java/rtgre/modeles/ContactWithAvatarTest2.java @@ -0,0 +1,120 @@ +package rtgre.modeles; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** Tests unitaires du modèle de base de Contact (étape 1) */ + +class ContactWithAvatarTest2 { + + static Class classe = Contact.class; + static String module = "rtgre.modeles"; + + @DisplayName("01-Structure de la classe Contact") + @Nested + class StructureTest { + + static List constructeursSignatures; + static List methodesSignatures; + + @BeforeAll + static void init() { + Constructor[] constructeurs = classe.getConstructors(); + constructeursSignatures = Arrays.stream(constructeurs).map(Constructor::toString).collect(Collectors.toList()); + Method[] methodes = classe.getDeclaredMethods(); + methodesSignatures = Arrays.stream(methodes).map(Method::toString).collect(Collectors.toList()); + } + + + + static Stream constructeursProvider2() { + return Stream.of( + arguments("public %s.Contact(java.lang.String,boolean,java.io.File)") + ); + } + // @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier") + @DisplayName("Déclaration des constructeurs (avec avatars à partir d'un fichier)") + @ParameterizedTest + @MethodSource("constructeursProvider2") + void testConstructeurs2(String signature) { + Assertions.assertTrue(constructeursSignatures.contains(String.format(signature, module)), + String.format("Constructeur non déclaré : doit être %s\nalors que sont déclarés %s", + signature, constructeursSignatures)); + + } + + + static Stream methodesProvider2() { + return Stream.of( + arguments("avatarFromLogin", "public static java.awt.image.BufferedImage %s.Contact.avatarFromLogin(java.io.File,java.lang.String) throws java.io.IOException"), + arguments("setAvatarFromFile", "public void %s.Contact.setAvatarFromFile(java.io.File)") + ); + } + // @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier") + @DisplayName("Déclaration des méthodes (avec avatars à partir d'un fichier)") + @ParameterizedTest + @MethodSource("methodesProvider2") + void testDeclarationMethodes2(String nom, String signature) { + Assertions.assertTrue(methodesSignatures.contains(String.format(signature, module, module)), + String.format("Méthode non déclarée : doit être %s\nalors que sont déclarés %s", + signature, methodesSignatures)); + } + + } + + @DisplayName("02-Instanciation et getters") + @Nested + class InstanciationContactTest { + + // @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier") + @Test + @DisplayName("avec avatar : getters de avatar") + void TestConstructeurParDefautAvecAvatar() throws IOException { + String work_dir = System.getProperty("user.dir"); + Assertions.assertTrue(work_dir.endsWith("chat"), + "Le working dir doit être /chat/ et non : " + work_dir); + File f = new File("src/main/resources/rtgre/chat/anonymous.png"); + Assertions.assertTrue(f.canRead(), "Fichier manquant " + f.getAbsolutePath()); + Image avatar = ImageIO.read(f); + Contact fifi = new Contact("fifi", avatar); + Assertions.assertEquals(avatar, fifi.getAvatar(), "Avatar erroné"); + } + + } + + + // @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier") + @Nested + @DisplayName("06-Avatar à partir d'un fichier") + class AvatarFromFilesTest { + + @Test + @DisplayName("A partir d'un fichier") + void TestSetFromFile() throws IOException { + String work_dir = System.getProperty("user.dir"); + Assertions.assertTrue(work_dir.endsWith("chat"), + "Le working dir doit être /chat/ et non : " + work_dir); + File f = new File("src/main/resources/rtgre/chat/avatar1.png"); + Assertions.assertTrue(f.canRead(), "Fichier manquant " + f.getAbsolutePath()); + Contact fifi = new Contact("fifi", null); + fifi.setAvatarFromFile(f); + Assertions.assertNotNull(fifi.getAvatar(), "Avatar non chargé"); + } + } + +} \ No newline at end of file