LCOV - code coverage report
Current view: top level - testing - FakeEmailSender.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 42 46 91.3 %
Date: 2022-11-19 15:00:39 Functions: 17 17 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2015 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.testing;
      16             : 
      17             : import static java.util.stream.Collectors.toList;
      18             : 
      19             : import com.google.auto.value.AutoValue;
      20             : import com.google.common.collect.ImmutableList;
      21             : import com.google.common.collect.ImmutableMap;
      22             : import com.google.common.flogger.FluentLogger;
      23             : import com.google.gerrit.common.Nullable;
      24             : import com.google.gerrit.entities.Address;
      25             : import com.google.gerrit.entities.EmailHeader;
      26             : import com.google.gerrit.exceptions.EmailException;
      27             : import com.google.gerrit.mail.MailHeader;
      28             : import com.google.gerrit.server.git.WorkQueue;
      29             : import com.google.gerrit.server.mail.send.EmailSender;
      30             : import com.google.inject.AbstractModule;
      31             : import com.google.inject.Inject;
      32             : import com.google.inject.Singleton;
      33             : import java.util.ArrayList;
      34             : import java.util.Collection;
      35             : import java.util.Collections;
      36             : import java.util.List;
      37             : import java.util.Map;
      38             : import java.util.concurrent.ExecutionException;
      39             : 
      40             : /**
      41             :  * Email sender implementation that records messages in memory.
      42             :  *
      43             :  * <p>This class is mostly threadsafe. The only exception is that not all {@link EmailHeader}
      44             :  * subclasses are immutable. In particular, if a caller holds a reference to an {@code AddressList}
      45             :  * and mutates it after sending, the message returned by {@link #getMessages()} may or may not
      46             :  * reflect mutations.
      47             :  */
      48             : @Singleton
      49             : public class FakeEmailSender implements EmailSender {
      50         152 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      51             : 
      52         152 :   public static class FakeEmailSenderModule extends AbstractModule {
      53             :     @Override
      54             :     public void configure() {
      55         152 :       bind(EmailSender.class).to(FakeEmailSender.class);
      56         152 :     }
      57             :   }
      58             : 
      59             :   @AutoValue
      60          55 :   public abstract static class Message {
      61             :     private static Message create(
      62             :         Address from,
      63             :         Collection<Address> rcpt,
      64             :         Map<String, EmailHeader> headers,
      65             :         String body,
      66             :         String htmlBody) {
      67          55 :       return new AutoValue_FakeEmailSender_Message(
      68          55 :           from, ImmutableList.copyOf(rcpt), ImmutableMap.copyOf(headers), body, htmlBody);
      69             :     }
      70             : 
      71             :     public abstract Address from();
      72             : 
      73             :     public abstract ImmutableList<Address> rcpt();
      74             : 
      75             :     public abstract ImmutableMap<String, EmailHeader> headers();
      76             : 
      77             :     public abstract String body();
      78             : 
      79             :     @Nullable
      80             :     public abstract String htmlBody();
      81             :   }
      82             : 
      83             :   private final WorkQueue workQueue;
      84             :   private final List<Message> messages;
      85             :   private int messagesRead;
      86             : 
      87             :   @Inject
      88         152 :   FakeEmailSender(WorkQueue workQueue) {
      89         152 :     this.workQueue = workQueue;
      90         152 :     messages = Collections.synchronizedList(new ArrayList<>());
      91         152 :     messagesRead = 0;
      92         152 :   }
      93             : 
      94             :   @Override
      95             :   public boolean isEnabled() {
      96         106 :     return true;
      97             :   }
      98             : 
      99             :   @Override
     100             :   public boolean canEmail(String address) {
     101         104 :     return true;
     102             :   }
     103             : 
     104             :   @Override
     105             :   public void send(
     106             :       Address from, Collection<Address> rcpt, Map<String, EmailHeader> headers, String body)
     107             :       throws EmailException {
     108           2 :     send(from, rcpt, headers, body, null);
     109           2 :   }
     110             : 
     111             :   @Override
     112             :   public void send(
     113             :       Address from,
     114             :       Collection<Address> rcpt,
     115             :       Map<String, EmailHeader> headers,
     116             :       String body,
     117             :       String htmlBody)
     118             :       throws EmailException {
     119          55 :     messages.add(Message.create(from, rcpt, headers, body, htmlBody));
     120          55 :   }
     121             : 
     122             :   public void clear() {
     123         132 :     waitForEmails();
     124         132 :     synchronized (messages) {
     125         132 :       messages.clear();
     126         132 :       messagesRead = 0;
     127         132 :     }
     128         132 :   }
     129             : 
     130             :   public synchronized @Nullable Message peekMessage() {
     131           2 :     if (messagesRead >= messages.size()) {
     132           1 :       return null;
     133             :     }
     134           2 :     return messages.get(messagesRead);
     135             :   }
     136             : 
     137             :   public synchronized @Nullable Message nextMessage() {
     138           2 :     Message msg = peekMessage();
     139           2 :     messagesRead++;
     140           2 :     return msg;
     141             :   }
     142             : 
     143             :   public ImmutableList<Message> getMessages() {
     144          25 :     waitForEmails();
     145          25 :     synchronized (messages) {
     146          25 :       return ImmutableList.copyOf(messages);
     147             :     }
     148             :   }
     149             : 
     150             :   public List<Message> getMessages(String changeId, String type) {
     151           2 :     final String idFooter = "\n" + MailHeader.CHANGE_ID.withDelimiter() + changeId + "\n";
     152           2 :     final String typeFooter = "\n" + MailHeader.MESSAGE_TYPE.withDelimiter() + type + "\n";
     153           2 :     return getMessages().stream()
     154           2 :         .filter(in -> in.body().contains(idFooter) && in.body().contains(typeFooter))
     155           2 :         .collect(toList());
     156             :   }
     157             : 
     158             :   private void waitForEmails() {
     159             :     // TODO(dborowitz): This is brittle; consider forcing emails to use
     160             :     // a single thread in tests (tricky because most callers just use the
     161             :     // default executor).
     162         132 :     for (WorkQueue.Task<?> task : workQueue.getTasks()) {
     163         132 :       if (task.toString().contains("send-email")) {
     164             :         try {
     165           0 :           task.get();
     166           0 :         } catch (ExecutionException | InterruptedException e) {
     167           0 :           logger.atWarning().withCause(e).log("error finishing email task");
     168           0 :         }
     169             :       }
     170         132 :     }
     171         132 :   }
     172             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750